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 <[email protected]>
- Loading branch information
Showing
11 changed files
with
1,083 additions
and
87 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,154 @@ | ||
// 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 entirely up to the | ||
// collector implementer to guarantee the metrics correctness, including | ||
// enforcing the labels constraints. | ||
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 := getVariableLabelNames[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,63 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Tetragon | ||
|
||
// The metrics package provides a set of helpers (wrappers around | ||
// [prometheus Go library](https://pkg.go.dev/github.com/prometheus/client_golang/prometheus)) | ||
// for defining and managing prometheus metrics. | ||
// | ||
// The package is designed to support the following functionality: | ||
// - Group metrics based on their purpose and load groups independently. | ||
// This gives us more control over what metrics are exposed and how | ||
// cardinality is managed. | ||
// - Define custom collectors, e.g. reading metrics directly from BPF maps. | ||
// This decouples metrics from events passed through ringbuffer. | ||
// - Let users configure high-cardinality dynamic labels, for both "regular" | ||
// metrics and custom collectors. | ||
// - Constrain metrics cardinality for metrics with known labels. | ||
// - Initialize metrics with known labels on startup. | ||
// This makes resources usage more predictable, as cardinality of these | ||
// metrics won't grow. | ||
// - Autogenerate reference documentation from metrics help texts. | ||
// - Delete stale metrics. This will prevent growing cardinality. | ||
// Currently we do it when a pod is deleted, but it should be easy to | ||
// extend this to other cases. | ||
// - Keep common labels consistent between metrics. | ||
// This makes it easier to write queries. | ||
// | ||
// Here we describe the key parts of the metrics package. See also doc comments | ||
// in the code for more details. | ||
// | ||
// `Group` interface and `metricsGroup` struct implementing it are | ||
// wrappers around `prometheus.Registry` intended to define sub-registries of | ||
// the root registry. In addition to registering metrics, it supports: | ||
// - initializing metrics on startup | ||
// - initializing metrics for generating docs | ||
// - constraining metrics cardinality (constrained group contains only | ||
// metrics with constrained cardinality) | ||
// | ||
// `MetricOpts` struct is a wrapper around `prometheus.Opts` that additionally | ||
// supports defining constrained and unconstrained labels. | ||
// | ||
// `ConstrainedLabel` and `UnconstrainedLabel` structs represent metric labels. | ||
// | ||
// `FilteredLabels` interface represents configurable labels, passed to metrics | ||
// via type parameter. The values are always unconstrained. | ||
// | ||
// `GranularCounter[L FilteredLabels]` (and analogous Gauge and Histogram) | ||
// struct is a wrapper around `prometheus.CounterVec` (Gauge, Histogram) with | ||
// additional properties: | ||
// - cardinality can be constrained (meaning all label values are known) | ||
// - support for configurable labels | ||
// - metric is initialized at startup for known label values | ||
// - metric is automatically included in generated docs | ||
// | ||
// `customCollector[L FilteredLabels]` struct represents a custom collector | ||
// (e.g. reading metrics directly from a BPF map). It contains a list of | ||
// metrics, collect function and an optional separate collect function for | ||
// generating docs. | ||
// | ||
// `GranularCustomMetric[L FilteredLabels]` (and analogous Gauge) interface | ||
// represents a custom metric (e.g. read directly form a BPF map). Similarly | ||
// like "regular" metrics tracked by prometheus library, it supports | ||
// constraining cardinality and configurable labels. | ||
package metrics |
Oops, something went wrong.