From 0808d013ab7455bd56d7a375f50cf13eb3568e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= <61736220+Daniel-WWU-IT@users.noreply.github.com> Date: Tue, 25 Aug 2020 16:10:04 +0200 Subject: [PATCH] The system info metrics now use OpenCensus (#1114) --- changelog/unreleased/sysinfo-cleanup.md | 5 ++ cmd/revad/main.go | 5 +- go.mod | 1 - go.sum | 1 + .../http/services/prometheus/prometheus.go | 15 +--- pkg/sysinfo/{prometheus.go => metrics.go} | 81 ++++++++++--------- pkg/sysinfo/reva.go | 2 +- pkg/sysinfo/sysinfo.go | 29 ++++--- 8 files changed, 73 insertions(+), 66 deletions(-) create mode 100644 changelog/unreleased/sysinfo-cleanup.md rename pkg/sysinfo/{prometheus.go => metrics.go} (52%) diff --git a/changelog/unreleased/sysinfo-cleanup.md b/changelog/unreleased/sysinfo-cleanup.md new file mode 100644 index 0000000000..b247ff5d97 --- /dev/null +++ b/changelog/unreleased/sysinfo-cleanup.md @@ -0,0 +1,5 @@ +Enhancement: System information metrics cleanup + +The system information metrics are now based on OpenCensus instead of the Prometheus client library. Furthermore, its initialization was moved out of the Prometheus HTTP service to keep things clean. + +https://github.com/cs3org/reva/pull/1114 diff --git a/cmd/revad/main.go b/cmd/revad/main.go index cfb5cb3a37..ac6eddae7e 100644 --- a/cmd/revad/main.go +++ b/cmd/revad/main.go @@ -53,7 +53,10 @@ func main() { flag.Parse() // initialize the global system information - sysinfo.InitSystemInfo(&sysinfo.RevaVersion{Version: version, BuildDate: buildDate, GitCommit: gitCommit, GoVersion: goVersion}) + if err := sysinfo.InitSystemInfo(&sysinfo.RevaVersion{Version: version, BuildDate: buildDate, GitCommit: gitCommit, GoVersion: goVersion}); err != nil { + fmt.Fprintf(os.Stderr, "error initializing system info: %s\n", err.Error()) + // This is not really a fatal error, so don't panic + } handleVersionFlag() handleSignalFlag() diff --git a/go.mod b/go.mod index 5e91d48b9d..5a00947977 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,6 @@ require ( github.com/pkg/term v0.0.0-20200520122047-c3ffed290a03 // indirect github.com/pkg/xattr v0.4.1 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect - github.com/prometheus/client_golang v1.7.1 github.com/rs/cors v1.7.0 github.com/rs/zerolog v1.19.0 github.com/stretchr/testify v1.6.1 diff --git a/go.sum b/go.sum index e492c2ff34..b3c2b80dda 100644 --- a/go.sum +++ b/go.sum @@ -906,6 +906,7 @@ golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20u golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d h1:nc5K6ox/4lTFbMVSL9WRR81ixkcwXThoiF6yf+R9scA= golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f h1:gWF768j/LaZugp8dyS4UwsslYCYz9XgFxvlgsn0n9H8= golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1 h1:ogLJMz+qpzav7lGMh10LMvAkM/fAoGlaiiHYiFYdm80= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/internal/http/services/prometheus/prometheus.go b/internal/http/services/prometheus/prometheus.go index 99f2bd5f3b..7330fcb111 100644 --- a/internal/http/services/prometheus/prometheus.go +++ b/internal/http/services/prometheus/prometheus.go @@ -24,13 +24,10 @@ import ( "contrib.go.opencensus.io/exporter/prometheus" "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" ) @@ -48,23 +45,15 @@ 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, sysinfo: sysinfo}, nil + return &svc{prefix: conf.Prefix, h: pe}, nil } type config struct { @@ -80,8 +69,6 @@ func (c *config) init() { type svc struct { prefix string h http.Handler - - sysinfo *sysinfo.PrometheusExporter } func (s *svc) Prefix() string { diff --git a/pkg/sysinfo/prometheus.go b/pkg/sysinfo/metrics.go similarity index 52% rename from pkg/sysinfo/prometheus.go rename to pkg/sysinfo/metrics.go index f327cb4010..6b7b443a9e 100644 --- a/pkg/sysinfo/prometheus.go +++ b/pkg/sysinfo/metrics.go @@ -19,43 +19,59 @@ package sysinfo import ( + "context" "fmt" "reflect" "strings" - "github.com/prometheus/client_golang/prometheus" + "github.com/pkg/errors" + "go.opencensus.io/stats" + "go.opencensus.io/stats/view" + "go.opencensus.io/tag" "github.com/cs3org/reva/pkg/utils" ) -// PrometheusExporter exports system information via Prometheus. -type PrometheusExporter struct { - registry *prometheus.Registry - sysInfoMetric prometheus.GaugeFunc -} +type sysInfoMetricsLabels = map[tag.Key]string + +func registerSystemInfoMetrics() error { + labels := getSystemInfoMetricsLabels("", SysInfo) + + // Collect all labels and their values; the values are stored as mutators + tagKeys := make([]tag.Key, 0, len(labels)) + mutators := make([]tag.Mutator, 0, len(labels)) + for key, value := range labels { + tagKeys = append(tagKeys, key) + mutators = append(mutators, tag.Insert(key, value)) + } + + // Create the OpenCensus statistics and a corresponding view + sysInfoStats := stats.Int64("sys_info", "A metric with a constant '1' value labeled by various system information elements", stats.UnitDimensionless) + sysInfoView := &view.View{ + Name: sysInfoStats.Name(), + Description: sysInfoStats.Description(), + Measure: sysInfoStats, + TagKeys: tagKeys, + Aggregation: view.LastValue(), + } + + if err := view.Register(sysInfoView); err != nil { + return errors.Wrap(err, "unable to register the system info metrics view") + } -func (psysinfo *PrometheusExporter) init(registry *prometheus.Registry) error { - // Create all necessary Prometheus objects - 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), - }, - func() float64 { return 1 }, - ) - - if err := psysinfo.registry.Register(psysinfo.sysInfoMetric); err != nil { - return fmt.Errorf("unable to register the system information metrics: %v", err) + // Create a new context to serve the metrics + if ctx, err := tag.New(context.Background(), mutators...); err == nil { + // Just record a simple hardcoded '1' to expose the system info as a metric + stats.Record(ctx, sysInfoStats.M(1)) + } else { + return errors.Wrap(err, "unable to create a context for the system info metrics") } return nil } -func (psysinfo *PrometheusExporter) getLabels(root string, i interface{}) prometheus.Labels { - labels := prometheus.Labels{} +func getSystemInfoMetricsLabels(root string, i interface{}) sysInfoMetricsLabels { + labels := sysInfoMetricsLabels{} // Iterate over each field of the given interface, recursively collecting the values as labels v := reflect.ValueOf(i).Elem() @@ -77,27 +93,14 @@ func (psysinfo *PrometheusExporter) getLabels(root string, i interface{}) promet f := v.Field(i) if f.Kind() == reflect.Struct || (f.Kind() == reflect.Ptr && f.Elem().Kind() == reflect.Struct) { // Merge labels recursively - for key, val := range psysinfo.getLabels(fieldName, f.Interface()) { + for key, val := range getSystemInfoMetricsLabels(fieldName, f.Interface()) { labels[key] = val } } else { // Store the value of the field in the labels - labels[fieldName] = fmt.Sprintf("%v", f) + key := tag.MustNewKey(fieldName) + labels[key] = fmt.Sprintf("%v", f) } } 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 -} diff --git a/pkg/sysinfo/reva.go b/pkg/sysinfo/reva.go index 1420a5252c..6438e85cc8 100644 --- a/pkg/sysinfo/reva.go +++ b/pkg/sysinfo/reva.go @@ -21,7 +21,7 @@ package sysinfo // RevaVersion stores version information about Reva. type RevaVersion struct { Version string `json:"version"` - BuildDate string `json:"build_date" sysinfo:"omitlabel"` + BuildDate string `json:"build_date"` GitCommit string `json:"git_commit" sysinfo:"omitlabel"` GoVersion string `json:"go_version" sysinfo:"omitlabel"` } diff --git a/pkg/sysinfo/sysinfo.go b/pkg/sysinfo/sysinfo.go index 8c8743df97..da4f89075a 100644 --- a/pkg/sysinfo/sysinfo.go +++ b/pkg/sysinfo/sysinfo.go @@ -22,6 +22,8 @@ import ( "encoding/json" "fmt" "reflect" + + "github.com/pkg/errors" ) // SystemInformation stores general information about Reva and the system it's running on. @@ -44,16 +46,6 @@ func (sysInfo *SystemInformation) ToJSON() (string, error) { return string(data), nil } -// InitSystemInfo initializes the global system information object. -func InitSystemInfo(revaVersion *RevaVersion) { - SysInfo = &SystemInformation{ - Reva: revaVersion, - } - - // Replace any empty values in the system information by more meaningful ones - replaceEmptyInfoValues(SysInfo) -} - func replaceEmptyInfoValues(i interface{}) { // Iterate over each field of the given interface and search for "empty" values v := reflect.ValueOf(i).Elem() @@ -71,3 +63,20 @@ func replaceEmptyInfoValues(i interface{}) { } } } + +// InitSystemInfo initializes the global system information object and also registers the corresponding metrics. +func InitSystemInfo(revaVersion *RevaVersion) error { + SysInfo = &SystemInformation{ + Reva: revaVersion, + } + + // Replace any empty values in the system information by more meaningful ones + replaceEmptyInfoValues(SysInfo) + + // Register the system information metrics, as the necessary system info object has been filled out + if err := registerSystemInfoMetrics(); err != nil { + return errors.Wrap(err, "unable to register the system info metrics") + } + + return nil +}