Ctrl K
ComunidadGuías…

internal/telemetry/metric.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package telemetry

import (
	"math"
	"slices"
	"strings"
	"sync"
	"sync/atomic"
)

type vector struct {
	name    string
	help    string
	kind    string
	labels  []string
	buckets []float64
	reg     *Registry
	silent  bool

	mu     sync.Mutex
	series map[string]*series
}

type series struct {
	values  []string
	value   atomic.Uint64
	sum     atomic.Uint64
	count   atomic.Uint64
	buckets []atomic.Uint64
}

func (v *vector) blank(values []string) *series {
	made := &series{values: slices.Clone(values)}
	if v.kind == histogramKind {
		made.buckets = make([]atomic.Uint64, len(v.buckets))
	}
	return made
}

func (v *vector) at(values []string) *series {
	if len(values) != len(v.labels) {
		v.reject()
		return nil
	}
	key := strings.Join(values, keyJoin)
	v.mu.Lock()
	found, known := v.series[key]
	if known {
		v.mu.Unlock()
		return found
	}
	if len(v.series) < seriesLimit {
		made := v.blank(values)
		v.series[key] = made
		v.mu.Unlock()
		return made
	}
	folded := v.otherLocked()
	v.mu.Unlock()
	v.fold()
	return folded
}

func (v *vector) otherLocked() *series {
	values := make([]string, len(v.labels))
	for at := range values {
		values[at] = otherValue
	}
	key := strings.Join(values, keyJoin)
	found, known := v.series[key]
	if known {
		return found
	}
	made := v.blank(values)
	v.series[key] = made
	return made
}

func (v *vector) fold() {
	if v.silent {
		return
	}
	v.reg.folded.Inc(v.name)
}

func (v *vector) reject() {
	if v.silent {
		return
	}
	v.reg.rejected.Inc(v.name)
}

func (v *vector) count() int {
	v.mu.Lock()
	defer v.mu.Unlock()
	return len(v.series)
}

type Counter struct {
	vec *vector
}

func (c *Counter) Inc(labels ...string) {
	c.Add(1, labels...)
}

func (c *Counter) Add(delta float64, labels ...string) {
	if math.IsNaN(delta) || delta < 0 {
		c.vec.reject()
		return
	}
	found := c.vec.at(labels)
	if found == nil {
		return
	}
	addFloat(&found.value, delta)
}

type Gauge struct {
	vec *vector
}

func (g *Gauge) Set(value float64, labels ...string) {
	found := g.vec.at(labels)
	if found == nil {
		return
	}
	found.value.Store(math.Float64bits(value))
}

func (g *Gauge) Add(delta float64, labels ...string) {
	found := g.vec.at(labels)
	if found == nil {
		return
	}
	addFloat(&found.value, delta)
}

func (g *Gauge) Inc(labels ...string) {
	g.Add(1, labels...)
}

func (g *Gauge) Dec(labels ...string) {
	g.Add(-1, labels...)
}

type Histogram struct {
	vec *vector
}

func (h *Histogram) Observe(value float64, labels ...string) {
	if math.IsNaN(value) {
		h.vec.reject()
		return
	}
	found := h.vec.at(labels)
	if found == nil {
		return
	}
	found.count.Add(1)
	for at, bound := range h.vec.buckets {
		if value <= bound {
			found.buckets[at].Add(1)
			break
		}
	}
	addFloat(&found.sum, value)
}

func addFloat(into *atomic.Uint64, delta float64) {
	for {
		current := into.Load()
		next := math.Float64bits(math.Float64frombits(current) + delta)
		if into.CompareAndSwap(current, next) {
			return
		}
	}
}