diff --git a/changelog/unreleased/sysinfo-prometheus.md b/changelog/unreleased/sysinfo-prometheus.md new file mode 100644 index 00000000000..798eea0cb05 --- /dev/null +++ b/changelog/unreleased/sysinfo-prometheus.md @@ -0,0 +1,5 @@ +Enhancement: System information included in Prometheus metrics + +System information is now included in the main Prometheus metrics exposed at `/metrics`. + +https://github.com/cs3org/reva/pull/1071 diff --git a/internal/http/services/prometheus/prometheus.go b/internal/http/services/prometheus/prometheus.go index d1392dc4997..99f2bd5f3bb 100644 --- a/internal/http/services/prometheus/prometheus.go +++ b/internal/http/services/prometheus/prometheus.go @@ -22,12 +22,15 @@ import ( "net/http" "contrib.go.opencensus.io/exporter/prometheus" - "github.com/cs3org/reva/pkg/rhttp/global" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" + promclient "github.com/prometheus/client_golang/prometheus" "github.com/rs/zerolog" "go.opencensus.io/stats/view" + "github.com/cs3org/reva/pkg/rhttp/global" + "github.com/cs3org/reva/pkg/sysinfo" + // Initializes goroutines which periodically update stats _ "github.com/cs3org/reva/pkg/metrics/reader/dummy" ) @@ -45,15 +48,23 @@ func New(m map[string]interface{}, log *zerolog.Logger) (global.Service, error) conf.init() + registry := promclient.NewRegistry() // A Prometheus registry shared by OpenCensus and SysInfo pe, err := prometheus.NewExporter(prometheus.Options{ Namespace: "revad", + Registry: registry, }) if err != nil { return nil, errors.Wrap(err, "prometheus: error creating exporter") } + // Initialize the SysInfo Prometheus exporter + sysinfo, err := sysinfo.NewPrometheusExporter(registry) + if err != nil { + return nil, errors.Wrap(err, "prometheus: unable to create system info exporter") + } + view.RegisterExporter(pe) - return &svc{prefix: conf.Prefix, h: pe}, nil + return &svc{prefix: conf.Prefix, h: pe, sysinfo: sysinfo}, nil } type config struct { @@ -69,6 +80,8 @@ func (c *config) init() { type svc struct { prefix string h http.Handler + + sysinfo *sysinfo.PrometheusExporter } func (s *svc) Prefix() string { diff --git a/internal/http/services/sysinfo/sysinfo.go b/internal/http/services/sysinfo/sysinfo.go index b2c20154bfc..24fcd71edb4 100644 --- a/internal/http/services/sysinfo/sysinfo.go +++ b/internal/http/services/sysinfo/sysinfo.go @@ -20,7 +20,6 @@ package sysinfo import ( "net/http" - "strings" "github.com/mitchellh/mapstructure" "github.com/pkg/errors" @@ -41,8 +40,6 @@ type config struct { type svc struct { conf *config - - promHandler *prometheusSysInfoHandler } const ( @@ -67,24 +64,9 @@ func (s *svc) Unprotected() []string { // Handler serves all HTTP requests. func (s *svc) Handler() http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt := strings.ToLower(r.URL.Query().Get("format")) - if len(fmt) == 0 { - // No format was specified, so let Prometheus handle the request - s.promHandler.httpHandler.ServeHTTP(w, r) - } else { - // Otherwise, provide the system information in the requested format - data := "" - switch fmt { - case "json": - data = s.getJSONData() - default: - data = "Unsupported format" - } - - log := appctx.GetLogger(r.Context()) - if _, err := w.Write([]byte(data)); err != nil { - log.Err(err).Msg("error writing SysInfo response") - } + log := appctx.GetLogger(r.Context()) + if _, err := w.Write([]byte(s.getJSONData())); err != nil { + log.Err(err).Msg("error writing SysInfo response") } }) } @@ -120,16 +102,9 @@ func New(m map[string]interface{}, log *zerolog.Logger) (global.Service, error) return nil, err } - // Create the Prometheus system information handler - promHandler := &prometheusSysInfoHandler{} - if err := promHandler.init(); err != nil { - return nil, err - } - // Create the service s := &svc{ - conf: conf, - promHandler: promHandler, + conf: conf, } return s, nil } diff --git a/internal/http/services/sysinfo/prometheus.go b/pkg/sysinfo/prometheus.go similarity index 77% rename from internal/http/services/sysinfo/prometheus.go rename to pkg/sysinfo/prometheus.go index 98ddeb08217..f327cb40103 100644 --- a/internal/http/services/sysinfo/prometheus.go +++ b/pkg/sysinfo/prometheus.go @@ -20,46 +20,41 @@ package sysinfo import ( "fmt" - "net/http" "reflect" "strings" "github.com/prometheus/client_golang/prometheus" - "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/cs3org/reva/pkg/sysinfo" "github.com/cs3org/reva/pkg/utils" ) -type prometheusSysInfoHandler struct { +// PrometheusExporter exports system information via Prometheus. +type PrometheusExporter struct { registry *prometheus.Registry sysInfoMetric prometheus.GaugeFunc - - httpHandler http.Handler } -func (psysinfo *prometheusSysInfoHandler) init() error { +func (psysinfo *PrometheusExporter) init(registry *prometheus.Registry) error { // Create all necessary Prometheus objects - psysinfo.registry = prometheus.NewRegistry() + psysinfo.registry = registry psysinfo.sysInfoMetric = prometheus.NewGaugeFunc( prometheus.GaugeOpts{ Namespace: "revad", Name: "sys_info", Help: "A metric with a constant '1' value labeled by various system information elements", - ConstLabels: psysinfo.getLabels("", sysinfo.SysInfo), + ConstLabels: psysinfo.getLabels("", SysInfo), }, func() float64 { return 1 }, ) - psysinfo.httpHandler = promhttp.HandlerFor(psysinfo.registry, promhttp.HandlerOpts{}) if err := psysinfo.registry.Register(psysinfo.sysInfoMetric); err != nil { - return fmt.Errorf("unable to register the system information metric: %v", err) + return fmt.Errorf("unable to register the system information metrics: %v", err) } return nil } -func (psysinfo *prometheusSysInfoHandler) getLabels(root string, i interface{}) prometheus.Labels { +func (psysinfo *PrometheusExporter) getLabels(root string, i interface{}) prometheus.Labels { labels := prometheus.Labels{} // Iterate over each field of the given interface, recursively collecting the values as labels @@ -92,3 +87,17 @@ func (psysinfo *prometheusSysInfoHandler) getLabels(root string, i interface{}) return labels } + +// NewPrometheusExporter creates a new Prometheus system information exporter. +func NewPrometheusExporter(registry *prometheus.Registry) (*PrometheusExporter, error) { + if registry == nil { + return nil, fmt.Errorf("no registry provided") + } + + exporter := &PrometheusExporter{} + if err := exporter.init(registry); err != nil { + return nil, err + } + + return exporter, nil +}