util/usermetrics: add package and container for usermetrics

Signed-off-by: Kristoffer Dalby <kristoffer@tailscale.com>
This commit is contained in:
Kristoffer Dalby
2024-08-01 17:42:19 +02:00
parent dd5499ed5f
commit 9eec0d969c
10 changed files with 82 additions and 28 deletions

View File

@@ -39,7 +39,7 @@ func NewMultiLabelMap[T comparable](name string, promType, helpText string) *Mul
Help: helpText,
}
var zero T
_ = labelString(zero) // panic early if T is invalid
_ = LabelString(zero) // panic early if T is invalid
expvar.Publish(name, m)
return m
}
@@ -50,8 +50,10 @@ type labelsAndValue[T comparable] struct {
val expvar.Var
}
// labelString returns a Prometheus-formatted label string for the given key.
func labelString(k any) string {
// LabelString returns a Prometheus-formatted label string for the given key.
// k must be a struct type with scalar fields, as required by MultiLabelMap,
// if k is not a struct, it will panic.
func LabelString(k any) string {
rv := reflect.ValueOf(k)
t := rv.Type()
if t.Kind() != reflect.Struct {
@@ -150,7 +152,7 @@ func (v *MultiLabelMap[T]) Init() *MultiLabelMap[T] {
//
// v.mu must be held.
func (v *MultiLabelMap[T]) addKeyLocked(key T, val expvar.Var) {
ls := labelString(key)
ls := LabelString(key)
ent := labelsAndValue[T]{key, ls, val}
// Using insertion sort to place key into the already-sorted v.keys.
@@ -234,7 +236,7 @@ func (v *MultiLabelMap[T]) AddFloat(key T, delta float64) {
// This is not optimized for highly concurrent usage; it's presumed to only be
// used rarely, at startup.
func (v *MultiLabelMap[T]) Delete(key T) {
ls := labelString(key)
ls := LabelString(key)
v.mu.Lock()
defer v.mu.Unlock()