From 8b805646e2ebfe673fef7327a383e5ab2fe1e740 Mon Sep 17 00:00:00 2001 From: Gang Li Date: Wed, 19 Feb 2025 23:28:53 +0000 Subject: [PATCH] add prometheus metric Signed-off-by: Gang Li --- pkg/featuregate/feature_gate.go | 17 +++++++--- pkg/featuregate/feature_gate_test.go | 48 ++++++++++++++++++++++++++++ server/etcdserver/metrics.go | 9 ++++++ server/etcdserver/server.go | 2 ++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/pkg/featuregate/feature_gate.go b/pkg/featuregate/feature_gate.go index c899616d92a6..9c2bb24c6f77 100644 --- a/pkg/featuregate/feature_gate.go +++ b/pkg/featuregate/feature_gate.go @@ -25,6 +25,7 @@ import ( "sync" "sync/atomic" + "github.com/prometheus/client_golang/prometheus" "github.com/spf13/pflag" "go.uber.org/zap" ) @@ -94,6 +95,8 @@ type FeatureGate interface { DeepCopy() MutableFeatureGate // String returns a string containing all enabled feature gates, formatted as "key1=value1,key2=value2,...". String() string + // AddMetrics adds feature enablement metrics + AddMetrics(gaugeVec *prometheus.GaugeVec) } // MutableFeatureGate parses and stores flag gates for known features from @@ -112,8 +115,6 @@ type MutableFeatureGate interface { Add(features map[Feature]FeatureSpec) error // GetAll returns a copy of the map of known feature names to feature specs. GetAll() map[Feature]FeatureSpec - // AddMetrics adds feature enablement metrics - AddMetrics() // OverrideDefault sets a local override for the registered default value of a named // feature. If the feature has not been previously registered (e.g. by a call to Add), has a // locked default, or if the gate has already registered itself with a FlagSet, a non-nil @@ -363,8 +364,16 @@ func (f *featureGate) AddFlag(fs *flag.FlagSet, flagName string) { "Options are:\n"+strings.Join(known, "\n")) } -func (f *featureGate) AddMetrics() { - // TODO(henrybear327): implement this. +func (f *featureGate) AddMetrics(gaugeVec *prometheus.GaugeVec) { + for Feature, FeatureSpec := range f.GetAll() { + var metricVal float64 + if f.Enabled(Feature) { + metricVal = 1 + } else { + metricVal = 0 + } + gaugeVec.With(prometheus.Labels{"name": string(Feature), "stage": string(FeatureSpec.PreRelease)}).Set(metricVal) + } } // KnownFeatures returns a slice of strings describing the FeatureGate's known features. diff --git a/pkg/featuregate/feature_gate_test.go b/pkg/featuregate/feature_gate_test.go index 5dc5a86537d7..d8f42d04c756 100644 --- a/pkg/featuregate/feature_gate_test.go +++ b/pkg/featuregate/feature_gate_test.go @@ -20,6 +20,7 @@ import ( "strings" "testing" + "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" @@ -555,3 +556,50 @@ func TestFeatureGateOverrideDefault(t *testing.T) { assert.Errorf(t, err, "expected a non-nil error to be returned") }) } + +func TestAddMetric(t *testing.T) { + + etcdServerFeatureEnabled = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "etcd", + Subsystem: "server", + Name: "etcd_server_feature_enabled", + Help: "Whether or not a feature is enabled. 1 is enabled, 0 is not.", + }, + []string{"name", "stage"}, + ) + + const testAlphaGate Feature = "TestAlpha" + const testBetaGate Feature = "TestBeta" + const testGAGate Feature = "TestGA" + + featuremap := map[Feature]FeatureSpec{ + testGAGate: {Default: true, PreRelease: GA}, + testAlphaGate: {Default: false, PreRelease: Alpha}, + testBetaGate: {Default: false, PreRelease: Beta}, + } + + expectedMetrics := []metricValue{ + {featureName: "TestAlpha", preRelease: "Alpha", value: 1}, + {featureName: "TestBeta", preRelease: "Beta", value: 1}, + {featureName: "TestGA", preRelease: "GA", value: 0}, + } + + f := New("test", zaptest.NewLogger(t)) + f.Add(featuremap) + f.SetFromMap(map[string]bool{"testAlphaGate": true}) + + type metricValue struct { + featureName string + preRelease string + value float64 + } + + f.AddMetric(etcdServerFeatureEnabled) + + for _, expected := range expectedMetrics { + gotVal, err := etcdServerFeatureEnabled.GetMetricWithLabelValues(expected.featureName, expected.preRelease) + require.NoError(t, err) + require.Equal(t, expected.value, gotVal) + } +} diff --git a/server/etcdserver/metrics.go b/server/etcdserver/metrics.go index 5f3c2f51368f..e48e820a5917 100644 --- a/server/etcdserver/metrics.go +++ b/server/etcdserver/metrics.go @@ -140,6 +140,15 @@ var ( }, []string{"server_id"}, ) + etcdServerFeatureEnabled = prometheus.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: "etcd", + Subsystem: "server", + Name: "etcd_server_feature_enabled", + Help: "Whether or not a feature is enabled. 1 is enabled, 0 is not.", + }, + []string{"name", "stage"}, + ) fdUsed = prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: "os", Subsystem: "fd", diff --git a/server/etcdserver/server.go b/server/etcdserver/server.go index 2c45553bc833..b8238b99efc2 100644 --- a/server/etcdserver/server.go +++ b/server/etcdserver/server.go @@ -342,6 +342,8 @@ func NewServer(cfg config.ServerConfig) (srv *EtcdServer, err error) { firstCommitInTerm: notify.NewNotifier(), clusterVersionChanged: notify.NewNotifier(), } + + cfg.ServerFeatureGate.(featuregate.MutableFeatureGate).AddMetrics(etcdServerFeatureEnabled) serverID.With(prometheus.Labels{"server_id": b.cluster.nodeID.String()}).Set(1) srv.cluster.SetVersionChangedNotifier(srv.clusterVersionChanged)