63 lines
1.6 KiB
Go
63 lines
1.6 KiB
Go
package main
|
|
|
|
import (
|
|
"sort"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var latencyBounds = []time.Duration{
|
|
time.Millisecond, 3 * time.Millisecond, 5 * time.Millisecond, 10 * time.Millisecond,
|
|
25 * time.Millisecond, 50 * time.Millisecond, 100 * time.Millisecond, 200 * time.Millisecond,
|
|
500 * time.Millisecond, time.Second, 2 * time.Second, 5 * time.Second, 10 * time.Second,
|
|
}
|
|
|
|
type DurationHistogram struct {
|
|
count atomic.Int64
|
|
sum atomic.Int64
|
|
max atomic.Int64
|
|
bucket []atomic.Int64
|
|
}
|
|
|
|
type HistogramSnapshot struct {
|
|
Count int64 `json:"count"`
|
|
SumMS float64 `json:"sum_ms"`
|
|
MaxMS float64 `json:"max_ms"`
|
|
Buckets map[string]int64 `json:"buckets"`
|
|
}
|
|
|
|
func NewDurationHistogram() *DurationHistogram {
|
|
return &DurationHistogram{bucket: make([]atomic.Int64, len(latencyBounds)+1)}
|
|
}
|
|
|
|
func (h *DurationHistogram) Observe(value time.Duration) {
|
|
if value < 0 {
|
|
return
|
|
}
|
|
nanos := int64(value)
|
|
h.count.Add(1)
|
|
h.sum.Add(nanos)
|
|
for previous := h.max.Load(); nanos > previous; previous = h.max.Load() {
|
|
if h.max.CompareAndSwap(previous, nanos) {
|
|
break
|
|
}
|
|
}
|
|
idx := sort.Search(len(latencyBounds), func(i int) bool { return value <= latencyBounds[i] })
|
|
h.bucket[idx].Add(1)
|
|
}
|
|
|
|
func (h *DurationHistogram) Snapshot() HistogramSnapshot {
|
|
buckets := make(map[string]int64, len(h.bucket))
|
|
for i := range h.bucket {
|
|
label := "+Inf"
|
|
if i < len(latencyBounds) {
|
|
label = latencyBounds[i].String()
|
|
}
|
|
buckets[label] = h.bucket[i].Load()
|
|
}
|
|
return HistogramSnapshot{
|
|
Count: h.count.Load(), SumMS: float64(h.sum.Load()) / float64(time.Millisecond),
|
|
MaxMS: float64(h.max.Load()) / float64(time.Millisecond), Buckets: buckets,
|
|
}
|
|
}
|