tailscale/prober/histogram.go
Percy Wegmann 5095efd628 prober: make histogram buckets cumulative
Histogram buckets should include counts for all values under the bucket ceiling,
not just those between the ceiling and the next lower ceiling.

See https://prometheus.io/docs/tutorials/understanding_metric_types/\#histogram

Updates tailscale/corp#24522

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2024-12-20 10:28:37 -06:00

50 lines
1.2 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package prober
import (
"slices"
"sync"
)
// histogram serves as an adapter to the Prometheus histogram datatype.
// The prober framework passes labels at custom metric collection time that
// it expects to be coupled with the returned metrics. See ProbeClass.Metrics
// and its call sites. Native prometheus histograms cannot be collected while
// injecting more labels. Instead we use this type and pass observations +
// collection labels to prometheus.MustNewConstHistogram() at prometheus
// metric collection time.
type histogram struct {
count uint64
sum float64
buckets []float64
bucketedCounts map[float64]uint64
mx sync.Mutex
}
// newHistogram constructs a histogram that buckets data based on the given
// slice of upper bounds.
func newHistogram(buckets []float64) *histogram {
slices.Sort(buckets)
return &histogram{
buckets: buckets,
bucketedCounts: make(map[float64]uint64, len(buckets)),
}
}
func (h *histogram) add(v float64) {
h.mx.Lock()
defer h.mx.Unlock()
h.count++
h.sum += v
for _, b := range h.buckets {
if v > b {
continue
}
h.bucketedCounts[b] += 1
}
}