|
|
@ -1,9 +1,7 @@ |
|
|
|
package metrics |
|
|
|
package metrics |
|
|
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
import ( |
|
|
|
"math" |
|
|
|
|
|
|
|
"sync" |
|
|
|
"sync" |
|
|
|
"sync/atomic" |
|
|
|
|
|
|
|
"time" |
|
|
|
"time" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
@ -20,13 +18,10 @@ type MeterSnapshot interface { |
|
|
|
type Meter interface { |
|
|
|
type Meter interface { |
|
|
|
Mark(int64) |
|
|
|
Mark(int64) |
|
|
|
Snapshot() MeterSnapshot |
|
|
|
Snapshot() MeterSnapshot |
|
|
|
Stop() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// GetOrRegisterMeter returns an existing Meter or constructs and registers a
|
|
|
|
// GetOrRegisterMeter returns an existing Meter or constructs and registers a
|
|
|
|
// new StandardMeter.
|
|
|
|
// new StandardMeter.
|
|
|
|
// Be sure to unregister the meter from the registry once it is of no use to
|
|
|
|
|
|
|
|
// allow for garbage collection.
|
|
|
|
|
|
|
|
func GetOrRegisterMeter(name string, r Registry) Meter { |
|
|
|
func GetOrRegisterMeter(name string, r Registry) Meter { |
|
|
|
if nil == r { |
|
|
|
if nil == r { |
|
|
|
r = DefaultRegistry |
|
|
|
r = DefaultRegistry |
|
|
@ -35,36 +30,15 @@ func GetOrRegisterMeter(name string, r Registry) Meter { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NewMeter constructs a new StandardMeter and launches a goroutine.
|
|
|
|
// NewMeter constructs a new StandardMeter and launches a goroutine.
|
|
|
|
// Be sure to call Stop() once the meter is of no use to allow for garbage collection.
|
|
|
|
|
|
|
|
func NewMeter() Meter { |
|
|
|
func NewMeter() Meter { |
|
|
|
if !Enabled { |
|
|
|
if !Enabled { |
|
|
|
return NilMeter{} |
|
|
|
return NilMeter{} |
|
|
|
} |
|
|
|
} |
|
|
|
m := newStandardMeter() |
|
|
|
return newStandardMeter() |
|
|
|
arbiter.Lock() |
|
|
|
|
|
|
|
defer arbiter.Unlock() |
|
|
|
|
|
|
|
arbiter.meters[m] = struct{}{} |
|
|
|
|
|
|
|
if !arbiter.started { |
|
|
|
|
|
|
|
arbiter.started = true |
|
|
|
|
|
|
|
go arbiter.tick() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return m |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// NewInactiveMeter returns a meter but does not start any goroutines. This
|
|
|
|
|
|
|
|
// method is mainly intended for testing.
|
|
|
|
|
|
|
|
func NewInactiveMeter() Meter { |
|
|
|
|
|
|
|
if !Enabled { |
|
|
|
|
|
|
|
return NilMeter{} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
m := newStandardMeter() |
|
|
|
|
|
|
|
return m |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// NewRegisteredMeter constructs and registers a new StandardMeter
|
|
|
|
// NewRegisteredMeter constructs and registers a new StandardMeter
|
|
|
|
// and launches a goroutine.
|
|
|
|
// and launches a goroutine.
|
|
|
|
// Be sure to unregister the meter from the registry once it is of no use to
|
|
|
|
|
|
|
|
// allow for garbage collection.
|
|
|
|
|
|
|
|
func NewRegisteredMeter(name string, r Registry) Meter { |
|
|
|
func NewRegisteredMeter(name string, r Registry) Meter { |
|
|
|
return GetOrRegisterMeter(name, r) |
|
|
|
return GetOrRegisterMeter(name, r) |
|
|
|
} |
|
|
|
} |
|
|
@ -104,13 +78,12 @@ func (NilMeter) Stop() {} |
|
|
|
|
|
|
|
|
|
|
|
// StandardMeter is the standard implementation of a Meter.
|
|
|
|
// StandardMeter is the standard implementation of a Meter.
|
|
|
|
type StandardMeter struct { |
|
|
|
type StandardMeter struct { |
|
|
|
count atomic.Int64 |
|
|
|
count int64 |
|
|
|
uncounted atomic.Int64 // not yet added to the EWMAs
|
|
|
|
uncounted int64 |
|
|
|
rateMean atomic.Uint64 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a1, a5, a15 EWMA |
|
|
|
a1, a5, a15 EWMA |
|
|
|
startTime time.Time |
|
|
|
startTime time.Time |
|
|
|
stopped atomic.Bool |
|
|
|
lastMark time.Time |
|
|
|
|
|
|
|
mutex sync.Mutex |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func newStandardMeter() *StandardMeter { |
|
|
|
func newStandardMeter() *StandardMeter { |
|
|
@ -119,71 +92,58 @@ func newStandardMeter() *StandardMeter { |
|
|
|
a5: NewEWMA5(), |
|
|
|
a5: NewEWMA5(), |
|
|
|
a15: NewEWMA15(), |
|
|
|
a15: NewEWMA15(), |
|
|
|
startTime: time.Now(), |
|
|
|
startTime: time.Now(), |
|
|
|
} |
|
|
|
lastMark: time.Now(), |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Stop stops the meter, Mark() will be a no-op if you use it after being stopped.
|
|
|
|
|
|
|
|
func (m *StandardMeter) Stop() { |
|
|
|
|
|
|
|
if stopped := m.stopped.Swap(true); !stopped { |
|
|
|
|
|
|
|
arbiter.Lock() |
|
|
|
|
|
|
|
delete(arbiter.meters, m) |
|
|
|
|
|
|
|
arbiter.Unlock() |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Mark records the occurrence of n events.
|
|
|
|
// Mark records the occurrence of n events.
|
|
|
|
func (m *StandardMeter) Mark(n int64) { |
|
|
|
func (m *StandardMeter) Mark(n int64) { |
|
|
|
m.uncounted.Add(n) |
|
|
|
m.mutex.Lock() |
|
|
|
|
|
|
|
defer m.mutex.Unlock() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Synchronize rateMean so that it's only updated after
|
|
|
|
|
|
|
|
// a sampling period elapses.
|
|
|
|
|
|
|
|
if elapsed := time.Since(m.lastMark); elapsed >= 5*time.Second { |
|
|
|
|
|
|
|
m.lastMark = m.lastMark.Add(elapsed) |
|
|
|
|
|
|
|
m.count += m.uncounted |
|
|
|
|
|
|
|
m.uncounted = 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
m.uncounted += n |
|
|
|
|
|
|
|
m.a1.Update(n) |
|
|
|
|
|
|
|
m.a5.Update(n) |
|
|
|
|
|
|
|
m.a15.Update(n) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Snapshot returns a read-only copy of the meter.
|
|
|
|
// Snapshot returns a read-only copy of the meter.
|
|
|
|
func (m *StandardMeter) Snapshot() MeterSnapshot { |
|
|
|
func (m *StandardMeter) Snapshot() MeterSnapshot { |
|
|
|
|
|
|
|
m.mutex.Lock() |
|
|
|
|
|
|
|
defer m.mutex.Unlock() |
|
|
|
|
|
|
|
if elapsed := time.Since(m.lastMark); elapsed >= 5*time.Second { |
|
|
|
|
|
|
|
m.lastMark = m.lastMark.Add(elapsed) |
|
|
|
|
|
|
|
m.count += m.uncounted |
|
|
|
|
|
|
|
m.uncounted = 0 |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
rateMean := float64(m.count) / (1 + time.Since(m.startTime).Seconds()) |
|
|
|
return &meterSnapshot{ |
|
|
|
return &meterSnapshot{ |
|
|
|
count: m.count.Load() + m.uncounted.Load(), |
|
|
|
count: m.count + m.uncounted, |
|
|
|
rate1: m.a1.Snapshot().Rate(), |
|
|
|
rate1: m.a1.Snapshot().Rate(), |
|
|
|
rate5: m.a5.Snapshot().Rate(), |
|
|
|
rate5: m.a5.Snapshot().Rate(), |
|
|
|
rate15: m.a15.Snapshot().Rate(), |
|
|
|
rate15: m.a15.Snapshot().Rate(), |
|
|
|
rateMean: math.Float64frombits(m.rateMean.Load()), |
|
|
|
rateMean: rateMean, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (m *StandardMeter) tick() { |
|
|
|
// used to elapse time in unit tests.
|
|
|
|
// Take the uncounted values, add to count
|
|
|
|
func (m *StandardMeter) addToTimestamp(d time.Duration) { |
|
|
|
n := m.uncounted.Swap(0) |
|
|
|
m.startTime = m.startTime.Add(d) |
|
|
|
count := m.count.Add(n) |
|
|
|
m.lastMark = m.lastMark.Add(d) |
|
|
|
m.rateMean.Store(math.Float64bits(float64(count) / time.Since(m.startTime).Seconds())) |
|
|
|
a1, _ := m.a1.(*StandardEWMA) |
|
|
|
// Update the EWMA's internal state
|
|
|
|
a1.addToTimestamp(d) |
|
|
|
m.a1.Update(n) |
|
|
|
|
|
|
|
m.a5.Update(n) |
|
|
|
|
|
|
|
m.a15.Update(n) |
|
|
|
|
|
|
|
// And trigger them to calculate the rates
|
|
|
|
|
|
|
|
m.a1.Tick() |
|
|
|
|
|
|
|
m.a5.Tick() |
|
|
|
|
|
|
|
m.a15.Tick() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// meterArbiter ticks meters every 5s from a single goroutine.
|
|
|
|
a5, _ := m.a5.(*StandardEWMA) |
|
|
|
// meters are references in a set for future stopping.
|
|
|
|
a5.addToTimestamp(d) |
|
|
|
type meterArbiter struct { |
|
|
|
|
|
|
|
sync.RWMutex |
|
|
|
|
|
|
|
started bool |
|
|
|
|
|
|
|
meters map[*StandardMeter]struct{} |
|
|
|
|
|
|
|
ticker *time.Ticker |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var arbiter = meterArbiter{ticker: time.NewTicker(5 * time.Second), meters: make(map[*StandardMeter]struct{})} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// tick meters on the scheduled interval
|
|
|
|
|
|
|
|
func (ma *meterArbiter) tick() { |
|
|
|
|
|
|
|
for range ma.ticker.C { |
|
|
|
|
|
|
|
ma.tickMeters() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (ma *meterArbiter) tickMeters() { |
|
|
|
a15, _ := m.a15.(*StandardEWMA) |
|
|
|
ma.RLock() |
|
|
|
a15.addToTimestamp(d) |
|
|
|
defer ma.RUnlock() |
|
|
|
|
|
|
|
for meter := range ma.meters { |
|
|
|
|
|
|
|
meter.tick() |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|