123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238 |
- package metrics
- import (
- "math"
- "sort"
- "sync"
- "time"
- )
- // Initial slice capacity for the values stored in a ResettingTimer
- const InitialResettingTimerSliceCap = 10
- // ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
- type ResettingTimer interface {
- Values() []int64
- Snapshot() ResettingTimer
- Percentiles([]float64) []int64
- Mean() float64
- Time(func())
- Update(time.Duration)
- UpdateSince(time.Time)
- }
- // GetOrRegisterResettingTimer returns an existing ResettingTimer or constructs and registers a
- // new StandardResettingTimer.
- func GetOrRegisterResettingTimer(name string, r Registry) ResettingTimer {
- if nil == r {
- r = DefaultRegistry
- }
- return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer)
- }
- // NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer.
- func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
- c := NewResettingTimer()
- if nil == r {
- r = DefaultRegistry
- }
- r.Register(name, c)
- return c
- }
- // NewResettingTimer constructs a new StandardResettingTimer
- func NewResettingTimer() ResettingTimer {
- if !Enabled {
- return NilResettingTimer{}
- }
- return &StandardResettingTimer{
- values: make([]int64, 0, InitialResettingTimerSliceCap),
- }
- }
- // NilResettingTimer is a no-op ResettingTimer.
- type NilResettingTimer struct {
- }
- // Values is a no-op.
- func (NilResettingTimer) Values() []int64 { return nil }
- // Snapshot is a no-op.
- func (NilResettingTimer) Snapshot() ResettingTimer { return NilResettingTimer{} }
- // Time is a no-op.
- func (NilResettingTimer) Time(func()) {}
- // Update is a no-op.
- func (NilResettingTimer) Update(time.Duration) {}
- // Percentiles panics.
- func (NilResettingTimer) Percentiles([]float64) []int64 {
- panic("Percentiles called on a NilResettingTimer")
- }
- // Mean panics.
- func (NilResettingTimer) Mean() float64 {
- panic("Mean called on a NilResettingTimer")
- }
- // UpdateSince is a no-op.
- func (NilResettingTimer) UpdateSince(time.Time) {}
- // StandardResettingTimer is the standard implementation of a ResettingTimer.
- // and Meter.
- type StandardResettingTimer struct {
- values []int64
- mutex sync.Mutex
- }
- // Values returns a slice with all measurements.
- func (t *StandardResettingTimer) Values() []int64 {
- return t.values
- }
- // Snapshot resets the timer and returns a read-only copy of its contents.
- func (t *StandardResettingTimer) Snapshot() ResettingTimer {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- currentValues := t.values
- t.values = make([]int64, 0, InitialResettingTimerSliceCap)
- return &ResettingTimerSnapshot{
- values: currentValues,
- }
- }
- // Percentiles panics.
- func (t *StandardResettingTimer) Percentiles([]float64) []int64 {
- panic("Percentiles called on a StandardResettingTimer")
- }
- // Mean panics.
- func (t *StandardResettingTimer) Mean() float64 {
- panic("Mean called on a StandardResettingTimer")
- }
- // Record the duration of the execution of the given function.
- func (t *StandardResettingTimer) Time(f func()) {
- ts := time.Now()
- f()
- t.Update(time.Since(ts))
- }
- // Record the duration of an event.
- func (t *StandardResettingTimer) Update(d time.Duration) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- t.values = append(t.values, int64(d))
- }
- // Record the duration of an event that started at a time and ends now.
- func (t *StandardResettingTimer) UpdateSince(ts time.Time) {
- t.mutex.Lock()
- defer t.mutex.Unlock()
- t.values = append(t.values, int64(time.Since(ts)))
- }
- // ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
- type ResettingTimerSnapshot struct {
- values []int64
- mean float64
- thresholdBoundaries []int64
- calculated bool
- }
- // Snapshot returns the snapshot.
- func (t *ResettingTimerSnapshot) Snapshot() ResettingTimer { return t }
- // Time panics.
- func (*ResettingTimerSnapshot) Time(func()) {
- panic("Time called on a ResettingTimerSnapshot")
- }
- // Update panics.
- func (*ResettingTimerSnapshot) Update(time.Duration) {
- panic("Update called on a ResettingTimerSnapshot")
- }
- // UpdateSince panics.
- func (*ResettingTimerSnapshot) UpdateSince(time.Time) {
- panic("UpdateSince called on a ResettingTimerSnapshot")
- }
- // Values returns all values from snapshot.
- func (t *ResettingTimerSnapshot) Values() []int64 {
- return t.values
- }
- // Percentiles returns the boundaries for the input percentiles.
- func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []int64 {
- t.calc(percentiles)
- return t.thresholdBoundaries
- }
- // Mean returns the mean of the snapshotted values
- func (t *ResettingTimerSnapshot) Mean() float64 {
- if !t.calculated {
- t.calc([]float64{})
- }
- return t.mean
- }
- func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
- sort.Sort(Int64Slice(t.values))
- count := len(t.values)
- if count > 0 {
- min := t.values[0]
- max := t.values[count-1]
- cumulativeValues := make([]int64, count)
- cumulativeValues[0] = min
- for i := 1; i < count; i++ {
- cumulativeValues[i] = t.values[i] + cumulativeValues[i-1]
- }
- t.thresholdBoundaries = make([]int64, len(percentiles))
- thresholdBoundary := max
- for i, pct := range percentiles {
- if count > 1 {
- var abs float64
- if pct >= 0 {
- abs = pct
- } else {
- abs = 100 + pct
- }
- // poor man's math.Round(x):
- // math.Floor(x + 0.5)
- indexOfPerc := int(math.Floor(((abs / 100.0) * float64(count)) + 0.5))
- if pct >= 0 && indexOfPerc > 0 {
- indexOfPerc -= 1 // index offset=0
- }
- thresholdBoundary = t.values[indexOfPerc]
- }
- t.thresholdBoundaries[i] = thresholdBoundary
- }
- sum := cumulativeValues[count-1]
- t.mean = float64(sum) / float64(count)
- } else {
- t.thresholdBoundaries = make([]int64, len(percentiles))
- t.mean = 0
- }
- t.calculated = true
- }
- // Int64Slice attaches the methods of sort.Interface to []int64, sorting in increasing order.
- type Int64Slice []int64
- func (s Int64Slice) Len() int { return len(s) }
- func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] }
- func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|