瀏覽代碼

feat: add new telemetry server (#254)

Signed-off-by: Mark Sagi-Kazar <mark.sagikazar@gmail.com>
Márk Sági-Kazár 4 年之前
父節點
當前提交
6d895843dc
共有 5 個文件被更改,包括 128 次插入8 次删除
  1. 26 8
      config/config.go
  2. 6 0
      config/config_test.go
  3. 16 0
      service/service.go
  4. 32 0
      telemetry/router.go
  5. 48 0
      telemetry/telemetry.go

+ 26 - 8
config/config.go

@@ -16,6 +16,7 @@ import (
 	"github.com/drakkan/sftpgo/kms"
 	"github.com/drakkan/sftpgo/logger"
 	"github.com/drakkan/sftpgo/sftpd"
+	"github.com/drakkan/sftpgo/telemetry"
 	"github.com/drakkan/sftpgo/utils"
 	"github.com/drakkan/sftpgo/version"
 	"github.com/drakkan/sftpgo/webdavd"
@@ -38,14 +39,15 @@ var (
 )
 
 type globalConfig struct {
-	Common       common.Configuration  `json:"common" mapstructure:"common"`
-	SFTPD        sftpd.Configuration   `json:"sftpd" mapstructure:"sftpd"`
-	FTPD         ftpd.Configuration    `json:"ftpd" mapstructure:"ftpd"`
-	WebDAVD      webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
-	ProviderConf dataprovider.Config   `json:"data_provider" mapstructure:"data_provider"`
-	HTTPDConfig  httpd.Conf            `json:"httpd" mapstructure:"httpd"`
-	HTTPConfig   httpclient.Config     `json:"http" mapstructure:"http"`
-	KMSConfig    kms.Configuration     `json:"kms" mapstructure:"kms"`
+	Common          common.Configuration  `json:"common" mapstructure:"common"`
+	SFTPD           sftpd.Configuration   `json:"sftpd" mapstructure:"sftpd"`
+	FTPD            ftpd.Configuration    `json:"ftpd" mapstructure:"ftpd"`
+	WebDAVD         webdavd.Configuration `json:"webdavd" mapstructure:"webdavd"`
+	ProviderConf    dataprovider.Config   `json:"data_provider" mapstructure:"data_provider"`
+	HTTPDConfig     httpd.Conf            `json:"httpd" mapstructure:"httpd"`
+	HTTPConfig      httpclient.Config     `json:"http" mapstructure:"http"`
+	KMSConfig       kms.Configuration     `json:"kms" mapstructure:"kms"`
+	TelemetryConfig telemetry.Conf        `json:"telemetry" mapstructure:"telemetry"`
 }
 
 func init() {
@@ -182,6 +184,10 @@ func Init() {
 				MasterKeyPath: "",
 			},
 		},
+		TelemetryConfig: telemetry.Conf{
+			BindPort:    10000,
+			BindAddress: "127.0.0.1",
+		},
 	}
 
 	viper.SetEnvPrefix(configEnvPrefix)
@@ -268,6 +274,16 @@ func SetKMSConfig(config kms.Configuration) {
 	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.
 // Supported services are SFTP, FTP and WebDAV
 func HasServicesToStart() bool {
@@ -496,4 +512,6 @@ func setViperDefaults() {
 	viper.SetDefault("http.skip_tls_verify", globalConf.HTTPConfig.SkipTLSVerify)
 	viper.SetDefault("kms.secrets.url", globalConf.KMSConfig.Secrets.URL)
 	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)
 }

+ 6 - 0
config/config_test.go

@@ -315,6 +315,12 @@ func TestSetGetConfig(t *testing.T) {
 	config.SetKMSConfig(kmsConf)
 	assert.Equal(t, kmsConf.Secrets.MasterKeyPath, config.GetKMSConfig().Secrets.MasterKeyPath)
 	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) {

+ 16 - 0
service/service.go

@@ -128,6 +128,7 @@ func (s *Service) startServices() {
 	ftpdConf := config.GetFTPDConfig()
 	httpdConf := config.GetHTTPDConfig()
 	webDavDConf := config.GetWebDAVDConfig()
+	telemetryConf := config.GetTelemetryConfig()
 
 	if sftpdConf.BindPort > 0 {
 		go func() {
@@ -182,6 +183,21 @@ func (s *Service) startServices() {
 	} else {
 		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

+ 32 - 0
telemetry/router.go

@@ -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 - 0
telemetry/telemetry.go

@@ -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()
+}