github.com/puellanivis/breton@v0.2.16/lib/metrics/label.go (about) 1 package metrics 2 3 import ( 4 "fmt" 5 "regexp" 6 "sort" 7 8 "github.com/puellanivis/breton/lib/metrics/internal/kv" 9 ) 10 11 // cannot use `const LabelVar = Label("asdf")` if Label is a function that 12 // tests at compile-time. So, we will check at Registration time. 13 var validLabel = regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`) 14 15 // A Labeler returns the name=value pairting from the implementer. 16 type Labeler interface { 17 Label() (name, value string) 18 } 19 20 // Label describes only a label name, in a way that allows it to be set to a const. 21 type Label string 22 23 // Label implements Labeler. It returns an empty string for value. 24 func (l Label) Label() (name, value string) { 25 return string(l), "" 26 } 27 28 // WithValue takes the given Label, and attaches a value to it. 29 func (l Label) WithValue(value string) Labeler { 30 return LabelValue{ 31 key: string(l), 32 val: value, 33 } 34 } 35 36 // Const returns a Labeler that defines a label with a constant value. 37 // The concrete type is unexported in order to further enforce immutability. 38 func (l Label) Const(value string) Labeler { 39 return constLabel{ 40 key: string(l), 41 val: value, 42 } 43 } 44 45 // A LabelValue describes a complete pair of Label name and value. 46 type LabelValue struct { 47 key, val string 48 } 49 50 // Label returns the name and value of the LabelValue. 51 func (l LabelValue) Label() (name, value string) { 52 return l.key, l.val 53 } 54 55 // WithValue returns a new Labeler with the same Label name, but a new value. 56 func (l LabelValue) WithValue(value string) Labeler { 57 return LabelValue{ 58 key: l.key, 59 val: value, 60 } 61 } 62 63 type constLabel LabelValue 64 65 func (l constLabel) Label() (name, value string) { 66 return l.key, l.val 67 } 68 69 // labelSet describes a set of labels, i.e. which keys are valid, and whether they may be set. 70 type labelSet struct { 71 keys []string 72 canSet map[string]bool 73 } 74 75 func newLabelSet(labels []Labeler) *labelSet { 76 s := &labelSet{ 77 canSet: make(map[string]bool), 78 } 79 80 for _, label := range labels { 81 k, _ := label.Label() 82 83 if !validLabel.MatchString(k) { 84 panic(fmt.Sprintf("label name %q is invalid", k)) 85 } 86 87 if _, ok := s.canSet[k]; ok { 88 panic(fmt.Sprintf("label %q redefined", k)) 89 } 90 91 // Let’s assume it can be set 92 s.canSet[k] = true 93 94 if _, ok := label.(constLabel); ok { 95 // Well, ok then, it is a constant. 96 s.canSet[k] = false 97 } 98 } 99 100 s.keys = nil // fastest way to empty the list 101 for key := range s.canSet { 102 s.keys = append(s.keys, key) 103 } 104 sort.Strings(s.keys) 105 106 return s 107 } 108 109 // labelScope allows for the scoping of labels, meaning successive levels 110 // of labels may be applied one after another. 111 type labelScope struct { 112 set *labelSet // keep track of the labelSet, for canSet testing 113 p *labelScope // keep track of the parent of this scope 114 115 kv kv.KeyVal // the key:val set defined at this scope. 116 } 117 118 // defineLabels takes a list of Labelers, constructs an appropriate labelSet, 119 // and then returns a labelScope containing the Constant and Default label values. 120 func defineLabels(labels ...Labeler) *labelScope { 121 l := &labelScope{ 122 set: newLabelSet(labels), 123 } 124 125 for _, label := range labels { 126 if k, v := label.Label(); v != "" { 127 l.kv.Append(k, v) 128 } 129 } 130 sort.Sort(l.kv) 131 132 return l 133 } 134 135 // With returns a child labelScope that has the given Labelers additionally set. 136 func (l *labelScope) With(labels ...Labeler) *labelScope { 137 if l == nil { 138 panic("metric does not define any labels") 139 } 140 141 n := &labelScope{ 142 set: l.set, 143 p: l, 144 } 145 146 for _, label := range labels { 147 k, v := label.Label() 148 149 canSet, ok := n.set.canSet[k] 150 if !ok { 151 panic(fmt.Sprintf("attempt to assign to undefined label %q", k)) 152 } 153 154 if !canSet { 155 panic(fmt.Sprintf("attempt to assign to constant label %q", k)) 156 } 157 158 n.kv.Append(k, v) 159 } 160 sort.Sort(n.kv) 161 162 return n 163 } 164 165 // get returns the most-recently-set value for a given Label name. 166 func (l *labelScope) get(name string) Labeler { 167 if i, ok := l.kv.Index(name); ok { 168 return LabelValue{ 169 key: name, 170 val: l.kv.Vals[i], 171 } 172 } 173 174 if l.p != nil { 175 return l.p.get(name) 176 } 177 178 return Label(name) 179 } 180 181 // getList returns a slice of Labelers that defines the labels and their values. 182 func (l *labelScope) getList() []Labeler { 183 var list []Labeler 184 185 for _, k := range l.set.keys { 186 _, v := l.get(k).Label() 187 188 list = append(list, LabelValue{ 189 key: k, 190 val: v, 191 }) 192 } 193 194 return list 195 } 196 197 // getMap is a conversion function necessary to wrap the prometheus client. 198 func (l *labelScope) getMap() map[string]string { 199 m := make(map[string]string) 200 201 for _, k := range l.set.keys { 202 _, v := l.get(k).Label() 203 204 m[k] = v 205 } 206 207 return m 208 }