diff --git a/pkg/metrics/counter.go b/pkg/metrics/counter.go new file mode 100644 index 00000000000..c2343205cf8 --- /dev/null +++ b/pkg/metrics/counter.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// NewCounterVecWithPod is a wrapper around prometheus.NewCounterVec that also registers the metric +// to be cleaned up when a pod is deleted. It should be used only to register metrics that have +// "pod" and "namespace" labels. +func NewCounterVecWithPod(opts prometheus.CounterOpts, labels []string) *prometheus.CounterVec { + metric := prometheus.NewCounterVec(opts, labels) + metricsWithPodMutex.Lock() + metricsWithPod = append(metricsWithPod, metric.MetricVec) + metricsWithPodMutex.Unlock() + return metric +} + +type GranularCounter[L FilteredLabels] struct { + metric *prometheus.CounterVec +} + +func NewGranularCounter[L FilteredLabels](opts prometheus.CounterOpts, extraLabels []string) (*GranularCounter[L], error) { + var dummy L + commonLabels := dummy.Keys() + err := validateExtraLabels(commonLabels, extraLabels) + if err != nil { + return nil, err + } + return &GranularCounter[L]{ + // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and + // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the + // metric will still work, just the unnecessary cleanup logic will add some overhead. + metric: NewCounterVecWithPod(opts, append(commonLabels, extraLabels...)), + }, nil +} + +func MustNewGranularCounter[L FilteredLabels](opts prometheus.CounterOpts, extraLabels []string) *GranularCounter[L] { + result, err := NewGranularCounter[L](opts, extraLabels) + if err != nil { + panic(err) + } + return result +} + +func (m *GranularCounter[L]) Describe(ch chan<- *prometheus.Desc) { + m.metric.Describe(ch) +} + +func (m *GranularCounter[L]) Collect(ch chan<- prometheus.Metric) { + m.metric.Collect(ch) +} + +func (m *GranularCounter[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Counter { + lvs := append((*commonLvs).Values(), extraLvs...) + return m.metric.WithLabelValues(lvs...) +} diff --git a/pkg/metrics/labels.go b/pkg/metrics/filteredlabels.go similarity index 100% rename from pkg/metrics/labels.go rename to pkg/metrics/filteredlabels.go diff --git a/pkg/metrics/labels_test.go b/pkg/metrics/filteredlabels_test.go similarity index 100% rename from pkg/metrics/labels_test.go rename to pkg/metrics/filteredlabels_test.go diff --git a/pkg/metrics/gauge.go b/pkg/metrics/gauge.go new file mode 100644 index 00000000000..6650148e045 --- /dev/null +++ b/pkg/metrics/gauge.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// NewGaugeVecWithPod is a wrapper around prometheus.NewGaugeVec that also registers the metric +// to be cleaned up when a pod is deleted. It should be used only to register metrics that have +// "pod" and "namespace" labels. +func NewGaugeVecWithPod(opts prometheus.GaugeOpts, labels []string) *prometheus.GaugeVec { + metric := prometheus.NewGaugeVec(opts, labels) + metricsWithPodMutex.Lock() + metricsWithPod = append(metricsWithPod, metric.MetricVec) + metricsWithPodMutex.Unlock() + return metric +} + +type GranularGauge[L FilteredLabels] struct { + metric *prometheus.GaugeVec +} + +func NewGranularGauge[L FilteredLabels](opts prometheus.GaugeOpts, extraLabels []string) (*GranularGauge[L], error) { + var dummy L + commonLabels := dummy.Keys() + err := validateExtraLabels(commonLabels, extraLabels) + if err != nil { + return nil, err + } + return &GranularGauge[L]{ + // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and + // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the + // metric will still work, just the unnecessary cleanup logic will add some overhead. + metric: NewGaugeVecWithPod(opts, append(commonLabels, extraLabels...)), + }, nil +} + +func MustNewGranularGauge[L FilteredLabels](opts prometheus.GaugeOpts, extraLabels []string) *GranularGauge[L] { + result, err := NewGranularGauge[L](opts, extraLabels) + if err != nil { + panic(err) + } + return result +} + +func (m *GranularGauge[L]) Describe(ch chan<- *prometheus.Desc) { + m.metric.Describe(ch) +} + +func (m *GranularGauge[L]) Collect(ch chan<- prometheus.Metric) { + m.metric.Collect(ch) +} + +func (m *GranularGauge[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Gauge { + lvs := append((*commonLvs).Values(), extraLvs...) + return m.metric.WithLabelValues(lvs...) +} diff --git a/pkg/metrics/granularmetric.go b/pkg/metrics/granularmetric.go index 1d14f8baed1..01a1aa4894c 100644 --- a/pkg/metrics/granularmetric.go +++ b/pkg/metrics/granularmetric.go @@ -6,8 +6,6 @@ package metrics import ( "fmt" "slices" - - "github.com/prometheus/client_golang/prometheus" ) func validateExtraLabels(common []string, extra []string) error { @@ -18,129 +16,3 @@ func validateExtraLabels(common []string, extra []string) error { } return nil } - -// counter - -type GranularCounter[L FilteredLabels] struct { - metric *prometheus.CounterVec -} - -func NewGranularCounter[L FilteredLabels](opts prometheus.CounterOpts, extraLabels []string) (*GranularCounter[L], error) { - var dummy L - commonLabels := dummy.Keys() - err := validateExtraLabels(commonLabels, extraLabels) - if err != nil { - return nil, err - } - return &GranularCounter[L]{ - // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and - // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the - // metric will still work, just the unnecessary cleanup logic will add some overhead. - metric: NewCounterVecWithPod(opts, append(commonLabels, extraLabels...)), - }, nil -} - -func MustNewGranularCounter[L FilteredLabels](opts prometheus.CounterOpts, extraLabels []string) *GranularCounter[L] { - result, err := NewGranularCounter[L](opts, extraLabels) - if err != nil { - panic(err) - } - return result -} - -func (m *GranularCounter[L]) Describe(ch chan<- *prometheus.Desc) { - m.metric.Describe(ch) -} - -func (m *GranularCounter[L]) Collect(ch chan<- prometheus.Metric) { - m.metric.Collect(ch) -} - -func (m *GranularCounter[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Counter { - lvs := append((*commonLvs).Values(), extraLvs...) - return m.metric.WithLabelValues(lvs...) -} - -// gauge - -type GranularGauge[L FilteredLabels] struct { - metric *prometheus.GaugeVec -} - -func NewGranularGauge[L FilteredLabels](opts prometheus.GaugeOpts, extraLabels []string) (*GranularGauge[L], error) { - var dummy L - commonLabels := dummy.Keys() - err := validateExtraLabels(commonLabels, extraLabels) - if err != nil { - return nil, err - } - return &GranularGauge[L]{ - // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and - // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the - // metric will still work, just the unnecessary cleanup logic will add some overhead. - metric: NewGaugeVecWithPod(opts, append(commonLabels, extraLabels...)), - }, nil -} - -func MustNewGranularGauge[L FilteredLabels](opts prometheus.GaugeOpts, extraLabels []string) *GranularGauge[L] { - result, err := NewGranularGauge[L](opts, extraLabels) - if err != nil { - panic(err) - } - return result -} - -func (m *GranularGauge[L]) Describe(ch chan<- *prometheus.Desc) { - m.metric.Describe(ch) -} - -func (m *GranularGauge[L]) Collect(ch chan<- prometheus.Metric) { - m.metric.Collect(ch) -} - -func (m *GranularGauge[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Gauge { - lvs := append((*commonLvs).Values(), extraLvs...) - return m.metric.WithLabelValues(lvs...) -} - -// histogram - -type GranularHistogram[L FilteredLabels] struct { - metric *prometheus.HistogramVec -} - -func NewGranularHistogram[L FilteredLabels](opts prometheus.HistogramOpts, extraLabels []string) (*GranularHistogram[L], error) { - var dummy L - commonLabels := dummy.Keys() - err := validateExtraLabels(commonLabels, extraLabels) - if err != nil { - return nil, err - } - return &GranularHistogram[L]{ - // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and - // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the - // metric will still work, just the unnecessary cleanup logic will add some overhead. - metric: NewHistogramVecWithPod(opts, append(commonLabels, extraLabels...)), - }, nil -} - -func MustNewGranularHistogram[L FilteredLabels](opts prometheus.HistogramOpts, extraLabels []string) *GranularHistogram[L] { - result, err := NewGranularHistogram[L](opts, extraLabels) - if err != nil { - panic(err) - } - return result -} - -func (m *GranularHistogram[L]) Describe(ch chan<- *prometheus.Desc) { - m.metric.Describe(ch) -} - -func (m *GranularHistogram[L]) Collect(ch chan<- prometheus.Metric) { - m.metric.Collect(ch) -} - -func (m *GranularHistogram[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Observer { - lvs := append((*commonLvs).Values(), extraLvs...) - return m.metric.WithLabelValues(lvs...) -} diff --git a/pkg/metrics/histogram.go b/pkg/metrics/histogram.go new file mode 100644 index 00000000000..b74c93b83d7 --- /dev/null +++ b/pkg/metrics/histogram.go @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Tetragon + +package metrics + +import ( + "github.com/prometheus/client_golang/prometheus" +) + +// NewHistogramVecWithPod is a wrapper around prometheus.NewHistogramVec that also registers the metric +// to be cleaned up when a pod is deleted. It should be used only to register metrics that have +// "pod" and "namespace" labels. +func NewHistogramVecWithPod(opts prometheus.HistogramOpts, labels []string) *prometheus.HistogramVec { + metric := prometheus.NewHistogramVec(opts, labels) + metricsWithPodMutex.Lock() + metricsWithPod = append(metricsWithPod, metric.MetricVec) + metricsWithPodMutex.Unlock() + return metric +} + +type GranularHistogram[L FilteredLabels] struct { + metric *prometheus.HistogramVec +} + +func NewGranularHistogram[L FilteredLabels](opts prometheus.HistogramOpts, extraLabels []string) (*GranularHistogram[L], error) { + var dummy L + commonLabels := dummy.Keys() + err := validateExtraLabels(commonLabels, extraLabels) + if err != nil { + return nil, err + } + return &GranularHistogram[L]{ + // NB: Using the WithPod wrapper means an implicit assumption that the metric has "pod" and + // "namespace" labels, and will be cleaned up on pod deletion. If this is not the case, the + // metric will still work, just the unnecessary cleanup logic will add some overhead. + metric: NewHistogramVecWithPod(opts, append(commonLabels, extraLabels...)), + }, nil +} + +func MustNewGranularHistogram[L FilteredLabels](opts prometheus.HistogramOpts, extraLabels []string) *GranularHistogram[L] { + result, err := NewGranularHistogram[L](opts, extraLabels) + if err != nil { + panic(err) + } + return result +} + +func (m *GranularHistogram[L]) Describe(ch chan<- *prometheus.Desc) { + m.metric.Describe(ch) +} + +func (m *GranularHistogram[L]) Collect(ch chan<- prometheus.Metric) { + m.metric.Collect(ch) +} + +func (m *GranularHistogram[L]) WithLabelValues(commonLvs *L, extraLvs ...string) prometheus.Observer { + lvs := append((*commonLvs).Values(), extraLvs...) + return m.metric.WithLabelValues(lvs...) +} diff --git a/pkg/metrics/metricwithpod.go b/pkg/metrics/metricwithpod.go index 9e8b695e096..7d50a66efa7 100644 --- a/pkg/metrics/metricwithpod.go +++ b/pkg/metrics/metricwithpod.go @@ -23,39 +23,6 @@ var ( deleteDelay = 1 * time.Minute ) -// NewCounterVecWithPod is a wrapper around prometheus.NewCounterVec that also registers the metric -// to be cleaned up when a pod is deleted. It should be used only to register metrics that have -// "pod" and "namespace" labels. -func NewCounterVecWithPod(opts prometheus.CounterOpts, labels []string) *prometheus.CounterVec { - metric := prometheus.NewCounterVec(opts, labels) - metricsWithPodMutex.Lock() - metricsWithPod = append(metricsWithPod, metric.MetricVec) - metricsWithPodMutex.Unlock() - return metric -} - -// NewGaugeVecWithPod is a wrapper around prometheus.NewGaugeVec that also registers the metric -// to be cleaned up when a pod is deleted. It should be used only to register metrics that have -// "pod" and "namespace" labels. -func NewGaugeVecWithPod(opts prometheus.GaugeOpts, labels []string) *prometheus.GaugeVec { - metric := prometheus.NewGaugeVec(opts, labels) - metricsWithPodMutex.Lock() - metricsWithPod = append(metricsWithPod, metric.MetricVec) - metricsWithPodMutex.Unlock() - return metric -} - -// NewHistogramVecWithPod is a wrapper around prometheus.NewHistogramVec that also registers the metric -// to be cleaned up when a pod is deleted. It should be used only to register metrics that have -// "pod" and "namespace" labels. -func NewHistogramVecWithPod(opts prometheus.HistogramOpts, labels []string) *prometheus.HistogramVec { - metric := prometheus.NewHistogramVec(opts, labels) - metricsWithPodMutex.Lock() - metricsWithPod = append(metricsWithPod, metric.MetricVec) - metricsWithPodMutex.Unlock() - return metric -} - // RegisterPodDeleteHandler registers handler for deleting metrics associated // with deleted pods. Without it, Tetragon kept exposing stale metrics for // deleted pods. This was causing continuous increase in memory usage in