65 lines
1.5 KiB
Go
65 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"sort"
|
|
"sync"
|
|
"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 {
|
|
mu sync.Mutex
|
|
count int64
|
|
sum time.Duration
|
|
max time.Duration
|
|
bucket []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([]int64, len(latencyBounds)+1)}
|
|
}
|
|
|
|
func (h *DurationHistogram) Observe(value time.Duration) {
|
|
if value < 0 {
|
|
return
|
|
}
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
h.count++
|
|
h.sum += value
|
|
if value > h.max {
|
|
h.max = value
|
|
}
|
|
idx := sort.Search(len(latencyBounds), func(i int) bool { return value <= latencyBounds[i] })
|
|
h.bucket[idx]++
|
|
}
|
|
|
|
func (h *DurationHistogram) Snapshot() HistogramSnapshot {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
buckets := make(map[string]int64, len(h.bucket))
|
|
for i, count := range h.bucket {
|
|
label := "+Inf"
|
|
if i < len(latencyBounds) {
|
|
label = latencyBounds[i].String()
|
|
}
|
|
buckets[label] = count
|
|
}
|
|
return HistogramSnapshot{
|
|
Count: h.count, SumMS: float64(h.sum) / float64(time.Millisecond),
|
|
MaxMS: float64(h.max) / float64(time.Millisecond), Buckets: buckets,
|
|
}
|
|
}
|