forked from cilium/tetragon
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Details of the goals and implementation of the metrics library are documented in pkg/metrics/doc.go and in the code doc comments. Fixes: cilium#2376 Signed-off-by: Anna Kapuscinska <anna@isovalent.com>
- Loading branch information
Showing
7 changed files
with
844 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
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) { | ||
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] { | ||
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) { | ||
return NewGranularCustomCounter[NilLabels](opts) | ||
} | ||
|
||
// MustNewCustomCounter is a convenience function that wraps NewCustomCounter | ||
// and panics on error. | ||
func MustNewCustomCounter(opts MetricOpts) GranularCustomMetric[NilLabels] { | ||
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) { | ||
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] { | ||
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) { | ||
return NewGranularCustomGauge[NilLabels](opts) | ||
} | ||
|
||
// MustNewCustomGauge is a convenience function that wraps NewCustomGauge | ||
// and panics on error. | ||
func MustNewCustomGauge(opts MetricOpts) GranularCustomMetric[NilLabels] { | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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} | ||
} |
Oops, something went wrong.