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