From adda4a9d0ea5920d72c8ebfac43cf03a2cdd855a Mon Sep 17 00:00:00 2001 From: Vladimir Buyanov Date: Sat, 23 Aug 2025 01:46:20 +0300 Subject: [PATCH 1/2] Refactor Vec types Signed-off-by: Vladimir Buyanov --- prometheus/benchmark_test.go | 20 +++--- prometheus/counter.go | 38 +++++++---- prometheus/counter_test.go | 20 +++--- prometheus/example_metricvec_test.go | 2 +- prometheus/example_timer_test.go | 2 +- prometheus/gauge.go | 40 +++++++---- prometheus/histogram.go | 16 ++--- prometheus/observer.go | 29 ++++---- prometheus/promauto/auto.go | 8 +-- prometheus/promhttp/http.go | 4 +- prometheus/promhttp/http_test.go | 2 +- prometheus/promhttp/instrument_client.go | 4 +- prometheus/promhttp/instrument_server.go | 16 ++--- prometheus/summary.go | 16 ++--- prometheus/timer.go | 12 ++-- prometheus/vec.go | 85 +++++++++++++++++++----- prometheus/vec_test.go | 72 ++++++++++---------- 17 files changed, 236 insertions(+), 150 deletions(-) diff --git a/prometheus/benchmark_test.go b/prometheus/benchmark_test.go index 046efd086..e0a9bfee1 100644 --- a/prometheus/benchmark_test.go +++ b/prometheus/benchmark_test.go @@ -19,42 +19,42 @@ import ( ) func BenchmarkCounter(b *testing.B) { - type fns []func(*CounterVec) Counter + type fns []func(CounterVec) Counter twoConstraint := func(_ string) string { return "two" } - deLV := func(m *CounterVec) Counter { + deLV := func(m CounterVec) Counter { return m.WithLabelValues("eins", "zwei", "drei") } - frLV := func(m *CounterVec) Counter { + frLV := func(m CounterVec) Counter { return m.WithLabelValues("une", "deux", "trois") } - nlLV := func(m *CounterVec) Counter { + nlLV := func(m CounterVec) Counter { return m.WithLabelValues("een", "twee", "drie") } - deML := func(m *CounterVec) Counter { + deML := func(m CounterVec) Counter { return m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"}) } - frML := func(m *CounterVec) Counter { + frML := func(m CounterVec) Counter { return m.With(Labels{"two": "deux", "one": "une", "three": "trois"}) } - nlML := func(m *CounterVec) Counter { + nlML := func(m CounterVec) Counter { return m.With(Labels{"two": "twee", "one": "een", "three": "drie"}) } deLabels := Labels{"two": "zwei", "one": "eins", "three": "drei"} - dePML := func(m *CounterVec) Counter { + dePML := func(m CounterVec) Counter { return m.With(deLabels) } frLabels := Labels{"two": "deux", "one": "une", "three": "trois"} - frPML := func(m *CounterVec) Counter { + frPML := func(m CounterVec) Counter { return m.With(frLabels) } nlLabels := Labels{"two": "twee", "one": "een", "three": "drie"} - nlPML := func(m *CounterVec) Counter { + nlPML := func(m CounterVec) Counter { return m.With(nlLabels) } diff --git a/prometheus/counter.go b/prometheus/counter.go index 4ce84e7a8..7eee639ee 100644 --- a/prometheus/counter.go +++ b/prometheus/counter.go @@ -180,18 +180,32 @@ func (c *counter) updateExemplar(v float64, l Labels) { c.exemplar.Store(e) } +type CounterVec interface { + Delete(Labels) bool + DeleteLabelValues(...string) bool + DeletePartialMatch(Labels) int + GetMetricWith(Labels) (Counter, error) + GetMetricWithLabelValues(...string) (Counter, error) + With(Labels) Counter + WithLabelValues(...string) Counter + CurryWith(Labels) (CounterVec, error) + MustCurryWith(Labels) CounterVec + Reset() + Collector +} + // CounterVec is a Collector that bundles a set of Counters that all share the // same Desc, but have different values for their variable labels. This is used // if you want to count the same thing partitioned by various dimensions // (e.g. number of HTTP requests, partitioned by response code and // method). Create instances with NewCounterVec. -type CounterVec struct { - *MetricVec +type counterVec struct { + MetricVec } // NewCounterVec creates a new CounterVec based on the provided CounterOpts and // partitioned by the given label names. -func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { +func NewCounterVec(opts CounterOpts, labelNames []string) CounterVec { return V2.NewCounterVec(CounterVecOpts{ CounterOpts: opts, VariableLabels: UnconstrainedLabels(labelNames), @@ -199,7 +213,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec { } // NewCounterVec creates a new CounterVec based on the provided CounterVecOpts. -func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { +func (v2) NewCounterVec(opts CounterVecOpts) CounterVec { desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, @@ -209,7 +223,7 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { if opts.now == nil { opts.now = time.Now } - return &CounterVec{ + return &counterVec{ MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { if len(lvs) != len(desc.variableLabels.names) { panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs)) @@ -245,7 +259,7 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec { // latter has a much more readable (albeit more verbose) syntax, but it comes // with a performance overhead (for creating and processing the Labels map). // See also the GaugeVec example. -func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { +func (v *counterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { return metric.(Counter), err @@ -265,7 +279,7 @@ func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) { // This method is used for the same purpose as // GetMetricWithLabelValues(...string). See there for pros and cons of the two // methods. -func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { +func (v *counterVec) GetMetricWith(labels Labels) (Counter, error) { metric, err := v.MetricVec.GetMetricWith(labels) if metric != nil { return metric.(Counter), err @@ -278,7 +292,7 @@ func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) { // error allows shortcuts like // // myVec.WithLabelValues("404", "GET").Add(42) -func (v *CounterVec) WithLabelValues(lvs ...string) Counter { +func (v *counterVec) WithLabelValues(lvs ...string) Counter { c, err := v.GetMetricWithLabelValues(lvs...) if err != nil { panic(err) @@ -290,7 +304,7 @@ func (v *CounterVec) WithLabelValues(lvs ...string) Counter { // returned an error. Not returning an error allows shortcuts like // // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) -func (v *CounterVec) With(labels Labels) Counter { +func (v *counterVec) With(labels Labels) Counter { c, err := v.GetMetricWith(labels) if err != nil { panic(err) @@ -311,17 +325,17 @@ func (v *CounterVec) With(labels Labels) Counter { // vectors behave identically in terms of collection. Only one must be // registered with a given registry (usually the uncurried version). The Reset // method deletes all metrics, even if called on a curried vector. -func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) { +func (v *counterVec) CurryWith(labels Labels) (CounterVec, error) { vec, err := v.MetricVec.CurryWith(labels) if vec != nil { - return &CounterVec{vec}, err + return &counterVec{vec}, err } return nil, err } // MustCurryWith works as CurryWith but panics where CurryWith would have // returned an error. -func (v *CounterVec) MustCurryWith(labels Labels) *CounterVec { +func (v *counterVec) MustCurryWith(labels Labels) CounterVec { vec, err := v.CurryWith(labels) if err != nil { panic(err) diff --git a/prometheus/counter_test.go b/prometheus/counter_test.go index 2b733494d..8519c4d11 100644 --- a/prometheus/counter_test.go +++ b/prometheus/counter_test.go @@ -320,41 +320,41 @@ func TestCounterExemplar(t *testing.T) { func TestCounterVecCreatedTimestampWithDeletes(t *testing.T) { now := time.Now() - counterVec := NewCounterVec(CounterOpts{ + vec := NewCounterVec(CounterOpts{ Name: "test", Help: "test help", now: func() time.Time { return now }, }, []string{"label"}) // First use of "With" should populate CT. - counterVec.WithLabelValues("1") + vec.WithLabelValues("1") expected := map[string]time.Time{"1": now} now = now.Add(1 * time.Hour) - expectCTsForMetricVecValues(t, counterVec.MetricVec, dto.MetricType_COUNTER, expected) + expectCTsForMetricVecValues(t, vec.(*counterVec).MetricVec, dto.MetricType_COUNTER, expected) // Two more labels at different times. - counterVec.WithLabelValues("2") + vec.WithLabelValues("2") expected["2"] = now now = now.Add(1 * time.Hour) - counterVec.WithLabelValues("3") + vec.WithLabelValues("3") expected["3"] = now now = now.Add(1 * time.Hour) - expectCTsForMetricVecValues(t, counterVec.MetricVec, dto.MetricType_COUNTER, expected) + expectCTsForMetricVecValues(t, vec.(*counterVec).MetricVec, dto.MetricType_COUNTER, expected) // Recreate metric instance should reset created timestamp to now. - counterVec.DeleteLabelValues("1") - counterVec.WithLabelValues("1") + vec.DeleteLabelValues("1") + vec.WithLabelValues("1") expected["1"] = now now = now.Add(1 * time.Hour) - expectCTsForMetricVecValues(t, counterVec.MetricVec, dto.MetricType_COUNTER, expected) + expectCTsForMetricVecValues(t, vec.(*counterVec).MetricVec, dto.MetricType_COUNTER, expected) } -func expectCTsForMetricVecValues(t testing.TB, vec *MetricVec, typ dto.MetricType, ctsPerLabelValue map[string]time.Time) { +func expectCTsForMetricVecValues(t testing.TB, vec MetricVec, typ dto.MetricType, ctsPerLabelValue map[string]time.Time) { t.Helper() for val, ct := range ctsPerLabelValue { diff --git a/prometheus/example_metricvec_test.go b/prometheus/example_metricvec_test.go index 59e43f8f8..33502511f 100644 --- a/prometheus/example_metricvec_test.go +++ b/prometheus/example_metricvec_test.go @@ -49,7 +49,7 @@ func (i Info) Write(out *dto.Metric) error { // to do to fully implement a vector for a custom Metric implementation, we do // it in this example anyway. type InfoVec struct { - *prometheus.MetricVec + prometheus.MetricVec } func NewInfoVec(name, help string, labelNames []string) *InfoVec { diff --git a/prometheus/example_timer_test.go b/prometheus/example_timer_test.go index e851a5765..df7472886 100644 --- a/prometheus/example_timer_test.go +++ b/prometheus/example_timer_test.go @@ -28,7 +28,7 @@ var requestDuration = prometheus.NewHistogram(prometheus.HistogramOpts{ func ExampleTimer() { // timer times this example function. It uses a Histogram, but a Summary - // would also work, as both implement Observer. Check out + // would also work, as both implement ObserverMethod. Check out // https://prometheus.io/docs/practices/histograms/ for differences. timer := prometheus.NewTimer(requestDuration) defer timer.ObserveDuration() diff --git a/prometheus/gauge.go b/prometheus/gauge.go index dd2eac940..accc17f79 100644 --- a/prometheus/gauge.go +++ b/prometheus/gauge.go @@ -143,13 +143,27 @@ func (g *gauge) Write(out *dto.Metric) error { // you want to count the same thing partitioned by various dimensions // (e.g. number of operations queued, partitioned by user and operation // type). Create instances with NewGaugeVec. -type GaugeVec struct { - *MetricVec +type GaugeVec interface { + Delete(labels Labels) bool + DeleteLabelValues(...string) bool + DeletePartialMatch(Labels) int + GetMetricWith(Labels) (Gauge, error) + GetMetricWithLabelValues(...string) (Gauge, error) + With(Labels) Gauge + WithLabelValues(...string) Gauge + CurryWith(Labels) (GaugeVec, error) + MustCurryWith(Labels) GaugeVec + Reset() + Collector +} + +type gaugeVec struct { + MetricVec } // NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and // partitioned by the given label names. -func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { +func NewGaugeVec(opts GaugeOpts, labelNames []string) GaugeVec { return V2.NewGaugeVec(GaugeVecOpts{ GaugeOpts: opts, VariableLabels: UnconstrainedLabels(labelNames), @@ -157,15 +171,15 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec { } // NewGaugeVec creates a new GaugeVec based on the provided GaugeVecOpts. -func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { +func (v2) NewGaugeVec(opts GaugeVecOpts) GaugeVec { desc := V2.NewDesc( BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), opts.Help, opts.VariableLabels, opts.ConstLabels, ) - return &GaugeVec{ - MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { + return &gaugeVec{ + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { if len(lvs) != len(desc.variableLabels.names) { panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs)) } @@ -199,7 +213,7 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec { // an alternative to avoid that type of mistake. For higher label numbers, the // latter has a much more readable (albeit more verbose) syntax, but it comes // with a performance overhead (for creating and processing the Labels map). -func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { +func (v *gaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { return metric.(Gauge), err @@ -219,7 +233,7 @@ func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) { // This method is used for the same purpose as // GetMetricWithLabelValues(...string). See there for pros and cons of the two // methods. -func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { +func (v *gaugeVec) GetMetricWith(labels Labels) (Gauge, error) { metric, err := v.MetricVec.GetMetricWith(labels) if metric != nil { return metric.(Gauge), err @@ -232,7 +246,7 @@ func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) { // error allows shortcuts like // // myVec.WithLabelValues("404", "GET").Add(42) -func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { +func (v *gaugeVec) WithLabelValues(lvs ...string) Gauge { g, err := v.GetMetricWithLabelValues(lvs...) if err != nil { panic(err) @@ -244,7 +258,7 @@ func (v *GaugeVec) WithLabelValues(lvs ...string) Gauge { // returned an error. Not returning an error allows shortcuts like // // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) -func (v *GaugeVec) With(labels Labels) Gauge { +func (v *gaugeVec) With(labels Labels) Gauge { g, err := v.GetMetricWith(labels) if err != nil { panic(err) @@ -265,17 +279,17 @@ func (v *GaugeVec) With(labels Labels) Gauge { // vectors behave identically in terms of collection. Only one must be // registered with a given registry (usually the uncurried version). The Reset // method deletes all metrics, even if called on a curried vector. -func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) { +func (v *gaugeVec) CurryWith(labels Labels) (GaugeVec, error) { vec, err := v.MetricVec.CurryWith(labels) if vec != nil { - return &GaugeVec{vec}, err + return &gaugeVec{vec}, err } return nil, err } // MustCurryWith works as CurryWith but panics where CurryWith would have // returned an error. -func (v *GaugeVec) MustCurryWith(labels Labels) *GaugeVec { +func (v *gaugeVec) MustCurryWith(labels Labels) GaugeVec { vec, err := v.CurryWith(labels) if err != nil { panic(err) diff --git a/prometheus/histogram.go b/prometheus/histogram.go index c453b754a..12cb7e34b 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -1171,7 +1171,7 @@ func (h *histogram) updateExemplar(v float64, bucket int, l Labels) { // (e.g. HTTP request latencies, partitioned by status code and method). Create // instances with NewHistogramVec. type HistogramVec struct { - *MetricVec + MetricVec } // NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and @@ -1192,7 +1192,7 @@ func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { opts.ConstLabels, ) return &HistogramVec{ - MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { return newHistogram(desc, opts.HistogramOpts, lvs...) }), } @@ -1222,10 +1222,10 @@ func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { // latter has a much more readable (albeit more verbose) syntax, but it comes // with a performance overhead (for creating and processing the Labels map). // See also the GaugeVec example. -func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { +func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (ObserverMethod, error) { metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { - return metric.(Observer), err + return metric.(ObserverMethod), err } return nil, err } @@ -1242,10 +1242,10 @@ func (v *HistogramVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) // This method is used for the same purpose as // GetMetricWithLabelValues(...string). See there for pros and cons of the two // methods. -func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) { +func (v *HistogramVec) GetMetricWith(labels Labels) (ObserverMethod, error) { metric, err := v.MetricVec.GetMetricWith(labels) if metric != nil { - return metric.(Observer), err + return metric.(ObserverMethod), err } return nil, err } @@ -1255,7 +1255,7 @@ func (v *HistogramVec) GetMetricWith(labels Labels) (Observer, error) { // error allows shortcuts like // // myVec.WithLabelValues("404", "GET").Observe(42.21) -func (v *HistogramVec) WithLabelValues(lvs ...string) Observer { +func (v *HistogramVec) WithLabelValues(lvs ...string) ObserverMethod { h, err := v.GetMetricWithLabelValues(lvs...) if err != nil { panic(err) @@ -1267,7 +1267,7 @@ func (v *HistogramVec) WithLabelValues(lvs ...string) Observer { // returned an error. Not returning an error allows shortcuts like // // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) -func (v *HistogramVec) With(labels Labels) Observer { +func (v *HistogramVec) With(labels Labels) ObserverMethod { h, err := v.GetMetricWith(labels) if err != nil { panic(err) diff --git a/prometheus/observer.go b/prometheus/observer.go index 03773b21f..3cdd370f2 100644 --- a/prometheus/observer.go +++ b/prometheus/observer.go @@ -13,47 +13,52 @@ package prometheus -// Observer is the interface that wraps the Observe method, which is used by +// ObserverMethod is the interface that wraps the Observe method, which is used by // Histogram and Summary to add observations. -type Observer interface { +type ObserverMethod interface { Observe(float64) } +type Observer interface { + ObserverMethod + Collector + Metric +} + // The ObserverFunc type is an adapter to allow the use of ordinary // functions as Observers. If f is a function with the appropriate -// signature, ObserverFunc(f) is an Observer that calls f. +// signature, ObserverFunc(f) is an ObserverMethod that calls f. // // This adapter is usually used in connection with the Timer type, and there are // two general use cases: // -// The most common one is to use a Gauge as the Observer for a Timer. +// The most common one is to use a Gauge as the ObserverMethod for a Timer. // See the "Gauge" Timer example. // // The more advanced use case is to create a function that dynamically decides -// which Observer to use for observing the duration. See the "Complex" Timer +// which ObserverMethod to use for observing the duration. See the "Complex" Timer // example. type ObserverFunc func(float64) -// Observe calls f(value). It implements Observer. +// Observe calls f(value). It implements ObserverMethod. func (f ObserverFunc) Observe(value float64) { f(value) } // ObserverVec is an interface implemented by `HistogramVec` and `SummaryVec`. type ObserverVec interface { - GetMetricWith(Labels) (Observer, error) - GetMetricWithLabelValues(lvs ...string) (Observer, error) - With(Labels) Observer - WithLabelValues(...string) Observer + GetMetricWith(Labels) (ObserverMethod, error) + GetMetricWithLabelValues(lvs ...string) (ObserverMethod, error) + With(Labels) ObserverMethod + WithLabelValues(...string) ObserverMethod CurryWith(Labels) (ObserverVec, error) MustCurryWith(Labels) ObserverVec - Collector } // ExemplarObserver is implemented by Observers that offer the option of // observing a value together with an exemplar. Its ObserveWithExemplar method -// works like the Observe method of an Observer but also replaces the currently +// works like the Observe method of an ObserverMethod but also replaces the currently // saved exemplar (if any) with a new one, created from the provided value, the // current time as timestamp, and the provided Labels. Empty Labels will lead to // a valid (label-less) exemplar. But if Labels is nil, the current exemplar is diff --git a/prometheus/promauto/auto.go b/prometheus/promauto/auto.go index 58f96599f..c7a1609a9 100644 --- a/prometheus/promauto/auto.go +++ b/prometheus/promauto/auto.go @@ -172,7 +172,7 @@ func NewCounter(opts prometheus.CounterOpts) prometheus.Counter { // package but it automatically registers the CounterVec with the // prometheus.DefaultRegisterer. If the registration fails, NewCounterVec // panics. -func NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec { +func NewCounterVec(opts prometheus.CounterOpts, labelNames []string) prometheus.CounterVec { return With(prometheus.DefaultRegisterer).NewCounterVec(opts, labelNames) } @@ -194,7 +194,7 @@ func NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge { // NewGaugeVec works like the function of the same name in the prometheus // package but it automatically registers the GaugeVec with the // prometheus.DefaultRegisterer. If the registration fails, NewGaugeVec panics. -func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { +func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) prometheus.GaugeVec { return With(prometheus.DefaultRegisterer).NewGaugeVec(opts, labelNames) } @@ -270,7 +270,7 @@ func (f Factory) NewCounter(opts prometheus.CounterOpts) prometheus.Counter { // NewCounterVec works like the function of the same name in the prometheus // package but it automatically registers the CounterVec with the Factory's // Registerer. -func (f Factory) NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec { +func (f Factory) NewCounterVec(opts prometheus.CounterOpts, labelNames []string) prometheus.CounterVec { c := prometheus.NewCounterVec(opts, labelNames) if f.r != nil { f.r.MustRegister(c) @@ -302,7 +302,7 @@ func (f Factory) NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge { // NewGaugeVec works like the function of the same name in the prometheus // package but it automatically registers the GaugeVec with the Factory's // Registerer. -func (f Factory) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { +func (f Factory) NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) prometheus.GaugeVec { g := prometheus.NewGaugeVec(opts, labelNames) if f.r != nil { f.r.MustRegister(g) diff --git a/prometheus/promhttp/http.go b/prometheus/promhttp/http.go index fd3c76342..9e442aa99 100644 --- a/prometheus/promhttp/http.go +++ b/prometheus/promhttp/http.go @@ -134,7 +134,7 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO if err := opts.Registry.Register(errCnt); err != nil { are := &prometheus.AlreadyRegisteredError{} if errors.As(err, are) { - errCnt = are.ExistingCollector.(*prometheus.CounterVec) + errCnt = are.ExistingCollector.(prometheus.CounterVec) } else { panic(err) } @@ -298,7 +298,7 @@ func InstrumentMetricHandler(reg prometheus.Registerer, handler http.Handler) ht if err := reg.Register(cnt); err != nil { are := &prometheus.AlreadyRegisteredError{} if errors.As(err, are) { - cnt = are.ExistingCollector.(*prometheus.CounterVec) + cnt = are.ExistingCollector.(prometheus.CounterVec) } else { panic(err) } diff --git a/prometheus/promhttp/http_test.go b/prometheus/promhttp/http_test.go index 189ee357c..0fdd940f1 100644 --- a/prometheus/promhttp/http_test.go +++ b/prometheus/promhttp/http_test.go @@ -618,7 +618,7 @@ func BenchmarkCompression(b *testing.B) { for idx := 0; idx < size.labelCount; idx++ { labelValues[idx] = fmt.Sprintf("label_val_%s_%v", strings.Repeat("v", size.labelLength), idx) } - metrics := make([]*prometheus.GaugeVec, size.metricCount) + metrics := make([]prometheus.GaugeVec, size.metricCount) for idx := 0; idx < size.metricCount; idx++ { gauge := prometheus.NewGaugeVec(prometheus.GaugeOpts{ Name: fmt.Sprintf("avalanche_metric_%s_%v_%v", strings.Repeat("m", size.metricLength), 0, idx), diff --git a/prometheus/promhttp/instrument_client.go b/prometheus/promhttp/instrument_client.go index d3482c40c..3117b5321 100644 --- a/prometheus/promhttp/instrument_client.go +++ b/prometheus/promhttp/instrument_client.go @@ -62,7 +62,7 @@ func InstrumentRoundTripperInFlight(gauge prometheus.Gauge, next http.RoundTripp // Use with WithExemplarFromContext to instrument the exemplars on the counter of requests. // // See the example for ExampleInstrumentRoundTripperDuration for example usage. -func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { +func InstrumentRoundTripperCounter(counter prometheus.CounterVec, next http.RoundTripper, opts ...Option) RoundTripperFunc { rtOpts := defaultOptions() for _, o := range opts { o.apply(rtOpts) @@ -91,7 +91,7 @@ func InstrumentRoundTripperCounter(counter *prometheus.CounterVec, next http.Rou // "method". The function panics otherwise. For the "method" label a predefined // default label value set is used to filter given values. Values besides // predefined values will count as `unknown` method. `WithExtraMethods` -// can be used to add more methods to the set. The Observe method of the Observer +// can be used to add more methods to the set. The Observe method of the ObserverMethod // in the ObserverVec is called with the request duration in // seconds. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For diff --git a/prometheus/promhttp/instrument_server.go b/prometheus/promhttp/instrument_server.go index 9332b0249..98361f7e2 100644 --- a/prometheus/promhttp/instrument_server.go +++ b/prometheus/promhttp/instrument_server.go @@ -29,8 +29,8 @@ import ( const magicString = "zZgWfBxLqvG8kc8IMv3POi2Bb0tZI3vAnBx+gBaFi9FyPzB/CzKUer1yufDa" // observeWithExemplar is a wrapper for [prometheus.ExemplarAdder.ExemplarObserver], -// which falls back to [prometheus.Observer.Observe] if no labels are provided. -func observeWithExemplar(obs prometheus.Observer, val float64, labels map[string]string) { +// which falls back to [prometheus.ObserverMethod.Observe] if no labels are provided. +func observeWithExemplar(obs prometheus.ObserverMethod, val float64, labels map[string]string) { if labels == nil { obs.Observe(val) return @@ -69,7 +69,7 @@ func InstrumentHandlerInFlight(g prometheus.Gauge, next http.Handler) http.Handl // label a predefined default label value set is used to filter given values. // Values besides predefined values will count as `unknown` method. // `WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the request duration +// method of the ObserverMethod in the ObserverVec is called with the request duration // in seconds. Partitioning happens by HTTP status code and/or HTTP method if // the respective instance label names are present in the ObserverVec. For // unpartitioned observations, use an ObserverVec with zero labels. Note that @@ -132,7 +132,7 @@ func InstrumentHandlerDuration(obs prometheus.ObserverVec, next http.Handler, op // If the wrapped Handler panics, the Counter is not incremented. // // See the example for InstrumentHandlerDuration for example usage. -func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, opts ...Option) http.HandlerFunc { +func InstrumentHandlerCounter(counter prometheus.CounterVec, next http.Handler, opts ...Option) http.HandlerFunc { hOpts := defaultOptions() for _, o := range opts { o.apply(hOpts) @@ -173,7 +173,7 @@ func InstrumentHandlerCounter(counter *prometheus.CounterVec, next http.Handler, // function panics otherwise. For the "method" label a predefined default label // value set is used to filter given values. Values besides predefined values // will count as `unknown` method.`WithExtraMethods` can be used to add more -// methods to the set. The Observe method of the Observer in the +// methods to the set. The Observe method of the ObserverMethod in the // ObserverVec is called with the request duration in seconds. Partitioning // happens by HTTP status code and/or HTTP method if the respective instance // label names are present in the ObserverVec. For unpartitioned observations, @@ -217,7 +217,7 @@ func InstrumentHandlerTimeToWriteHeader(obs prometheus.ObserverVec, next http.Ha // label a predefined default label value set is used to filter given values. // Values besides predefined values will count as `unknown` method. // `WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the request size in +// method of the ObserverMethod in the ObserverVec is called with the request size in // bytes. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For // unpartitioned observations, use an ObserverVec with zero labels. Note that @@ -271,7 +271,7 @@ func InstrumentHandlerRequestSize(obs prometheus.ObserverVec, next http.Handler, // label a predefined default label value set is used to filter given values. // Values besides predefined values will count as `unknown` method. // `WithExtraMethods` can be used to add more methods to the set. The Observe -// method of the Observer in the ObserverVec is called with the response size in +// method of the ObserverMethod in the ObserverVec is called with the response size in // bytes. Partitioning happens by HTTP status code and/or HTTP method if the // respective instance label names are present in the ObserverVec. For // unpartitioned observations, use an ObserverVec with zero labels. Note that @@ -375,7 +375,7 @@ func isLabelCurried(c prometheus.Collector, label string) bool { // But for that, we need to type-convert to the two // types we use here, ObserverVec or *CounterVec. switch v := c.(type) { - case *prometheus.CounterVec: + case prometheus.CounterVec: if _, err := v.CurryWith(prometheus.Labels{label: "dummy"}); err == nil { return false } diff --git a/prometheus/summary.go b/prometheus/summary.go index ac5203c6f..f8c917a35 100644 --- a/prometheus/summary.go +++ b/prometheus/summary.go @@ -550,7 +550,7 @@ func (s quantSort) Less(i, j int) bool { // (e.g. HTTP request latencies, partitioned by status code and method). Create // instances with NewSummaryVec. type SummaryVec struct { - *MetricVec + MetricVec } // NewSummaryVec creates a new SummaryVec based on the provided SummaryOpts and @@ -580,7 +580,7 @@ func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { opts.ConstLabels, ) return &SummaryVec{ - MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { + MetricVec: newMetricVec(desc, func(lvs ...string) Metric { return newSummary(desc, opts.SummaryOpts, lvs...) }), } @@ -610,10 +610,10 @@ func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { // latter has a much more readable (albeit more verbose) syntax, but it comes // with a performance overhead (for creating and processing the Labels map). // See also the GaugeVec example. -func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { +func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (ObserverMethod, error) { metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...) if metric != nil { - return metric.(Observer), err + return metric.(ObserverMethod), err } return nil, err } @@ -630,10 +630,10 @@ func (v *SummaryVec) GetMetricWithLabelValues(lvs ...string) (Observer, error) { // This method is used for the same purpose as // GetMetricWithLabelValues(...string). See there for pros and cons of the two // methods. -func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) { +func (v *SummaryVec) GetMetricWith(labels Labels) (ObserverMethod, error) { metric, err := v.MetricVec.GetMetricWith(labels) if metric != nil { - return metric.(Observer), err + return metric.(ObserverMethod), err } return nil, err } @@ -643,7 +643,7 @@ func (v *SummaryVec) GetMetricWith(labels Labels) (Observer, error) { // error allows shortcuts like // // myVec.WithLabelValues("404", "GET").Observe(42.21) -func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { +func (v *SummaryVec) WithLabelValues(lvs ...string) ObserverMethod { s, err := v.GetMetricWithLabelValues(lvs...) if err != nil { panic(err) @@ -655,7 +655,7 @@ func (v *SummaryVec) WithLabelValues(lvs ...string) Observer { // returned an error. Not returning an error allows shortcuts like // // myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Observe(42.21) -func (v *SummaryVec) With(labels Labels) Observer { +func (v *SummaryVec) With(labels Labels) ObserverMethod { s, err := v.GetMetricWith(labels) if err != nil { panic(err) diff --git a/prometheus/timer.go b/prometheus/timer.go index 52344fef5..0c1b77e9d 100644 --- a/prometheus/timer.go +++ b/prometheus/timer.go @@ -19,11 +19,11 @@ import "time" // instances. type Timer struct { begin time.Time - observer Observer + observer ObserverMethod } -// NewTimer creates a new Timer. The provided Observer is used to observe a -// duration in seconds. If the Observer implements ExemplarObserver, passing exemplar +// NewTimer creates a new Timer. The provided ObserverMethod is used to observe a +// duration in seconds. If the ObserverMethod implements ExemplarObserver, passing exemplar // later on will be also supported. // Timer is usually used to time a function call in the // following way: @@ -41,7 +41,7 @@ type Timer struct { // defer timer.ObserveDurationWithExemplar(exemplar) // // Do actual work. // } -func NewTimer(o Observer) *Timer { +func NewTimer(o ObserverMethod) *Timer { return &Timer{ begin: time.Now(), observer: o, @@ -49,7 +49,7 @@ func NewTimer(o Observer) *Timer { } // ObserveDuration records the duration passed since the Timer was created with -// NewTimer. It calls the Observe method of the Observer provided during +// NewTimer. It calls the Observe method of the ObserverMethod provided during // construction with the duration in seconds as an argument. The observed // duration is also returned. ObserveDuration is usually called with a defer // statement. @@ -65,7 +65,7 @@ func (t *Timer) ObserveDuration() time.Duration { } // ObserveDurationWithExemplar is like ObserveDuration, but it will also -// observe exemplar with the duration unless exemplar is nil or provided Observer can't +// observe exemplar with the duration unless exemplar is nil or provided ObserverMethod can't // be casted to ExemplarObserver. func (t *Timer) ObserveDurationWithExemplar(exemplar Labels) time.Duration { d := time.Since(t.begin) diff --git a/prometheus/vec.go b/prometheus/vec.go index 487b46656..e5f97e376 100644 --- a/prometheus/vec.go +++ b/prometheus/vec.go @@ -33,7 +33,21 @@ import ( // (*FooVec, error) rather than (*MetricVec, error). It is recommended to also // add the convenience methods WithLabelValues, With, and MustCurryWith, which // panic instead of returning errors. See also the MetricVec example. -type MetricVec struct { +type MetricVec interface { + Delete(labels Labels) bool + DeleteLabelValues(lvs ...string) bool + DeletePartialMatch(labels Labels) int + GetMetricWith(Labels) (Metric, error) + GetMetricWithLabelValues(lvs ...string) (Metric, error) + With(Labels) Metric + WithLabelValues(...string) Metric + CurryWith(Labels) (MetricVec, error) + MustCurryWith(Labels) MetricVec + Reset() + Collector +} + +type metricVec struct { *metricMap curry []curriedLabelValue @@ -43,9 +57,8 @@ type MetricVec struct { hashAddByte func(h uint64, b byte) uint64 } -// NewMetricVec returns an initialized metricVec. -func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { - return &MetricVec{ +func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { + return &metricVec{ metricMap: &metricMap{ metrics: map[uint64][]metricWithLabelValues{}, desc: desc, @@ -56,6 +69,11 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { } } +// NewMetricVec returns an initialized metricVec. +func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) MetricVec { + return newMetricVec(desc, newMetric) +} + // DeleteLabelValues removes the metric where the variable labels are the same // as those passed in as labels (same order as the VariableLabels in Desc). It // returns true if a metric was deleted. @@ -71,7 +89,7 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec { // latter has a much more readable (albeit more verbose) syntax, but it comes // with a performance overhead (for creating and processing the Labels map). // See also the CounterVec example. -func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { +func (m *metricVec) DeleteLabelValues(lvs ...string) bool { lvs = constrainLabelValues(m.desc, lvs, m.curry) h, err := m.hashLabelValues(lvs) @@ -92,7 +110,7 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool { // // This method is used for the same purpose as DeleteLabelValues(...string). See // there for pros and cons of the two methods. -func (m *MetricVec) Delete(labels Labels) bool { +func (m *metricVec) Delete(labels Labels) bool { labels, closer := constrainLabels(m.desc, labels) defer closer() @@ -110,7 +128,7 @@ func (m *MetricVec) Delete(labels Labels) bool { // // Note that curried labels will never be matched if deleting from the curried vector. // To match curried labels with DeletePartialMatch, it must be called on the base vector. -func (m *MetricVec) DeletePartialMatch(labels Labels) int { +func (m *metricVec) DeletePartialMatch(labels Labels) int { labels, closer := constrainLabels(m.desc, labels) defer closer() @@ -121,13 +139,13 @@ func (m *MetricVec) DeletePartialMatch(labels Labels) int { // show up in GoDoc. // Describe implements Collector. -func (m *MetricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) } +func (m *metricVec) Describe(ch chan<- *Desc) { m.metricMap.Describe(ch) } // Collect implements Collector. -func (m *MetricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) } +func (m *metricVec) Collect(ch chan<- Metric) { m.metricMap.Collect(ch) } // Reset deletes all metrics in this vector. -func (m *MetricVec) Reset() { m.metricMap.Reset() } +func (m *metricVec) Reset() { m.metricMap.Reset() } // CurryWith returns a vector curried with the provided labels, i.e. the // returned vector has those labels pre-set for all labeled operations performed @@ -146,7 +164,7 @@ func (m *MetricVec) Reset() { m.metricMap.Reset() } // Note that CurryWith is usually not called directly but through a wrapper // around MetricVec, implementing a vector for a specific Metric // implementation, for example GaugeVec. -func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { +func (m *metricVec) CurryWith(labels Labels) (MetricVec, error) { var ( newCurry []curriedLabelValue oldCurry = m.curry @@ -174,7 +192,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { return nil, fmt.Errorf("%d unknown label(s) found during currying", l) } - return &MetricVec{ + return &metricVec{ metricMap: m.metricMap, curry: newCurry, hashAdd: m.hashAdd, @@ -182,6 +200,16 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { }, nil } +// MustCurryWith works as CurryWith but panics where CurryWith would have +// returned an error. +func (m *metricVec) MustCurryWith(labels Labels) MetricVec { + vec, err := m.CurryWith(labels) + if err != nil { + panic(err) + } + return vec +} + // GetMetricWithLabelValues returns the Metric for the given slice of label // values (same order as the variable labels in Desc). If that combination of // label values is accessed for the first time, a new Metric is created (by @@ -209,7 +237,7 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) { // Note that GetMetricWithLabelValues is usually not called directly but through // a wrapper around MetricVec, implementing a vector for a specific Metric // implementation, for example GaugeVec. -func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { +func (m *metricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { lvs = constrainLabelValues(m.desc, lvs, m.curry) h, err := m.hashLabelValues(lvs) if err != nil { @@ -235,7 +263,7 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) { // Note that GetMetricWith is usually not called directly but through a wrapper // around MetricVec, implementing a vector for a specific Metric implementation, // for example GaugeVec. -func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { +func (m *metricVec) GetMetricWith(labels Labels) (Metric, error) { labels, closer := constrainLabels(m.desc, labels) defer closer() @@ -247,7 +275,32 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) { return m.getOrCreateMetricWithLabels(h, labels, m.curry), nil } -func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { +// With works as GetMetricWith, but panics where GetMetricWithLabels would have +// returned an error. Not returning an error allows shortcuts like +// +// myVec.With(prometheus.Labels{"code": "404", "method": "GET"}).Add(42) +func (m *metricVec) With(labels Labels) Metric { + metric, err := m.GetMetricWith(labels) + if err != nil { + panic(err) + } + return metric +} + +// WithLabelValues works as GetMetricWithLabelValues, but panics where +// GetMetricWithLabelValues would have returned an error. Not returning an +// error allows shortcuts like +// +// myVec.WithLabelValues("404", "GET").Add(42) +func (m *metricVec) WithLabelValues(lvs ...string) Metric { + metric, err := m.GetMetricWithLabelValues(lvs...) + if err != nil { + panic(err) + } + return metric +} + +func (m *metricVec) hashLabelValues(vals []string) (uint64, error) { if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil { return 0, err } @@ -270,7 +323,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) { return h, nil } -func (m *MetricVec) hashLabels(labels Labels) (uint64, error) { +func (m *metricVec) hashLabels(labels Labels) (uint64, error) { if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil { return 0, err } diff --git a/prometheus/vec_test.go b/prometheus/vec_test.go index 03223f2f6..3ec14cc9f 100644 --- a/prometheus/vec_test.go +++ b/prometheus/vec_test.go @@ -40,9 +40,9 @@ func TestDeleteWithCollisions(t *testing.T) { Help: "helpless", }, []string{"l1", "l2"}, - ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + ).(*gaugeVec) + vec.MetricVec.(*metricVec).hashAdd = func(h uint64, s string) uint64 { return 1 } + vec.MetricVec.(*metricVec).hashAddByte = func(h uint64, b byte) uint64 { return 1 } testDelete(t, vec) } @@ -60,7 +60,7 @@ func TestDeleteWithConstraints(t *testing.T) { testDelete(t, vec) } -func testDelete(t *testing.T, vec *GaugeVec) { +func testDelete(t *testing.T, vec GaugeVec) { if got, want := vec.Delete(Labels{"l1": "v1", "l2": "v2"}), false; got != want { t.Errorf("got %v, want %v", got, want) } @@ -108,9 +108,9 @@ func TestDeleteLabelValuesWithCollisions(t *testing.T) { Help: "helpless", }, []string{"l1", "l2"}, - ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + ).(*gaugeVec) + vec.MetricVec.(*metricVec).hashAdd = func(h uint64, s string) uint64 { return 1 } + vec.MetricVec.(*metricVec).hashAddByte = func(h uint64, b byte) uint64 { return 1 } testDeleteLabelValues(t, vec) } @@ -128,7 +128,7 @@ func TestDeleteLabelValuesWithConstraints(t *testing.T) { testDeleteLabelValues(t, vec) } -func testDeleteLabelValues(t *testing.T, vec *GaugeVec) { +func testDeleteLabelValues(t *testing.T, vec GaugeVec) { if got, want := vec.DeleteLabelValues("v1", "v2"), false; got != want { t.Errorf("got %v, want %v", got, want) } @@ -162,7 +162,7 @@ func TestDeletePartialMatch(t *testing.T) { Help: "helpless", }, []string{"l1", "l2", "l3"}, - ) + ).(*gaugeVec) testDeletePartialMatch(t, vec) } @@ -177,13 +177,13 @@ func TestDeletePartialMatchWithConstraints(t *testing.T) { {Name: "l2", Constraint: func(s string) string { return "x" + s }}, {Name: "l3"}, }, - }) + }).(*gaugeVec) testDeletePartialMatch(t, vec) } -func testDeletePartialMatch(t *testing.T, baseVec *GaugeVec) { +func testDeletePartialMatch(t *testing.T, baseVec *gaugeVec) { assertNoMetric := func(t *testing.T) { - if n := len(baseVec.metrics); n != 0 { + if n := len(baseVec.MetricVec.(*metricVec).metrics); n != 0 { t.Error("expected no metrics, got", n) } } @@ -198,7 +198,7 @@ func testDeletePartialMatch(t *testing.T, baseVec *GaugeVec) { baseVec.With(Labels{"l1": "multiDeleteV1", "l2": "diff2BaseValue2", "l3": "v3"}).Set(84) baseVec.With(Labels{"l1": "multiDeleteV1", "l2": "diff3BaseValue2", "l3": "v3"}).Set(168) - curriedVec := baseVec.MustCurryWith(Labels{"l2": "curriedValue2"}) + curriedVec := baseVec.MustCurryWith(Labels{"l2": "curriedValue2"}).(*gaugeVec) curriedVec.WithLabelValues("curriedValue1", "curriedValue3").Inc() curriedVec.WithLabelValues("curriedValue1", "differentCurriedValue3").Inc() curriedVec.WithLabelValues("differentCurriedValue1", "differentCurriedValue3").Inc() @@ -267,7 +267,7 @@ func TestMetricVec(t *testing.T) { Help: "helpless", }, []string{"l1", "l2"}, - ) + ).(*gaugeVec) testMetricVec(t, vec) } @@ -278,13 +278,13 @@ func TestMetricVecWithCollisions(t *testing.T) { Help: "helpless", }, []string{"l1", "l2"}, - ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + ).(*gaugeVec) + vec.MetricVec.(*metricVec).hashAdd = func(h uint64, s string) uint64 { return 1 } + vec.MetricVec.(*metricVec).hashAddByte = func(h uint64, b byte) uint64 { return 1 } testMetricVec(t, vec) } -func testMetricVec(t *testing.T, vec *GaugeVec) { +func testMetricVec(t *testing.T, vec *gaugeVec) { vec.Reset() // Actually test Reset now! var pair [2]string @@ -301,7 +301,7 @@ func testMetricVec(t *testing.T, vec *GaugeVec) { } var total int - for _, metrics := range vec.metrics { + for _, metrics := range vec.MetricVec.(*metricVec).metrics { for _, metric := range metrics { total++ copy(pair[:], metric.values) @@ -336,7 +336,7 @@ func testMetricVec(t *testing.T, vec *GaugeVec) { vec.Reset() - if len(vec.metrics) > 0 { + if len(vec.MetricVec.(*metricVec).metrics) > 0 { t.Fatalf("reset failed") } } @@ -352,11 +352,11 @@ func TestMetricVecWithConstraints(t *testing.T) { {Name: "l1"}, {Name: "l2", Constraint: constraint}, }, - }) + }).(*gaugeVec) testConstrainedMetricVec(t, vec, constraint) } -func testConstrainedMetricVec(t *testing.T, vec *GaugeVec, constrain func(string) string) { +func testConstrainedMetricVec(t *testing.T, vec *gaugeVec, constrain func(string) string) { vec.Reset() // Actually test Reset now! var pair [2]string @@ -373,7 +373,7 @@ func testConstrainedMetricVec(t *testing.T, vec *GaugeVec, constrain func(string } var total int - for _, metrics := range vec.metrics { + for _, metrics := range vec.MetricVec.(*metricVec).metrics { for _, metric := range metrics { total++ copy(pair[:], metric.values) @@ -408,7 +408,7 @@ func testConstrainedMetricVec(t *testing.T, vec *GaugeVec, constrain func(string vec.Reset() - if len(vec.metrics) > 0 { + if len(vec.MetricVec.(*metricVec).metrics) > 0 { t.Fatalf("reset failed") } } @@ -453,7 +453,7 @@ func TestCurryVec(t *testing.T) { Help: "helpless", }, []string{"one", "two", "three"}, - ) + ).(*counterVec) testCurryVec(t, vec) } @@ -464,9 +464,9 @@ func TestCurryVecWithCollisions(t *testing.T) { Help: "helpless", }, []string{"one", "two", "three"}, - ) - vec.hashAdd = func(h uint64, s string) uint64 { return 1 } - vec.hashAddByte = func(h uint64, b byte) uint64 { return 1 } + ).(*counterVec) + vec.MetricVec.(*metricVec).hashAdd = func(h uint64, s string) uint64 { return 1 } + vec.MetricVec.(*metricVec).hashAddByte = func(h uint64, b byte) uint64 { return 1 } testCurryVec(t, vec) } @@ -483,7 +483,7 @@ func TestCurryVecWithConstraints(t *testing.T) { {Name: "two"}, {Name: "three", Constraint: constraint}, }, - }) + }).(*counterVec) testCurryVec(t, vec) }) t.Run("constrainedLabels reducing cardinality", func(t *testing.T) { @@ -498,15 +498,15 @@ func TestCurryVecWithConstraints(t *testing.T) { {Name: "two"}, {Name: "three", Constraint: constraint}, }, - }) + }).(*counterVec) testConstrainedCurryVec(t, vec, constraint) }) } -func testCurryVec(t *testing.T, vec *CounterVec) { +func testCurryVec(t *testing.T, vec *counterVec) { assertMetrics := func(t *testing.T) { n := 0 - for _, m := range vec.metrics { + for _, m := range vec.MetricVec.(*metricVec).metrics { n += len(m) } if n != 2 { @@ -533,7 +533,7 @@ func testCurryVec(t *testing.T, vec *CounterVec) { } assertNoMetric := func(t *testing.T) { - if n := len(vec.metrics); n != 0 { + if n := len(vec.MetricVec.(*metricVec).metrics); n != 0 { t.Error("expected no metrics, got", n) } } @@ -700,10 +700,10 @@ func testCurryVec(t *testing.T, vec *CounterVec) { }) } -func testConstrainedCurryVec(t *testing.T, vec *CounterVec, constraint func(string) string) { +func testConstrainedCurryVec(t *testing.T, vec *counterVec, constraint func(string) string) { assertMetrics := func(t *testing.T) { n := 0 - for _, m := range vec.metrics { + for _, m := range vec.MetricVec.(*metricVec).metrics { n += len(m) } if n != 2 { @@ -744,7 +744,7 @@ func testConstrainedCurryVec(t *testing.T, vec *CounterVec, constraint func(stri } assertNoMetric := func(t *testing.T) { - if n := len(vec.metrics); n != 0 { + if n := len(vec.MetricVec.(*metricVec).metrics); n != 0 { t.Error("expected no metrics, got", n) } } From f2cd70004545c43a6dc2d90bb476ef2d3a8fd50b Mon Sep 17 00:00:00 2001 From: Vladimir Buyanov Date: Sat, 23 Aug 2025 18:21:25 +0300 Subject: [PATCH 2/2] Small fix Signed-off-by: Vladimir Buyanov --- prometheus/gauge.go | 2 +- prometheus/histogram.go | 2 +- prometheus/summary.go | 2 +- prometheus/vec.go | 8 ++------ 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/prometheus/gauge.go b/prometheus/gauge.go index accc17f79..40855b6f5 100644 --- a/prometheus/gauge.go +++ b/prometheus/gauge.go @@ -179,7 +179,7 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) GaugeVec { opts.ConstLabels, ) return &gaugeVec{ - MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { if len(lvs) != len(desc.variableLabels.names) { panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs)) } diff --git a/prometheus/histogram.go b/prometheus/histogram.go index 12cb7e34b..971725b67 100644 --- a/prometheus/histogram.go +++ b/prometheus/histogram.go @@ -1192,7 +1192,7 @@ func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec { opts.ConstLabels, ) return &HistogramVec{ - MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { return newHistogram(desc, opts.HistogramOpts, lvs...) }), } diff --git a/prometheus/summary.go b/prometheus/summary.go index f8c917a35..2ebe772c8 100644 --- a/prometheus/summary.go +++ b/prometheus/summary.go @@ -580,7 +580,7 @@ func (v2) NewSummaryVec(opts SummaryVecOpts) *SummaryVec { opts.ConstLabels, ) return &SummaryVec{ - MetricVec: newMetricVec(desc, func(lvs ...string) Metric { + MetricVec: NewMetricVec(desc, func(lvs ...string) Metric { return newSummary(desc, opts.SummaryOpts, lvs...) }), } diff --git a/prometheus/vec.go b/prometheus/vec.go index e5f97e376..6ced41a09 100644 --- a/prometheus/vec.go +++ b/prometheus/vec.go @@ -57,7 +57,8 @@ type metricVec struct { hashAddByte func(h uint64, b byte) uint64 } -func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { +// NewMetricVec returns an initialized metricVec. +func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) MetricVec { return &metricVec{ metricMap: &metricMap{ metrics: map[uint64][]metricWithLabelValues{}, @@ -69,11 +70,6 @@ func newMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *metricVec { } } -// NewMetricVec returns an initialized metricVec. -func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) MetricVec { - return newMetricVec(desc, newMetric) -} - // DeleteLabelValues removes the metric where the variable labels are the same // as those passed in as labels (same order as the VariableLabels in Desc). It // returns true if a metric was deleted.