blob: b88f7995cbc5be95142bff6fc396304986142c76 [file] [log] [blame] [edit]
package scruffy
import (
"github.com/prometheus/client_golang/prometheus"
"source.monogon.dev/go/algorithm/cartesian"
)
// A labelDefinition describes a key/value pair that's a metric dimension. It
// consists of the label key/name (a string), and a list of possible values of
// this key. The list of values will be used to initialize the metrics at startup
// with zero values.
//
// The initialValues system is intended to be used with labels that are
// low-cardinality enums, e.g. the name of a subsystem.
//
// All labelDefinitions for a single metric will then create a cartesian product
// of all initialValues.
type labelDefinition struct {
// name/key of the label.
name string
// initialValues defines the default values for this label key/name that will be
// used to generate a list of initial zero-filled metrics.
initialValues []string
}
// labelDefinitions is a list of labelDefinition which define the label
// dimensions of a metric. All the initialValues of the respective
// labelDefinitions will create a cartesian set of default zero-filled metric
// values when the metric susbsystem gets initialized. These zero values will
// then get overridden by real data as it is collected.
type labelDefinitions []labelDefinition
// initialLabels generates the list of initial labels key/values that should be
// used to generate zero-filled metrics on startup. This is a cartesian product
// of all initialValues of all labelDefinitions.
func (l labelDefinitions) initialLabels() []prometheus.Labels {
// Nothing to do if this is an empty labelDefinitions.
if len(l) == 0 {
return nil
}
// Given:
//
// labelDefinitions := []labelDefinition{
// { name: "a", initialValues: []string{"foo", "bar"}},
// { name: "b", initialValues: []string{"baz", "barf"}},
// }
//
// This creates:
//
// values := []string{
// { "foo", "bar" }, // label 'a'
// { "baz", "barf" }, // label 'b'
// }
var values [][]string
for _, ld := range l {
values = append(values, ld.initialValues)
}
// Given the above:
//
// valuesProduct := []string{
// // a b
// { "foo", "baz" },
// { "foo", "barf" },
// { "bar", "baz" },
// { "bar", "barf" },
// }
valuesProduct := cartesian.Product[string](values...)
// This converts valuesProduct into an actual prometheus-compatible type,
// re-attaching the label names back into the columns as seen above.
var res []prometheus.Labels
for _, vp := range valuesProduct {
labels := make(prometheus.Labels)
for i, lv := range vp {
labelDef := l[i]
labels[labelDef.name] = lv
}
res = append(res, labels)
}
return res
}
// names returns the keys/names of all the metric labels from these
// labelDefinitions.
func (l labelDefinitions) names() []string {
var res []string
for _, ld := range l {
res = append(res, ld.name)
}
return res
}