blob: c90e43a0f7533fd04d3a5d784afd97b80d516054 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Serge Bazanski6f599512023-04-26 19:08:19 +02004package scruffy
5
6import (
7 "github.com/prometheus/client_golang/prometheus"
8
9 "source.monogon.dev/go/algorithm/cartesian"
10)
11
12// A labelDefinition describes a key/value pair that's a metric dimension. It
13// consists of the label key/name (a string), and a list of possible values of
14// this key. The list of values will be used to initialize the metrics at startup
15// with zero values.
16//
17// The initialValues system is intended to be used with labels that are
18// low-cardinality enums, e.g. the name of a subsystem.
19//
20// All labelDefinitions for a single metric will then create a cartesian product
21// of all initialValues.
22type labelDefinition struct {
23 // name/key of the label.
24 name string
25 // initialValues defines the default values for this label key/name that will be
26 // used to generate a list of initial zero-filled metrics.
27 initialValues []string
28}
29
30// labelDefinitions is a list of labelDefinition which define the label
31// dimensions of a metric. All the initialValues of the respective
32// labelDefinitions will create a cartesian set of default zero-filled metric
33// values when the metric susbsystem gets initialized. These zero values will
34// then get overridden by real data as it is collected.
35type labelDefinitions []labelDefinition
36
37// initialLabels generates the list of initial labels key/values that should be
38// used to generate zero-filled metrics on startup. This is a cartesian product
39// of all initialValues of all labelDefinitions.
40func (l labelDefinitions) initialLabels() []prometheus.Labels {
41 // Nothing to do if this is an empty labelDefinitions.
42 if len(l) == 0 {
43 return nil
44 }
45
46 // Given:
47 //
48 // labelDefinitions := []labelDefinition{
49 // { name: "a", initialValues: []string{"foo", "bar"}},
50 // { name: "b", initialValues: []string{"baz", "barf"}},
51 // }
52 //
53 // This creates:
54 //
55 // values := []string{
56 // { "foo", "bar" }, // label 'a'
57 // { "baz", "barf" }, // label 'b'
58 // }
59 var values [][]string
60 for _, ld := range l {
61 values = append(values, ld.initialValues)
62 }
63
64 // Given the above:
65 //
66 // valuesProduct := []string{
67 // // a b
68 // { "foo", "baz" },
69 // { "foo", "barf" },
70 // { "bar", "baz" },
71 // { "bar", "barf" },
72 // }
73 valuesProduct := cartesian.Product[string](values...)
74
75 // This converts valuesProduct into an actual prometheus-compatible type,
76 // re-attaching the label names back into the columns as seen above.
77 var res []prometheus.Labels
78 for _, vp := range valuesProduct {
79 labels := make(prometheus.Labels)
80 for i, lv := range vp {
81 labelDef := l[i]
82 labels[labelDef.name] = lv
83 }
84 res = append(res, labels)
85 }
86 return res
87}
88
89// names returns the keys/names of all the metric labels from these
90// labelDefinitions.
91func (l labelDefinitions) names() []string {
92 var res []string
93 for _, ld := range l {
94 res = append(res, ld.name)
95 }
96 return res
97}