-
Notifications
You must be signed in to change notification settings - Fork 362
Commit
Details of the goals and implementation of the metrics library are documented in pkg/metrics/doc.go and in the code doc comments. Fixes: #2376 Signed-off-by: Anna Kapuscinska <[email protected]>
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package metrics | ||
|
||
import ( | ||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
type collectFunc func(chan<- prometheus.Metric) | ||
|
||
type customCollector[L FilteredLabels] struct { | ||
metrics []GranularCustomMetric[L] | ||
collectFunc collectFunc | ||
collectForDocsFunc collectFunc | ||
} | ||
|
||
// NewCustomCollector creates a new customCollector. | ||
// | ||
// If collectForDocs is nil, the collector will use collect function for both | ||
// regular metrics server and generating documentation. | ||
func NewCustomCollector[L FilteredLabels]( | ||
metrics []GranularCustomMetric[L], collect collectFunc, collectForDocs collectFunc, | ||
) CollectorWithInit { | ||
return &customCollector[L]{ | ||
metrics: metrics, | ||
collectFunc: collect, | ||
collectForDocsFunc: collectForDocs, | ||
} | ||
} | ||
|
||
// Describe implements CollectorWithInit (prometheus.Collector). | ||
func (c *customCollector[L]) Describe(ch chan<- *prometheus.Desc) { | ||
for _, m := range c.metrics { | ||
ch <- m.Desc() | ||
} | ||
} | ||
|
||
// Collect implements CollectorWithInit (prometheus.Collector). | ||
func (c *customCollector[L]) Collect(ch chan<- prometheus.Metric) { | ||
if c.collectFunc != nil { | ||
c.collectFunc(ch) | ||
} | ||
} | ||
|
||
// IsConstrained implements CollectorWithInit. | ||
func (c *customCollector[L]) IsConstrained() bool { | ||
for _, m := range c.metrics { | ||
if !m.IsConstrained() { | ||
return false | ||
} | ||
} | ||
return true | ||
} | ||
|
||
// Init implements CollectorWithInit. | ||
func (c *customCollector[L]) Init() { | ||
// since metrics are collected independently, there's nothing to initialize | ||
} | ||
|
||
// InitForDocs implements CollectorWithInit. | ||
func (c *customCollector[L]) InitForDocs() { | ||
// override Collect method if there's a separate one for docs | ||
if c.collectForDocsFunc != nil { | ||
c.collectFunc = c.collectForDocsFunc | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package metrics | ||
|
||
import ( | ||
"github.com/prometheus/client_golang/prometheus" | ||
) | ||
|
||
// GranularCustomMetric represents a metric collected independently of | ||
// prometheus package, for example in a BPF map. It's intended to be used in | ||
// a custom collector (see customcollector.go). The interface doesn't provide | ||
// any validation, so it's up to the collector implementer to guarantee the | ||
// metrics consistency. | ||
type GranularCustomMetric[L FilteredLabels] interface { | ||
Desc() *prometheus.Desc | ||
MustMetric(value float64, commonLvs *L, extraLvs ...string) prometheus.Metric | ||
IsConstrained() bool | ||
} | ||
|
||
// getDesc is a helper function to retrieve the descriptor for a metric and | ||
// check if the metric is constrained. | ||
// | ||
// See getVariableLabels for the labels order. | ||
func getDesc[L FilteredLabels](opts *MetricOpts) (*prometheus.Desc, bool, error) { | ||
Check failure on line 25 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 25 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
labels, constrained, err := getVariableLabels[L](opts) | ||
if err != nil { | ||
return nil, false, err | ||
} | ||
|
||
desc := prometheus.NewDesc( | ||
prometheus.BuildFQName(opts.Namespace, opts.Subsystem, opts.Name), | ||
opts.Help, | ||
labels, | ||
opts.ConstLabels, | ||
) | ||
return desc, constrained, nil | ||
} | ||
|
||
// counter | ||
|
||
type granularCustomCounter[L FilteredLabels] struct { | ||
desc *prometheus.Desc | ||
constrained bool | ||
} | ||
|
||
// NewGranularCustomCounter creates a new granularCustomCounter. | ||
func NewGranularCustomCounter[L FilteredLabels](opts MetricOpts) (GranularCustomMetric[L], error) { | ||
Check failure on line 48 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 48 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
desc, constrained, err := getDesc[L](&opts) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &granularCustomCounter[L]{ | ||
desc: desc, | ||
constrained: constrained, | ||
}, nil | ||
} | ||
|
||
// MustNewGranularCustomCounter is a convenience function that wraps | ||
// NewGranularCustomCounter and panics on error. | ||
func MustNewGranularCustomCounter[L FilteredLabels](opts MetricOpts) GranularCustomMetric[L] { | ||
Check failure on line 62 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 62 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
m, err := NewGranularCustomCounter[L](opts) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return m | ||
} | ||
|
||
// NewCustomCounter creates a new granularCustomCounter with no configurable labels. | ||
func NewCustomCounter(opts MetricOpts) (GranularCustomMetric[NilLabels], error) { | ||
Check failure on line 71 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 71 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
return NewGranularCustomCounter[NilLabels](opts) | ||
} | ||
|
||
// MustNewCustomCounter is a convenience function that wraps NewCustomCounter | ||
// and panics on error. | ||
func MustNewCustomCounter(opts MetricOpts) GranularCustomMetric[NilLabels] { | ||
Check failure on line 77 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 77 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
return MustNewGranularCustomCounter[NilLabels](opts) | ||
} | ||
|
||
// Desc implements GranularCustomMetric. | ||
func (m *granularCustomCounter[L]) Desc() *prometheus.Desc { | ||
return m.desc | ||
} | ||
|
||
// MustMetric implements GranularCustomMetric. | ||
func (m *granularCustomCounter[L]) MustMetric(value float64, commonLvs *L, extraLvs ...string) prometheus.Metric { | ||
lvs := append((*commonLvs).Values(), extraLvs...) | ||
return prometheus.MustNewConstMetric(m.desc, prometheus.CounterValue, value, lvs...) | ||
} | ||
|
||
// IsConstrained implements GranularCustomMetric. | ||
func (m *granularCustomCounter[L]) IsConstrained() bool { | ||
return m.constrained | ||
} | ||
|
||
// gauge | ||
|
||
type granularCustomGauge[L FilteredLabels] struct { | ||
desc *prometheus.Desc | ||
constrained bool | ||
} | ||
|
||
// NewGranularCustomGauge creates a new granularCustomGauge. | ||
func NewGranularCustomGauge[L FilteredLabels](opts MetricOpts) (GranularCustomMetric[L], error) { | ||
Check failure on line 105 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 105 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
desc, constrained, err := getDesc[L](&opts) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &granularCustomGauge[L]{ | ||
desc: desc, | ||
constrained: constrained, | ||
}, nil | ||
} | ||
|
||
// MustNewGranularCustomGauge is a convenience function that wraps | ||
// NewGranularCustomGauge and panics on error. | ||
func MustNewGranularCustomGauge[L FilteredLabels](opts MetricOpts) GranularCustomMetric[L] { | ||
Check failure on line 119 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 119 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
m, err := NewGranularCustomGauge[L](opts) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return m | ||
} | ||
|
||
// NewCustomGauge creates a new granularCustomGauge with no configurable labels. | ||
func NewCustomGauge(opts MetricOpts) (GranularCustomMetric[NilLabels], error) { | ||
Check failure on line 128 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 128 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
return NewGranularCustomGauge[NilLabels](opts) | ||
} | ||
|
||
// MustNewCustomGauge is a convenience function that wraps NewCustomGauge | ||
// and panics on error. | ||
func MustNewCustomGauge(opts MetricOpts) GranularCustomMetric[NilLabels] { | ||
Check failure on line 134 in pkg/metrics/custommetric.go GitHub Actions / golangci-lint
Check failure on line 134 in pkg/metrics/custommetric.go GitHub Actions / generated-files
|
||
return MustNewGranularCustomGauge[NilLabels](opts) | ||
} | ||
|
||
// Desc implements GranularCustomMetric. | ||
func (m *granularCustomGauge[L]) Desc() *prometheus.Desc { | ||
return m.desc | ||
} | ||
|
||
// MustMetric implements GranularCustomMetric. | ||
func (m *granularCustomGauge[L]) MustMetric(value float64, commonLvs *L, extraLvs ...string) prometheus.Metric { | ||
lvs := append((*commonLvs).Values(), extraLvs...) | ||
return prometheus.MustNewConstMetric(m.desc, prometheus.GaugeValue, value, lvs...) | ||
} | ||
|
||
// IsConstrained implements GranularCustomMetric. | ||
func (m *granularCustomGauge[L]) IsConstrained() bool { | ||
return m.constrained | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
package metrics | ||
|
||
import ( | ||
"github.com/cilium/tetragon/pkg/metrics/consts" | ||
) | ||
|
||
type FilteredLabels interface { | ||
Keys() []string | ||
Values() []string | ||
} | ||
|
||
// FilteredLabelsWithExamples extends FilteredLabels with a method returning | ||
// example label values, intended to be used when generating documentation. | ||
type FilteredLabelsWithExamples interface { | ||
FilteredLabels | ||
ExampleValues() []string | ||
} | ||
|
||
type NilLabels struct{} | ||
|
||
func (l NilLabels) Keys() []string { return []string{} } | ||
|
||
func (l NilLabels) Values() []string { return []string{} } | ||
|
||
func (l NilLabels) ExampleValues() []string { return []string{} } | ||
|
||
type ProcessLabels struct { | ||
Namespace string | ||
Workload string | ||
Pod string | ||
Binary string | ||
} | ||
|
||
func NewProcessLabels(namespace, workload, pod, binary string) *ProcessLabels { | ||
return &ProcessLabels{ | ||
Namespace: namespace, | ||
Workload: workload, | ||
Pod: pod, | ||
Binary: binary, | ||
} | ||
} | ||
|
||
func (l ProcessLabels) Keys() []string { | ||
return []string{"namespace", "workload", "pod", "binary"} | ||
} | ||
|
||
func (l ProcessLabels) Values() []string { | ||
return []string{l.Namespace, l.Workload, l.Pod, l.Binary} | ||
} | ||
|
||
func (l ProcessLabels) ExampleValues() []string { | ||
return []string{consts.ExampleNamespace, consts.ExampleWorkload, consts.ExamplePod, consts.ExampleBinary} | ||
} |