mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 14:50:55 +03:00
feat: add new telemetry server (#254)
Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/drakkan/sftpgo/kms"
|
"github.com/drakkan/sftpgo/kms"
|
||||||
"github.com/drakkan/sftpgo/logger"
|
"github.com/drakkan/sftpgo/logger"
|
||||||
"github.com/drakkan/sftpgo/sftpd"
|
"github.com/drakkan/sftpgo/sftpd"
|
||||||
|
"github.com/drakkan/sftpgo/telemetry"
|
||||||
"github.com/drakkan/sftpgo/utils"
|
"github.com/drakkan/sftpgo/utils"
|
||||||
"github.com/drakkan/sftpgo/version"
|
"github.com/drakkan/sftpgo/version"
|
||||||
"github.com/drakkan/sftpgo/webdavd"
|
"github.com/drakkan/sftpgo/webdavd"
|
||||||
@@ -38,14 +39,15 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type globalConfig struct {
|
type globalConfig struct {
|
||||||
Common common.Configuration `json:"common" mapstructure:"common"`
|
Common common.Configuration `json:"common" mapstructure:"common"`
|
||||||
SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"`
|
SFTPD sftpd.Configuration `json:"sftpd" mapstructure:"sftpd"`
|
||||||
FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"`
|
FTPD ftpd.Configuration `json:"ftpd" mapstructure:"ftpd"`
|
||||||
WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
|
WebDAVD webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
|
||||||
ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"`
|
ProviderConf dataprovider.Config `json:"data_provider" mapstructure:"data_provider"`
|
||||||
HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"`
|
HTTPDConfig httpd.Conf `json:"httpd" mapstructure:"httpd"`
|
||||||
HTTPConfig httpclient.Config `json:"http" mapstructure:"http"`
|
HTTPConfig httpclient.Config `json:"http" mapstructure:"http"`
|
||||||
KMSConfig kms.Configuration `json:"kms" mapstructure:"kms"`
|
KMSConfig kms.Configuration `json:"kms" mapstructure:"kms"`
|
||||||
|
TelemetryConfig telemetry.Conf `json:"telemetry" mapstructure:"telemetry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -182,6 +184,10 @@ func Init() {
|
|||||||
MasterKeyPath: "",
|
MasterKeyPath: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
TelemetryConfig: telemetry.Conf{
|
||||||
|
BindPort: 10000,
|
||||||
|
BindAddress: "127.0.0.1",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
viper.SetEnvPrefix(configEnvPrefix)
|
viper.SetEnvPrefix(configEnvPrefix)
|
||||||
@@ -268,6 +274,16 @@ func SetKMSConfig(config kms.Configuration) {
|
|||||||
globalConf.KMSConfig = config
|
globalConf.KMSConfig = config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTelemetryConfig returns the telemetry configuration
|
||||||
|
func GetTelemetryConfig() telemetry.Conf {
|
||||||
|
return globalConf.TelemetryConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTelemetryConfig sets the telemetry configuration
|
||||||
|
func SetTelemetryConfig(config telemetry.Conf) {
|
||||||
|
globalConf.TelemetryConfig = config
|
||||||
|
}
|
||||||
|
|
||||||
// HasServicesToStart returns true if the config defines at least a service to start.
|
// HasServicesToStart returns true if the config defines at least a service to start.
|
||||||
// Supported services are SFTP, FTP and WebDAV
|
// Supported services are SFTP, FTP and WebDAV
|
||||||
func HasServicesToStart() bool {
|
func HasServicesToStart() bool {
|
||||||
@@ -496,4 +512,6 @@ func setViperDefaults() {
|
|||||||
viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
|
viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
|
||||||
viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL)
|
viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL)
|
||||||
viper.SetDefault("kms.secrets.master_key_path", globalConf.KMSConfig.Secrets.MasterKeyPath)
|
viper.SetDefault("kms.secrets.master_key_path", globalConf.KMSConfig.Secrets.MasterKeyPath)
|
||||||
|
viper.SetDefault("telemetry.bind_port", globalConf.TelemetryConfig.BindPort)
|
||||||
|
viper.SetDefault("telemetry.bind_address", globalConf.TelemetryConfig.BindAddress)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -315,6 +315,12 @@ func TestSetGetConfig(t *testing.T) {
|
|||||||
config.SetKMSConfig(kmsConf)
|
config.SetKMSConfig(kmsConf)
|
||||||
assert.Equal(t, kmsConf.Secrets.MasterKeyPath, config.GetKMSConfig().Secrets.MasterKeyPath)
|
assert.Equal(t, kmsConf.Secrets.MasterKeyPath, config.GetKMSConfig().Secrets.MasterKeyPath)
|
||||||
assert.Equal(t, kmsConf.Secrets.URL, config.GetKMSConfig().Secrets.URL)
|
assert.Equal(t, kmsConf.Secrets.URL, config.GetKMSConfig().Secrets.URL)
|
||||||
|
telemetryConf := config.GetTelemetryConfig()
|
||||||
|
telemetryConf.BindPort = 10001
|
||||||
|
telemetryConf.BindAddress = "0.0.0.0"
|
||||||
|
config.SetTelemetryConfig(telemetryConf)
|
||||||
|
assert.Equal(t, telemetryConf.BindPort, config.GetTelemetryConfig().BindPort)
|
||||||
|
assert.Equal(t, telemetryConf.BindAddress, config.GetTelemetryConfig().BindAddress)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceToStart(t *testing.T) {
|
func TestServiceToStart(t *testing.T) {
|
||||||
|
|||||||
@@ -128,6 +128,7 @@ func (s *Service) startServices() {
|
|||||||
ftpdConf := config.GetFTPDConfig()
|
ftpdConf := config.GetFTPDConfig()
|
||||||
httpdConf := config.GetHTTPDConfig()
|
httpdConf := config.GetHTTPDConfig()
|
||||||
webDavDConf := config.GetWebDAVDConfig()
|
webDavDConf := config.GetWebDAVDConfig()
|
||||||
|
telemetryConf := config.GetTelemetryConfig()
|
||||||
|
|
||||||
if sftpdConf.BindPort > 0 {
|
if sftpdConf.BindPort > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
@@ -182,6 +183,21 @@ func (s *Service) startServices() {
|
|||||||
} else {
|
} else {
|
||||||
logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
|
logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
|
||||||
}
|
}
|
||||||
|
if telemetryConf.BindPort > 0 {
|
||||||
|
go func() {
|
||||||
|
if err := telemetryConf.Initialize(s.Profiler); err != nil {
|
||||||
|
logger.Error(logSender, "", "could not start telemetry server: %v", err)
|
||||||
|
logger.ErrorToConsole("could not start telemetry server: %v", err)
|
||||||
|
s.Error = err
|
||||||
|
}
|
||||||
|
s.Shutdown <- true
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
logger.Debug(logSender, "", "telemetry server not started, disabled in config file")
|
||||||
|
if s.PortableMode != 1 {
|
||||||
|
logger.DebugToConsole("telemetry server not started, disabled in config file")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait blocks until the service exits
|
// Wait blocks until the service exits
|
||||||
|
|||||||
32
telemetry/router.go
Normal file
32
telemetry/router.go
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
"github.com/go-chi/chi/middleware"
|
||||||
|
"github.com/go-chi/render"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/logger"
|
||||||
|
"github.com/drakkan/sftpgo/metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initializeRouter(enableProfiler bool) {
|
||||||
|
router = chi.NewRouter()
|
||||||
|
|
||||||
|
router.Use(middleware.Recoverer)
|
||||||
|
|
||||||
|
router.Group(func(r chi.Router) {
|
||||||
|
r.Get("/healthz", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
render.PlainText(w, r, "ok")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
metrics.AddMetricsEndpoint(metricsPath, router)
|
||||||
|
|
||||||
|
if enableProfiler {
|
||||||
|
logger.InfoToConsole("enabling the built-in profiler")
|
||||||
|
logger.Info(logSender, "", "enabling the built-in profiler")
|
||||||
|
router.Mount(pprofBasePath, middleware.Profiler())
|
||||||
|
}
|
||||||
|
}
|
||||||
48
telemetry/telemetry.go
Normal file
48
telemetry/telemetry.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
// Package telemetry provides telemetry information for SFTPGo, such as:
|
||||||
|
// - health information (for health checks)
|
||||||
|
// - metrics
|
||||||
|
// - profiling information
|
||||||
|
package telemetry
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-chi/chi"
|
||||||
|
|
||||||
|
"github.com/drakkan/sftpgo/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
logSender = "telemetry"
|
||||||
|
metricsPath = "/metrics"
|
||||||
|
pprofBasePath = "/debug"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
router *chi.Mux
|
||||||
|
)
|
||||||
|
|
||||||
|
// Conf telemetry server configuration.
|
||||||
|
type Conf struct {
|
||||||
|
// The port used for serving HTTP requests. 0 disable the HTTP server. Default: 8080
|
||||||
|
BindPort int `json:"bind_port" mapstructure:"bind_port"`
|
||||||
|
// The address to listen on. A blank value means listen on all available network interfaces. Default: "127.0.0.1"
|
||||||
|
BindAddress string `json:"bind_address" mapstructure:"bind_address"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize configures and starts the telemetry server.
|
||||||
|
func (c Conf) Initialize(enableProfiler bool) error {
|
||||||
|
logger.Debug(logSender, "", "initializing telemetry server with config %+v", c)
|
||||||
|
initializeRouter(enableProfiler)
|
||||||
|
httpServer := &http.Server{
|
||||||
|
Addr: fmt.Sprintf("%s:%d", c.BindAddress, c.BindPort),
|
||||||
|
Handler: router,
|
||||||
|
ReadTimeout: 60 * time.Second,
|
||||||
|
WriteTimeout: 60 * time.Second,
|
||||||
|
IdleTimeout: 120 * time.Second,
|
||||||
|
MaxHeaderBytes: 1 << 16, // 64KB
|
||||||
|
}
|
||||||
|
return httpServer.ListenAndServe()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user