Skip to content

Commit

Permalink
metrics: Reorganize code
Browse files Browse the repository at this point in the history
This commit is a preparation for the further extension of pkg/metrics. There
are no functional changes, only moving code around:
- moved helpers for creating metrics from metricwithpod.go and
  granularmetric.go to per-type files: counter.go, gauge.go and histogram.go
- renamed labels.go to filteredlabels.go

Signed-off-by: Anna Kapuscinska <[email protected]>
  • Loading branch information
lambdanis committed Jul 19, 2024
1 parent 057d734 commit d660c6c
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 161 deletions.
59 changes: 59 additions & 0 deletions pkg/metrics/counter.go
Original file line number Diff line number Diff line change
@@ -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...)
}
File renamed without changes.
File renamed without changes.
59 changes: 59 additions & 0 deletions pkg/metrics/gauge.go
Original file line number Diff line number Diff line change
@@ -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...)
}
128 changes: 0 additions & 128 deletions pkg/metrics/granularmetric.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ package metrics
import (
"fmt"
"slices"

"github.com/prometheus/client_golang/prometheus"
)

func validateExtraLabels(common []string, extra []string) error {
Expand All @@ -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...)
}
59 changes: 59 additions & 0 deletions pkg/metrics/histogram.go
Original file line number Diff line number Diff line change
@@ -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...)
}
33 changes: 0 additions & 33 deletions pkg/metrics/metricwithpod.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit d660c6c

Please sign in to comment.