metrics: fix resetting timer

This commit is contained in:
Martin Holst Swende 2024-11-27 05:08:45 +01:00
parent 4111636841
commit 9e22de7c47
No known key found for this signature in database
GPG key ID: 683B438C05A5DDF0
5 changed files with 31 additions and 68 deletions

View file

@ -174,7 +174,7 @@ func (exp *exp) publishTimer(name string, metric *metrics.Timer) {
exp.getFloat(name + ".mean-rate").Set(t.RateMean())
}
func (exp *exp) publishResettingTimer(name string, metric metrics.ResettingTimer) {
func (exp *exp) publishResettingTimer(name string, metric *metrics.ResettingTimer) {
t := metric.Snapshot()
ps := t.Percentiles([]float64{0.50, 0.75, 0.95, 0.99})
exp.getInt(name + ".count").Set(int64(t.Count()))
@ -204,7 +204,7 @@ func (exp *exp) syncToExpvar() {
exp.publishMeter(name, i)
case *metrics.Timer:
exp.publishTimer(name, i)
case metrics.ResettingTimer:
case *metrics.ResettingTimer:
exp.publishResettingTimer(name, i)
default:
panic(fmt.Sprintf("unsupported type for '%s': %T", name, i))

View file

@ -97,7 +97,7 @@ func readMeter(namespace, name string, i interface{}) (string, map[string]interf
"meanrate": ms.RateMean(),
}
return measurement, fields
case metrics.ResettingTimer:
case *metrics.ResettingTimer:
ms := metric.Snapshot()
if ms.Count() == 0 {
break

View file

@ -67,7 +67,7 @@ func (c *collector) Add(name string, i any) error {
c.addMeter(name, m.Snapshot())
case *metrics.Timer:
c.addTimer(name, m.Snapshot())
case metrics.ResettingTimer:
case *metrics.ResettingTimer:
c.addResettingTimer(name, m.Snapshot())
default:
return fmt.Errorf("unknown prometheus metric type %T", i)
@ -121,7 +121,7 @@ func (c *collector) addTimer(name string, m *metrics.TimerSnapshot) {
c.buff.WriteRune('\n')
}
func (c *collector) addResettingTimer(name string, m metrics.ResettingTimerSnapshot) {
func (c *collector) addResettingTimer(name string, m *metrics.ResettingTimerSnapshot) {
if m.Count() <= 0 {
return
}

View file

@ -211,7 +211,7 @@ func (r *StandardRegistry) Unregister(name string) {
func (r *StandardRegistry) loadOrRegister(name string, i interface{}) (interface{}, bool, bool) {
switch i.(type) {
case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, ResettingTimer:
case *Counter, *CounterFloat64, *Gauge, *GaugeFloat64, *GaugeInfo, *Healthcheck, Histogram, *Meter, *Timer, *ResettingTimer:
default:
return nil, false, false
}

View file

@ -5,36 +5,17 @@ import (
"time"
)
// Initial slice capacity for the values stored in a ResettingTimer
const InitialResettingTimerSliceCap = 10
type ResettingTimerSnapshot interface {
Count() int
Mean() float64
Max() int64
Min() int64
Percentiles([]float64) []float64
}
// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
type ResettingTimer interface {
Snapshot() ResettingTimerSnapshot
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 {
// new ResettingTimer.
func GetOrRegisterResettingTimer(name string, r Registry) *ResettingTimer {
if nil == r {
r = DefaultRegistry
}
return r.GetOrRegister(name, NewResettingTimer).(ResettingTimer)
return r.GetOrRegister(name, NewResettingTimer).(*ResettingTimer)
}
// NewRegisteredResettingTimer constructs and registers a new StandardResettingTimer.
func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
// NewRegisteredResettingTimer constructs and registers a new ResettingTimer.
func NewRegisteredResettingTimer(name string, r Registry) *ResettingTimer {
c := NewResettingTimer()
if nil == r {
r = DefaultRegistry
@ -43,33 +24,15 @@ func NewRegisteredResettingTimer(name string, r Registry) ResettingTimer {
return c
}
// NewResettingTimer constructs a new StandardResettingTimer
func NewResettingTimer() ResettingTimer {
if !metricsEnabled {
return NilResettingTimer{}
}
return &StandardResettingTimer{
values: make([]int64, 0, InitialResettingTimerSliceCap),
// NewResettingTimer constructs a new ResettingTimer
func NewResettingTimer() *ResettingTimer {
return &ResettingTimer{
values: make([]int64, 0, 10),
}
}
// NilResettingTimer is a no-op ResettingTimer.
type NilResettingTimer struct{}
func (NilResettingTimer) Values() []int64 { return nil }
func (n NilResettingTimer) Snapshot() ResettingTimerSnapshot { return n }
func (NilResettingTimer) Time(f func()) { f() }
func (NilResettingTimer) Update(time.Duration) {}
func (NilResettingTimer) Percentiles([]float64) []float64 { return nil }
func (NilResettingTimer) Mean() float64 { return 0.0 }
func (NilResettingTimer) Max() int64 { return 0 }
func (NilResettingTimer) Min() int64 { return 0 }
func (NilResettingTimer) UpdateSince(time.Time) {}
func (NilResettingTimer) Count() int { return 0 }
// StandardResettingTimer is the standard implementation of a ResettingTimer.
// and Meter.
type StandardResettingTimer struct {
// ResettingTimer is used for storing aggregated values for timers, which are reset on every flush interval.
type ResettingTimer struct {
values []int64
sum int64 // sum is a running count of the total sum, used later to calculate mean
@ -77,28 +40,28 @@ type StandardResettingTimer struct {
}
// Snapshot resets the timer and returns a read-only copy of its contents.
func (t *StandardResettingTimer) Snapshot() ResettingTimerSnapshot {
func (t *ResettingTimer) Snapshot() *ResettingTimerSnapshot {
t.mutex.Lock()
defer t.mutex.Unlock()
snapshot := &resettingTimerSnapshot{}
snapshot := &ResettingTimerSnapshot{}
if len(t.values) > 0 {
snapshot.mean = float64(t.sum) / float64(len(t.values))
snapshot.values = t.values
t.values = make([]int64, 0, InitialResettingTimerSliceCap)
t.values = make([]int64, 0, 10)
}
t.sum = 0
return snapshot
}
// Record the duration of the execution of the given function.
func (t *StandardResettingTimer) Time(f func()) {
func (t *ResettingTimer) 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) {
func (t *ResettingTimer) Update(d time.Duration) {
t.mutex.Lock()
defer t.mutex.Unlock()
t.values = append(t.values, int64(d))
@ -106,12 +69,12 @@ func (t *StandardResettingTimer) Update(d time.Duration) {
}
// Record the duration of an event that started at a time and ends now.
func (t *StandardResettingTimer) UpdateSince(ts time.Time) {
func (t *ResettingTimer) UpdateSince(ts time.Time) {
t.Update(time.Since(ts))
}
// resettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
type resettingTimerSnapshot struct {
// ResettingTimerSnapshot is a point-in-time copy of another ResettingTimer.
type ResettingTimerSnapshot struct {
values []int64
mean float64
max int64
@ -121,20 +84,20 @@ type resettingTimerSnapshot struct {
}
// Count return the length of the values from snapshot.
func (t *resettingTimerSnapshot) Count() int {
func (t *ResettingTimerSnapshot) Count() int {
return len(t.values)
}
// Percentiles returns the boundaries for the input percentiles.
// note: this method is not thread safe
func (t *resettingTimerSnapshot) Percentiles(percentiles []float64) []float64 {
func (t *ResettingTimerSnapshot) Percentiles(percentiles []float64) []float64 {
t.calc(percentiles)
return t.thresholdBoundaries
}
// Mean returns the mean of the snapshotted values
// note: this method is not thread safe
func (t *resettingTimerSnapshot) Mean() float64 {
func (t *ResettingTimerSnapshot) Mean() float64 {
if !t.calculated {
t.calc(nil)
}
@ -144,7 +107,7 @@ func (t *resettingTimerSnapshot) Mean() float64 {
// Max returns the max of the snapshotted values
// note: this method is not thread safe
func (t *resettingTimerSnapshot) Max() int64 {
func (t *ResettingTimerSnapshot) Max() int64 {
if !t.calculated {
t.calc(nil)
}
@ -153,14 +116,14 @@ func (t *resettingTimerSnapshot) Max() int64 {
// Min returns the min of the snapshotted values
// note: this method is not thread safe
func (t *resettingTimerSnapshot) Min() int64 {
func (t *ResettingTimerSnapshot) Min() int64 {
if !t.calculated {
t.calc(nil)
}
return t.min
}
func (t *resettingTimerSnapshot) calc(percentiles []float64) {
func (t *ResettingTimerSnapshot) calc(percentiles []float64) {
scores := CalculatePercentiles(t.values, percentiles)
t.thresholdBoundaries = scores
if len(t.values) == 0 {