From a33529c0e48f060539517478c380df41c0753c55 Mon Sep 17 00:00:00 2001 From: Anna Kapuscinska Date: Wed, 10 Jul 2024 15:37:37 +0100 Subject: [PATCH] metrics: Fix custom initialization New... functions in pkg/metrics take an initialization function as an argument, in case custom initialization is needed. However, such function passed as part of a metric definition can't reference this metric - this would mean an initialization cycle. This commit changes the initialization functions signatures to take an inner prometheus metric as an argument, so that we can actually initialize it and avoid a cycle. Signed-off-by: Anna Kapuscinska --- pkg/metrics/counter.go | 24 +++++++++++++++--------- pkg/metrics/gauge.go | 21 +++++++++++++-------- pkg/metrics/granularmetric.go | 8 ++++---- pkg/metrics/histogram.go | 21 +++++++++++++-------- 4 files changed, 45 insertions(+), 29 deletions(-) diff --git a/pkg/metrics/counter.go b/pkg/metrics/counter.go index 2e7095765d5..12ad40186e4 100644 --- a/pkg/metrics/counter.go +++ b/pkg/metrics/counter.go @@ -7,6 +7,8 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +type initCounterFunc func(*prometheus.CounterVec) + // NewCounterVecWithPod is a wrapper around prometheus.NewCounterVec that also // registers the metric to be cleaned up when a pod is deleted. // @@ -37,7 +39,7 @@ func NewCounterVecWithPodV2(opts prometheus.CounterVecOpts) *prometheus.CounterV type GranularCounter[L FilteredLabels] struct { metric *prometheus.CounterVec constrained bool - initFunc func() + initFunc initCounterFunc initForDocs func() } @@ -56,8 +58,9 @@ type GranularCounter[L FilteredLabels] struct { // same thing in different formats, or two labels are mutually exclusive). // - metric is unconstrained, but some of the unconstrained label values are // known beforehand, so can be initialized. -// - you want to disable default initialization - pass func() {} in such case -func NewGranularCounter[L FilteredLabels](opts Opts, init func()) (*GranularCounter[L], error) { +// - you want to disable default initialization - pass +// func(*prometheus.CounterVec) {} in such case +func NewGranularCounter[L FilteredLabels](opts Opts, init initCounterFunc) (*GranularCounter[L], error) { labels, constrained, err := getVariableLabels[L](&opts) if err != nil { return nil, err @@ -85,9 +88,12 @@ func NewGranularCounter[L FilteredLabels](opts Opts, init func()) (*GranularCoun metric.WithLabelValues(lvs...).Add(0) } - // if metric is constrained, default to initializing all combinations of labels + // If metric is constrained, default to initializing all combinations of + // labels. Note that in such case the initialization function doesn't + // reference the wrapped metric passed as an argument because this metric + // is captured already in initMetric closure. if constrained && init == nil { - init = func() { + init = func(_ *prometheus.CounterVec) { initAllCombinations(initMetric, opts.ConstrainedLabels) } } @@ -125,7 +131,7 @@ func MustNewGranularCounter[L FilteredLabels](promOpts prometheus.CounterOpts, e // MustNewGranularCounterWithInit is a convenience function that wraps // NewGranularCounter and panics on error. -func MustNewGranularCounterWithInit[L FilteredLabels](opts Opts, init func()) *GranularCounter[L] { +func MustNewGranularCounterWithInit[L FilteredLabels](opts Opts, init initCounterFunc) *GranularCounter[L] { metric, err := NewGranularCounter[L](opts, init) if err != nil { panic(err) @@ -151,7 +157,7 @@ func (m *GranularCounter[L]) IsConstrained() bool { // Init implements CollectorWithInit. func (m *GranularCounter[L]) Init() { if m.initFunc != nil { - m.initFunc() + m.initFunc(m.metric) } } @@ -188,7 +194,7 @@ type Counter struct { // NewCounter creates a new Counter. // // See NewGranularCounter for usage notes. -func NewCounter(opts Opts, init func()) (*Counter, error) { +func NewCounter(opts Opts, init initCounterFunc) (*Counter, error) { metric, err := NewGranularCounter[NilLabels](opts, init) if err != nil { return nil, err @@ -198,7 +204,7 @@ func NewCounter(opts Opts, init func()) (*Counter, error) { // MustNewCounter is a convenience function that wraps NewCounter and panics on // error. -func MustNewCounter(opts Opts, init func()) *Counter { +func MustNewCounter(opts Opts, init initCounterFunc) *Counter { metric, err := NewCounter(opts, init) if err != nil { panic(err) diff --git a/pkg/metrics/gauge.go b/pkg/metrics/gauge.go index ce048063112..69dff54e9f2 100644 --- a/pkg/metrics/gauge.go +++ b/pkg/metrics/gauge.go @@ -7,6 +7,8 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +type initGaugeFunc func(*prometheus.GaugeVec) + // NewGaugeVecWithPod is a wrapper around prometheus.NewGaugeVec that also // registers the metric to be cleaned up when a pod is deleted. // @@ -35,14 +37,14 @@ func NewGaugeVecWithPodV2(opts prometheus.GaugeVecOpts) *prometheus.GaugeVec { type GranularGauge[L FilteredLabels] struct { metric *prometheus.GaugeVec constrained bool - initFunc func() + initFunc initGaugeFunc initForDocs func() } // NewGranularGauge creates a new GranularGauge. // // See NewGranularCounter for usage notes. -func NewGranularGauge[L FilteredLabels](opts Opts, init func()) (*GranularGauge[L], error) { +func NewGranularGauge[L FilteredLabels](opts Opts, init initGaugeFunc) (*GranularGauge[L], error) { labels, constrained, err := getVariableLabels[L](&opts) if err != nil { return nil, err @@ -70,9 +72,12 @@ func NewGranularGauge[L FilteredLabels](opts Opts, init func()) (*GranularGauge[ metric.WithLabelValues(lvs...).Set(0) } - // if metric is constrained, default to initializing all combinations of labels + // If metric is constrained, default to initializing all combinations of + // labels. Note that in such case the initialization function doesn't + // reference the wrapped metric passed as an argument because this metric + // is captured already in initMetric closure. if constrained && init == nil { - init = func() { + init = func(_ *prometheus.GaugeVec) { initAllCombinations(initMetric, opts.ConstrainedLabels) } } @@ -108,7 +113,7 @@ func MustNewGranularGauge[L FilteredLabels](promOpts prometheus.GaugeOpts, extra // MustNewGranularGaugeWithInit is a convenience function that wraps // NewGranularGauge and panics on error. -func MustNewGranularGaugeWithInit[L FilteredLabels](opts Opts, init func()) *GranularGauge[L] { +func MustNewGranularGaugeWithInit[L FilteredLabels](opts Opts, init initGaugeFunc) *GranularGauge[L] { metric, err := NewGranularGauge[L](opts, init) if err != nil { panic(err) @@ -134,7 +139,7 @@ func (m *GranularGauge[L]) IsConstrained() bool { // Init implements CollectorWithInit. func (m *GranularGauge[L]) Init() { if m.initFunc != nil { - m.initFunc() + m.initFunc(m.metric) } } @@ -171,7 +176,7 @@ type Gauge struct { // NewGauge creates a new Gauge. // // See NewGranularCounter for usage notes. -func NewGauge(opts Opts, init func()) (*Gauge, error) { +func NewGauge(opts Opts, init initGaugeFunc) (*Gauge, error) { metric, err := NewGranularGauge[NilLabels](opts, init) if err != nil { return nil, err @@ -181,7 +186,7 @@ func NewGauge(opts Opts, init func()) (*Gauge, error) { // MustNewGauge is a convenience function that wraps NewGauge and panics on // error. -func MustNewGauge(opts Opts, init func()) *Gauge { +func MustNewGauge(opts Opts, init initGaugeFunc) *Gauge { metric, err := NewGauge(opts, init) if err != nil { panic(err) diff --git a/pkg/metrics/granularmetric.go b/pkg/metrics/granularmetric.go index e0798270ea5..2d6eb9b39b5 100644 --- a/pkg/metrics/granularmetric.go +++ b/pkg/metrics/granularmetric.go @@ -3,11 +3,11 @@ package metrics -type initMetricFunc func(...string) +type initLabelValuesFunc func(...string) // initAllCombinations initializes a metric with all possible combinations of // label values. -func initAllCombinations(initMetric initMetricFunc, labels []ConstrainedLabel) { +func initAllCombinations(initMetric initLabelValuesFunc, labels []ConstrainedLabel) { initCombinations(initMetric, labels, make([]string, len(labels)), 0) } @@ -20,7 +20,7 @@ func initAllCombinations(initMetric initMetricFunc, labels []ConstrainedLabel) { // - cursor is in the range [0, len(labels)] // // If any of these is not met, the function will do nothing. -func initCombinations(initMetric initMetricFunc, labels []ConstrainedLabel, lvs []string, cursor int) { +func initCombinations(initMetric initLabelValuesFunc, labels []ConstrainedLabel, lvs []string, cursor int) { if initMetric == nil || len(labels) != len(lvs) || cursor < 0 || cursor > len(labels) { // The function was called with invalid arguments. Silently return. return @@ -43,7 +43,7 @@ func initCombinations(initMetric initMetricFunc, labels []ConstrainedLabel, lvs // metrics server - but here we care only about extracting labels for // documentation, so we don't try to make the metrics realistic. func initForDocs[L FilteredLabels]( - initMetric initMetricFunc, constrained []ConstrainedLabel, unconstrained []UnconstrainedLabel, + initMetric initLabelValuesFunc, constrained []ConstrainedLabel, unconstrained []UnconstrainedLabel, ) { var dummy L commonLabels := dummy.Keys() diff --git a/pkg/metrics/histogram.go b/pkg/metrics/histogram.go index c1ae38c2afd..00c0623eaed 100644 --- a/pkg/metrics/histogram.go +++ b/pkg/metrics/histogram.go @@ -7,6 +7,8 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +type initHistogramFunc func(*prometheus.HistogramVec) + // NewHistogramVecWithPod is a wrapper around prometheus.NewHistogramVec that also // registers the metric to be cleaned up when a pod is deleted. // @@ -35,14 +37,14 @@ func NewHistogramVecWithPodV2(opts prometheus.HistogramVecOpts) *prometheus.Hist type GranularHistogram[L FilteredLabels] struct { metric *prometheus.HistogramVec constrained bool - initFunc func() + initFunc initHistogramFunc initForDocs func() } // NewGranularHistogram creates a new GranularHistogram. // // See NewGranularCounter for usage notes. -func NewGranularHistogram[L FilteredLabels](opts HistogramOpts, init func()) (*GranularHistogram[L], error) { +func NewGranularHistogram[L FilteredLabels](opts HistogramOpts, init initHistogramFunc) (*GranularHistogram[L], error) { labels, constrained, err := getVariableLabels[L](&opts.Opts) if err != nil { return nil, err @@ -71,9 +73,12 @@ func NewGranularHistogram[L FilteredLabels](opts HistogramOpts, init func()) (*G metric.WithLabelValues(lvs...) } - // if metric is constrained, default to initializing all combinations of labels + // If metric is constrained, default to initializing all combinations of + // labels. Note that in such case the initialization function doesn't + // reference the wrapped metric passed as an argument because this metric + // is captured already in initMetric closure. if constrained && init == nil { - init = func() { + init = func(_ *prometheus.HistogramVec) { initAllCombinations(initMetric, opts.ConstrainedLabels) } } @@ -118,7 +123,7 @@ func MustNewGranularHistogram[L FilteredLabels](promOpts prometheus.HistogramOpt // MustNewGranularHistogramWithInit is a convenience function that wraps // NewGranularHistogram and panics on error. -func MustNewGranularHistogramWithInit[L FilteredLabels](opts HistogramOpts, init func()) *GranularHistogram[L] { +func MustNewGranularHistogramWithInit[L FilteredLabels](opts HistogramOpts, init initHistogramFunc) *GranularHistogram[L] { metric, err := NewGranularHistogram[L](opts, init) if err != nil { panic(err) @@ -144,7 +149,7 @@ func (m *GranularHistogram[L]) IsConstrained() bool { // Init implements CollectorWithInit. func (m *GranularHistogram[L]) Init() { if m.initFunc != nil { - m.initFunc() + m.initFunc(m.metric) } } @@ -181,7 +186,7 @@ type Histogram struct { // NewHistogram creates a new Histogram. // // See NewGranularCounter for usage notes. -func NewHistogram(opts HistogramOpts, init func()) (*Histogram, error) { +func NewHistogram(opts HistogramOpts, init initHistogramFunc) (*Histogram, error) { metric, err := NewGranularHistogram[NilLabels](opts, init) if err != nil { return nil, err @@ -191,7 +196,7 @@ func NewHistogram(opts HistogramOpts, init func()) (*Histogram, error) { // MustNewHistogram is a convenience function that wraps NewHistogram and panics on // error. -func MustNewHistogram(opts HistogramOpts, init func()) *Histogram { +func MustNewHistogram(opts HistogramOpts, init initHistogramFunc) *Histogram { metric, err := NewHistogram(opts, init) if err != nil { panic(err)