| Tim Windelschmidt | 6d33a43 | 2025-02-04 14:34:25 +0100 | [diff] [blame] | 1 | // Copyright The Monogon Project Authors. |
| 2 | // SPDX-License-Identifier: Apache-2.0 |
| 3 | |
| Serge Bazanski | 6f59951 | 2023-04-26 19:08:19 +0200 | [diff] [blame] | 4 | package scruffy |
| 5 | |
| 6 | import ( |
| 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. |
| 22 | type 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. |
| 35 | type 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. |
| 40 | func (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. |
| 91 | func (l labelDefinitions) names() []string { |
| 92 | var res []string |
| 93 | for _, ld := range l { |
| 94 | res = append(res, ld.name) |
| 95 | } |
| 96 | return res |
| 97 | } |