diff --git a/counter.go b/counter.go index 3bdc69c..b1b01da 100644 --- a/counter.go +++ b/counter.go @@ -3,17 +3,16 @@ package metrics import "sync/atomic" // Counters hold an int64 value that can be incremented and decremented. -// -// This is an interface so as to encourage other structs to implement -// the Counter API as appropriate. type Counter interface { Clear() Count() int64 Dec(int64) Inc(int64) + Snapshot() Counter } -// Get an existing or create and register a new Counter. +// GetOrRegisterCounter returns an existing Counter or constructs and registers +// a new StandardCounter. func GetOrRegisterCounter(name string, r Registry) Counter { if nil == r { r = DefaultRegistry @@ -21,7 +20,7 @@ func GetOrRegisterCounter(name string, r Registry) Counter { return r.GetOrRegister(name, NewCounter()).(Counter) } -// Create a new Counter. +// NewCounter constructs a new StandardCounter. func NewCounter() Counter { if UseNilMetrics { return NilCounter{} @@ -29,7 +28,7 @@ func NewCounter() Counter { return &StandardCounter{0} } -// Create and register a new Counter. +// NewRegisteredCounter constructs and registers a new StandardCounter. func NewRegisteredCounter(name string, r Registry) Counter { c := NewCounter() if nil == r { @@ -42,16 +41,16 @@ func NewRegisteredCounter(name string, r Registry) Counter { // No-op Counter. type NilCounter struct{} -// No-op. +// Clear is a no-op. func (NilCounter) Clear() {} -// No-op. +// Count is a no-op. func (NilCounter) Count() int64 { return 0 } -// No-op. +// Dec is a no-op. func (NilCounter) Dec(i int64) {} -// No-op. +// Inc is a no-op. func (NilCounter) Inc(i int64) {} // The standard implementation of a Counter uses the sync/atomic package @@ -60,22 +59,22 @@ type StandardCounter struct { count int64 } -// Clear the counter: set it to zero. +// Clear sets the counter to zero. func (c *StandardCounter) Clear() { atomic.StoreInt64(&c.count, 0) } -// Return the current count. +// Count returns the current count. func (c *StandardCounter) Count() int64 { return atomic.LoadInt64(&c.count) } -// Decrement the counter by the given amount. +// Dec decrements the counter by the given amount. func (c *StandardCounter) Dec(i int64) { atomic.AddInt64(&c.count, -i) } -// Increment the counter by the given amount. +// Inc increments the counter by the given amount. func (c *StandardCounter) Inc(i int64) { atomic.AddInt64(&c.count, i) } diff --git a/ewma.go b/ewma.go index b8abc59..f72040d 100644 --- a/ewma.go +++ b/ewma.go @@ -8,16 +8,14 @@ import ( // EWMAs continuously calculate an exponentially-weighted moving average // based on an outside source of clock ticks. -// -// This is an interface so as to encourage other structs to implement -// the EWMA API as appropriate. type EWMA interface { Rate() float64 + Snapshot() EWMA Tick() Update(int64) } -// Create a new EWMA with the given alpha. +// NewEWMA constructs a new EWMA with the given alpha. func NewEWMA(alpha float64) EWMA { if UseNilMetrics { return NilEWMA{} @@ -25,17 +23,17 @@ func NewEWMA(alpha float64) EWMA { return &StandardEWMA{alpha: alpha} } -// Create a new EWMA with alpha set for a one-minute moving average. +// NewEWMA1 constructs a new EWMA for a one-minute moving average. func NewEWMA1() EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/1)) } -// Create a new EWMA with alpha set for a five-minute moving average. +// NewEWMA5 constructs a new EWMA for a five-minute moving average. func NewEWMA5() EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/5)) } -// Create a new EWMA with alpha set for a fifteen-minute moving average. +// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. func NewEWMA15() EWMA { return NewEWMA(1 - math.Exp(-5.0/60.0/15)) } @@ -43,18 +41,18 @@ func NewEWMA15() EWMA { // No-op EWMA. type NilEWMA struct{} -// No-op. +// Rate is a no-op. func (NilEWMA) Rate() float64 { return 0.0 } // No-op. func (NilEWMA) Tick() {} -// No-op. +// Update is a no-op. func (NilEWMA) Update(n int64) {} -// The standard implementation of an EWMA tracks the number of uncounted -// events and processes them on each tick. It uses the sync/atomic package -// to manage uncounted events. +// StandardEWMA is the standard implementation of an EWMA and tracks the number +// of uncounted events and processes them on each tick. It uses the +// sync/atomic package to manage uncounted events. type StandardEWMA struct { alpha float64 init bool @@ -63,7 +61,7 @@ type StandardEWMA struct { uncounted int64 } -// Return the moving average rate of events per second. +// Rate returns the moving average rate of events per second. func (a *StandardEWMA) Rate() float64 { a.mutex.Lock() defer a.mutex.Unlock() @@ -85,7 +83,7 @@ func (a *StandardEWMA) Tick() { } } -// Add n uncounted events. +// Update adds n uncounted events. func (a *StandardEWMA) Update(n int64) { atomic.AddInt64(&a.uncounted, n) } diff --git a/gauge.go b/gauge.go index 69f07ed..b7f904a 100644 --- a/gauge.go +++ b/gauge.go @@ -3,15 +3,13 @@ package metrics import "sync/atomic" // Gauges hold an int64 value that can be set arbitrarily. -// -// This is an interface so as to encourage other structs to implement -// the Gauge API as appropriate. type Gauge interface { Update(int64) Value() int64 } -// Get an existing or create and register a new Gauge. +// GetOrRegisterGauge returns an existing Gauge or constructs and registers a +// new StandardGauge. func GetOrRegisterGauge(name string, r Registry) Gauge { if nil == r { r = DefaultRegistry @@ -19,7 +17,7 @@ func GetOrRegisterGauge(name string, r Registry) Gauge { return r.GetOrRegister(name, NewGauge()).(Gauge) } -// Create a new Gauge. +// NewGauge constructs a new StandardGauge. func NewGauge() Gauge { if UseNilMetrics { return NilGauge{} @@ -27,7 +25,7 @@ func NewGauge() Gauge { return &StandardGauge{0} } -// Create and register a new Gauge. +// NewRegisteredGauge constructs and registers a new StandardGauge. func NewRegisteredGauge(name string, r Registry) Gauge { c := NewGauge() if nil == r { @@ -43,11 +41,11 @@ type NilGauge struct{} // No-op. func (NilGauge) Update(v int64) {} -// No-op. +// Value is a no-op. func (NilGauge) Value() int64 { return 0 } -// The standard implementation of a Gauge uses the sync/atomic package -// to manage a single int64 value. +// StandardGauge is the standard implementation of a Gauge and uses the +// sync/atomic package to manage a single int64 value. type StandardGauge struct { value int64 } @@ -57,7 +55,7 @@ func (g *StandardGauge) Update(v int64) { atomic.StoreInt64(&g.value, v) } -// Return the gauge's current value. +// Value returns the gauge's current value. func (g *StandardGauge) Value() int64 { return atomic.LoadInt64(&g.value) } diff --git a/healthcheck.go b/healthcheck.go index ab924cf..445131c 100644 --- a/healthcheck.go +++ b/healthcheck.go @@ -1,9 +1,6 @@ package metrics -// Healthchecks hold an os.Error value describing an arbitrary up/down status. -// -// This is an interface so as to encourage other structs to implement -// the Healthcheck API as appropriate. +// Healthchecks hold an error value describing an arbitrary up/down status. type Healthcheck interface { Check() Error() error @@ -11,8 +8,8 @@ type Healthcheck interface { Unhealthy(error) } -// Create a new Healthcheck, which will use the given function to update -// its status. +// NewHealthcheck constructs a new Healthcheck which will use the given +// function to update its status. func NewHealthcheck(f func(Healthcheck)) Healthcheck { if UseNilMetrics { return NilHealthcheck{} @@ -20,44 +17,45 @@ func NewHealthcheck(f func(Healthcheck)) Healthcheck { return &StandardHealthcheck{nil, f} } -// No-op Healthcheck. +// NilHealthcheck is a no-op. type NilHealthcheck struct{} -// No-op. +// Check is a no-op. func (NilHealthcheck) Check() {} -// No-op. +// Error is a no-op. func (NilHealthcheck) Error() error { return nil } -// No-op. +// Healthy is a no-op. func (NilHealthcheck) Healthy() {} -// No-op. -func (NilHealthcheck) Unhealthy(err error) {} +// Unhealthy is a no-op. +func (NilHealthcheck) Unhealthy(error) {} -// The standard implementation of a Healthcheck stores the status and a -// function to call to update the status. +// StandardHealthcheck is the standard implementation of a Healthcheck and +// stores the status and a function to call to update the status. type StandardHealthcheck struct { err error f func(Healthcheck) } -// Update the healthcheck's status. +// Check runs the healthcheck function to update the healthcheck's status. func (h *StandardHealthcheck) Check() { h.f(h) } -// Return the healthcheck's status, which will be nil if it is healthy. +// Error returns the healthcheck's status, which will be nil if it is healthy. func (h *StandardHealthcheck) Error() error { return h.err } -// Mark the healthcheck as healthy. +// Healthy marks the healthcheck as healthy. func (h *StandardHealthcheck) Healthy() { h.err = nil } -// Mark the healthcheck as unhealthy. The error should provide details. +// Unhealthy marks the healthcheck as unhealthy. The error is stored and +// may be retrieved by the Error method. func (h *StandardHealthcheck) Unhealthy(err error) { h.err = err } diff --git a/histogram.go b/histogram.go index 7c25ad0..16786d1 100644 --- a/histogram.go +++ b/histogram.go @@ -6,10 +6,7 @@ import ( "sync/atomic" ) -// Histograms calculate distribution statistics from an int64 value. -// -// This is an interface so as to encourage other structs to implement -// the Histogram API as appropriate. +// Histograms calculate distribution statistics from a series of int64 values. type Histogram interface { Clear() Count() int64 @@ -19,13 +16,14 @@ type Histogram interface { Percentile(float64) float64 Percentiles([]float64) []float64 Sample() Sample + Snapshot() Histogram StdDev() float64 - Sum() int64 Update(int64) Variance() float64 } -// Get an existing or create and register a new Histogram. +// GetOrRegisterHistogram returns an existing Histogram or constructs and +// registers a new StandardHistogram. func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { if nil == r { r = DefaultRegistry @@ -33,22 +31,21 @@ func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { return r.GetOrRegister(name, NewHistogram(s)).(Histogram) } -// Create a new Histogram with the given Sample. The initial values compare -// so that the first value will be both min and max and the variance is flagged -// for special treatment on its first iteration. +// NewHistogram constructs a new StandardHistogram from a Sample. func NewHistogram(s Sample) Histogram { if UseNilMetrics { return NilHistogram{} } return &StandardHistogram{ - max: math.MinInt64, - min: math.MaxInt64, - s: s, - variance: [2]float64{-1.0, 0.0}, + max: math.MinInt64, + min: math.MaxInt64, + sample: s, + sampleMean: -1.0, } } -// Create and register a new Histogram. +// NewRegisteredHistogram constructs and registers a new StandardHistogram from +// a Sample. func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { c := NewHistogram(s) if nil == r { @@ -61,71 +58,73 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { // No-op Histogram. type NilHistogram struct{} -// No-op. +// Clear is a no-op. func (NilHistogram) Clear() {} -// No-op. +// Count is a no-op. func (NilHistogram) Count() int64 { return 0 } -// No-op. +// Max is a no-op. func (NilHistogram) Max() int64 { return 0 } -// No-op. +// Mean is a no-op. func (NilHistogram) Mean() float64 { return 0.0 } -// No-op. +// Min is a no-op. func (NilHistogram) Min() int64 { return 0 } -// No-op. +// Percentile is a no-op. func (NilHistogram) Percentile(p float64) float64 { return 0.0 } -// No-op. +// Percentiles is a no-op. func (NilHistogram) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -// No-op. +// Sample is a no-op. func (NilHistogram) Sample() Sample { return NilSample{} } // No-op. func (NilHistogram) StdDev() float64 { return 0.0 } -// No-op. -func (NilHistogram) Sum() int64 { return 0 } +// StdDev is a no-op. +func (NilHistogram) StdDev() float64 { return 0.0 } -// No-op. +// Update is a no-op. func (NilHistogram) Update(v int64) {} -// No-op. +// Variance is a no-op. func (NilHistogram) Variance() float64 { return 0.0 } -// The standard implementation of a Histogram uses a Sample and a goroutine -// to synchronize its calculations. +// StandardHistogram is the standard implementation of a Histogram and uses a +// Sample to bound its memory use. type StandardHistogram struct { - count, sum, min, max int64 + count, max, min, sum int64 mutex sync.Mutex - s Sample - variance [2]float64 + sample Sample + sampleMean float64 + varianceNumerator float64 } -// Clear the histogram. +// Clear clears the histogram and its sample. func (h *StandardHistogram) Clear() { h.mutex.Lock() defer h.mutex.Unlock() h.count = 0 h.max = math.MinInt64 h.min = math.MaxInt64 - h.s.Clear() + h.sample.Clear() h.sum = 0 - h.variance = [2]float64{-1.0, 0.0} + h.sampleMean = -1.0 + h.varianceNumerator = 0.0 } -// Return the count of inputs since the histogram was last cleared. +// Count returns the count of events since the histogram was last cleared. func (h *StandardHistogram) Count() int64 { return atomic.LoadInt64(&h.count) } -// Return the maximal value seen since the histogram was last cleared. +// Max returns the maximum value seen since the histogram was last cleared. func (h *StandardHistogram) Max() int64 { h.mutex.Lock() defer h.mutex.Unlock() @@ -135,7 +134,8 @@ func (h *StandardHistogram) Max() int64 { return h.max } -// Return the mean of all values seen since the histogram was last cleared. +// Mean returns the mean of all values seen since the histogram was last +// cleared. func (h *StandardHistogram) Mean() float64 { h.mutex.Lock() defer h.mutex.Unlock() @@ -145,7 +145,7 @@ func (h *StandardHistogram) Mean() float64 { return float64(h.sum) / float64(h.count) } -// Return the minimal value seen since the histogram was last cleared. +// Min returns the minimum value seen since the histogram was last cleared. func (h *StandardHistogram) Min() int64 { h.mutex.Lock() defer h.mutex.Unlock() @@ -155,12 +155,13 @@ func (h *StandardHistogram) Min() int64 { return h.min } -// Percentile returns an arbitrary percentile of sampled values. +// Percentile returns an arbitrary percentile of the values in the sample. func (h *StandardHistogram) Percentile(p float64) float64 { return h.s.Percentile(p) } -// Percentiles returns a slice of arbitrary percentiles of sampled values. +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. func (h *StandardHistogram) Percentiles(ps []float64) []float64 { return h.s.Percentiles(ps) } @@ -170,22 +171,17 @@ func (h *StandardHistogram) Sample() Sample { return h.s.Dup() } -// Return the standard deviation of all values seen since the histogram was -// last cleared. +// StdDev returns the standard deviation of all values seen since the histogram +// was last cleared. func (h *StandardHistogram) StdDev() float64 { return math.Sqrt(h.Variance()) } -// Return the sum of inputs since the histogram was last cleared. -func (h *StandardHistogram) Sum() int64 { - return atomic.LoadInt64(&h.sum) -} - -// Update the histogram with a new value. +// Update updates the histogram with a new value. func (h *StandardHistogram) Update(v int64) { h.mutex.Lock() defer h.mutex.Unlock() - h.s.Update(v) + h.sample.Update(v) h.count++ if v < h.min { h.min = v @@ -195,28 +191,34 @@ func (h *StandardHistogram) Update(v int64) { } h.sum += v fv := float64(v) - if -1.0 == h.variance[0] { - h.variance[0] = fv - h.variance[1] = 0.0 + if -1.0 == h.sampleMean { + h.sampleMean = fv + h.varianceNumerator = 0.0 } else { - m := h.variance[0] - s := h.variance[1] - h.variance[0] = m + (fv-m)/float64(h.count) - h.variance[1] = s + (fv-m)*(fv-h.variance[0]) + sampleMean := h.sampleMean + varianceNumerator := h.varianceNumerator + h.sampleMean = sampleMean + (fv-sampleMean)/float64(h.count) + h.varianceNumerator = varianceNumerator + (fv-sampleMean)*(fv-h.sampleMean) } } -// Return the variance of all values seen since the histogram was last cleared. +// Variance returns the variance of all values seen since the histogram was +// last cleared. func (h *StandardHistogram) Variance() float64 { h.mutex.Lock() defer h.mutex.Unlock() + return h.variance() +} + +// variance returns the variance of all the values in the sample but expects +// the lock to already be held. +func (h *StandardHistogram) variance() float64 { if 1 >= h.count { return 0.0 } - return h.variance[1] / float64(h.count-1) + return h.varianceNumerator / float64(h.count-1) } -// Cribbed from the standard library's `sort` package. type int64Slice []int64 func (p int64Slice) Len() int { return len(p) } diff --git a/json.go b/json.go index e7cca71..13e0230 100644 --- a/json.go +++ b/json.go @@ -1,8 +1,6 @@ package metrics -import ( - "encoding/json" -) +import "encoding/json" // MarshalJSON returns a byte slice containing a JSON representation of all // the metrics in the Registry. diff --git a/meter.go b/meter.go index f35d301..ad148a6 100644 --- a/meter.go +++ b/meter.go @@ -4,9 +4,6 @@ import "time" // Meters count events to produce exponentially-weighted moving average rates // at one-, five-, and fifteen-minutes and a mean rate. -// -// This is an interface so as to encourage other structs to implement -// the Meter API as appropriate. type Meter interface { Count() int64 Mark(int64) @@ -16,7 +13,8 @@ type Meter interface { RateMean() float64 } -// Get an existing or create and register a new Meter. +// GetOrRegisterMeter returns an existing Meter or constructs and registers a +// new StandardMeter. func GetOrRegisterMeter(name string, r Registry) Meter { if nil == r { r = DefaultRegistry @@ -24,22 +22,22 @@ func GetOrRegisterMeter(name string, r Registry) Meter { return r.GetOrRegister(name, NewMeter()).(Meter) } -// Create a new Meter. Create the communication channels and start the -// synchronizing goroutine. +// NewMeter constructs a new StandardMeter and launches a goroutine. func NewMeter() Meter { if UseNilMetrics { return NilMeter{} } m := &StandardMeter{ make(chan int64), - make(chan meterV), + make(chan *MeterSnapshot), time.NewTicker(5e9), } go m.arbiter() return m } -// Create and register a new Meter. +// NewMeter constructs and registers a new StandardMeter and launches a +// goroutine. func NewRegisteredMeter(name string, r Registry) Meter { c := NewMeter() if nil == r { @@ -52,22 +50,22 @@ func NewRegisteredMeter(name string, r Registry) Meter { // No-op Meter. type NilMeter struct{} -// No-op. +// Count is a no-op. func (NilMeter) Count() int64 { return 0 } -// No-op. +// Mark is a no-op. func (NilMeter) Mark(n int64) {} -// No-op. +// Rate1 is a no-op. func (NilMeter) Rate1() float64 { return 0.0 } -// No-op. +// Rate5 is a no-op. func (NilMeter) Rate5() float64 { return 0.0 } -// No-op. +// Rate15is a no-op. func (NilMeter) Rate15() float64 { return 0.0 } -// No-op. +// RateMean is a no-op. func (NilMeter) RateMean() float64 { return 0.0 } // The standard implementation of a Meter uses a goroutine to synchronize @@ -79,32 +77,32 @@ type StandardMeter struct { ticker *time.Ticker } -// Return the count of events seen. +// Count returns the number of events recorded. func (m *StandardMeter) Count() int64 { return (<-m.out).count } -// Mark the occurance of n events. +// Mark records the occurance of n events. func (m *StandardMeter) Mark(n int64) { m.in <- n } -// Return the meter's one-minute moving average rate of events. +// Rate1 returns the one-minute moving average rate of events per second. func (m *StandardMeter) Rate1() float64 { return (<-m.out).rate1 } -// Return the meter's five-minute moving average rate of events. +// Rate5 returns the five-minute moving average rate of events per second. func (m *StandardMeter) Rate5() float64 { return (<-m.out).rate5 } -// Return the meter's fifteen-minute moving average rate of events. +// Rate15 returns the fifteen-minute moving average rate of events per second. func (m *StandardMeter) Rate15() float64 { return (<-m.out).rate15 } -// Return the meter's mean rate of events. +// RateMean returns the meter's mean rate of events per second. func (m *StandardMeter) RateMean() float64 { return (<-m.out).rateMean } diff --git a/sample.go b/sample.go index 6ce07f8..042387f 100644 --- a/sample.go +++ b/sample.go @@ -14,13 +14,9 @@ const rescaleThreshold = time.Hour // Samples maintain a statistically-significant selection of values from // a stream. -// -// This is an interface so as to encourage other structs to implement -// the Sample API as appropriate. type Sample interface { Clear() Count() int64 - Dup() Sample Max() int64 Mean() float64 Min() int64 @@ -74,22 +70,6 @@ func (s *ExpDecaySample) Clear() { s.values = make(expDecaySampleHeap, 0, s.reservoirSize) } -// Dup returns a copy of the sample. -func (s *ExpDecaySample) Dup() Sample { - s.mutex.Lock() - defer s.mutex.Unlock() - values := make(expDecaySampleHeap, len(s.values)) - copy(values, s.values) - return &ExpDecaySample{ - alpha: s.alpha, - count: s.count, - reservoirSize: s.reservoirSize, - t0: s.t0, - t1: s.t1, - values: values, - } -} - // Count returns the number of samples recorded, which may exceed the // reservoir size. func (s *ExpDecaySample) Count() int64 { @@ -102,7 +82,7 @@ func (s *ExpDecaySample) Max() int64 { return SampleMax(s.Values()) } -// Return the mean of all values seen since the histogram was last cleared. +// Mean returns the mean of the values in the sample. func (s *ExpDecaySample) Mean() float64 { return SampleMean(s.Values()) } @@ -113,12 +93,13 @@ func (s *ExpDecaySample) Min() int64 { return SampleMin(s.Values()) } -// Percentile returns an arbitrary percentile of sampled values. +// Percentile returns an arbitrary percentile of values in the sample. func (s *ExpDecaySample) Percentile(p float64) float64 { return SamplePercentile(s.Values(), p) } -// Percentiles returns a slice of arbitrary percentiles of sampled values. +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. func (s *ExpDecaySample) Percentiles(ps []float64) []float64 { return SamplePercentiles(s.Values(), ps) } @@ -135,7 +116,7 @@ func (s *ExpDecaySample) StdDev() float64 { return SampleStdDev(s.Values()) } -// Sum returns the sum of the sample. +// Sum returns the sum of the values in the sample. func (s *ExpDecaySample) Sum() int64 { return SampleSum(s.Values()) } @@ -156,7 +137,7 @@ func (s *ExpDecaySample) Values() []int64 { return values } -// Variance returns the variance of the sample. +// Variance returns the variance of the values in the sample. func (s *ExpDecaySample) Variance() float64 { return SampleVariance(s.Values()) } @@ -187,51 +168,48 @@ func (s *ExpDecaySample) update(t time.Time, v int64) { } } -// No-op Sample. +// NilSample is a no-op Sample. type NilSample struct{} -// No-op. +// Clear is a no-op. func (NilSample) Clear() {} -// No-op. +// Count is a no-op. func (NilSample) Count() int64 { return 0 } -// No-op. -func (NilSample) Dup() Sample { return NilSample{} } - -// No-op. +// Max is a no-op. func (NilSample) Max() int64 { return 0 } -// No-op. +// Mean is a no-op. func (NilSample) Mean() float64 { return 0.0 } -// No-op. +// Min is a no-op. func (NilSample) Min() int64 { return 0 } -// No-op. +// Percentile is a no-op. func (NilSample) Percentile(p float64) float64 { return 0.0 } -// No-op. +// Percentiles is a no-op. func (NilSample) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -// No-op. +// Size is a no-op. func (NilSample) Size() int { return 0 } // No-op. func (NilSample) StdDev() float64 { return 0.0 } -// No-op. +// Sum is a no-op. func (NilSample) Sum() int64 { return 0 } -// No-op. +// Update is a no-op. func (NilSample) Update(v int64) {} -// No-op. +// Values is a no-op. func (NilSample) Values() []int64 { return []int64{} } -// No-op. +// Variance is a no-op. func (NilSample) Variance() float64 { return 0.0 } // SampleMax returns the maximum value of the slice of int64. @@ -333,7 +311,8 @@ type UniformSample struct { values []int64 } -// Create a new uniform sample with the given reservoir size. +// NewUniformSample constructs a new uniform sample with the given reservoir +// size. func NewUniformSample(reservoirSize int) Sample { if UseNilMetrics { return NilSample{} @@ -341,7 +320,7 @@ func NewUniformSample(reservoirSize int) Sample { return &UniformSample{reservoirSize: reservoirSize} } -// Clear all samples. +// Clear clears all samples. func (s *UniformSample) Clear() { s.mutex.Lock() defer s.mutex.Unlock() @@ -355,19 +334,6 @@ func (s *UniformSample) Count() int64 { return atomic.LoadInt64(&s.count) } -// Dup returns a copy of the sample. -func (s *UniformSample) Dup() Sample { - s.mutex.Lock() - defer s.mutex.Unlock() - values := make([]int64, len(s.values)) - copy(values, s.values) - return &UniformSample{ - count: s.count, - reservoirSize: s.reservoirSize, - values: values, - } -} - // Max returns the maximum value in the sample, which may not be the maximum // value ever to be part of the sample. func (s *UniformSample) Max() int64 { @@ -376,7 +342,7 @@ func (s *UniformSample) Max() int64 { return SampleMax(s.values) } -// Return the mean of all values seen since the histogram was last cleared. +// Mean returns the mean of the values in the sample. func (s *UniformSample) Mean() float64 { s.mutex.Lock() defer s.mutex.Unlock() @@ -391,40 +357,53 @@ func (s *UniformSample) Min() int64 { return SampleMin(s.values) } -// Percentile returns an arbitrary percentile of sampled values. +// Percentile returns an arbitrary percentile of values in the sample. func (s *UniformSample) Percentile(p float64) float64 { s.mutex.Lock() defer s.mutex.Unlock() return SamplePercentile(s.values, p) } -// Percentiles returns a slice of arbitrary percentiles of sampled values. +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. func (s *UniformSample) Percentiles(ps []float64) []float64 { s.mutex.Lock() defer s.mutex.Unlock() return SamplePercentiles(s.values, ps) } -// Return the size of the sample, which is at most the reservoir size. +// Size returns the size of the sample, which is at most the reservoir size. func (s *UniformSample) Size() int { s.mutex.Lock() defer s.mutex.Unlock() return len(s.values) } -// StdDev returns the standard deviation of the sample. +// Snapshot returns a read-only copy of the sample. +func (s *UniformSample) Snapshot() Sample { + s.mutex.Lock() + defer s.mutex.Unlock() + values := make([]int64, len(s.values)) + copy(values, s.values) + return &SampleSnapshot{ + count: s.count, + values: values, + } +} + +// StdDev returns the standard deviation of the values in the sample. func (s *UniformSample) StdDev() float64 { s.mutex.Lock() defer s.mutex.Unlock() return SampleStdDev(s.values) } -// Sum returns the sum of the sample. +// Sum returns the sum of the values in the sample. func (s *UniformSample) Sum() int64 { return SampleSum(s.values) } -// Update the sample with a new value. +// Update samples a new value. func (s *UniformSample) Update(v int64) { s.mutex.Lock() defer s.mutex.Unlock() @@ -436,7 +415,7 @@ func (s *UniformSample) Update(v int64) { } } -// Return all the values in the sample. +// Values returns a copy of the values in the sample. func (s *UniformSample) Values() []int64 { s.mutex.Lock() defer s.mutex.Unlock() @@ -445,7 +424,7 @@ func (s *UniformSample) Values() []int64 { return values } -// Variance returns the variance of the sample. +// Variance returns the variance of the values in the sample. func (s *UniformSample) Variance() float64 { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/sample_test.go b/sample_test.go index 919ec99..6f13fb6 100644 --- a/sample_test.go +++ b/sample_test.go @@ -144,16 +144,6 @@ func TestExpDecaySample1000(t *testing.T) { } } -func TestExpDecaySampleDup(t *testing.T) { - s1 := NewExpDecaySample(100, 0.99) - s1.Update(1) - s2 := s1.Dup() - s1.Update(1) - if 1 != s2.Size() { - t.Fatal(s2) - } -} - // This test makes sure that the sample's priority is not amplified by using // nanosecond duration since start rather than second duration since start. // The priority becomes +Inf quickly after starting if this is done, diff --git a/timer.go b/timer.go index b859866..1d5862f 100644 --- a/timer.go +++ b/timer.go @@ -1,11 +1,11 @@ package metrics -import "time" +import ( + "sync" + "time" +) // Timers capture the duration and rate of events. -// -// This is an interface so as to encourage other structs to implement -// the Timer API as appropriate. type Timer interface { Count() int64 Max() int64 @@ -21,9 +21,11 @@ type Timer interface { Time(func()) Update(time.Duration) UpdateSince(time.Time) + Variance() float64 } -// Get an existing or create and register a new Timer. +// GetOrRegisterTimer returns an existing Timer or constructs and registers a +// new StandardTimer. func GetOrRegisterTimer(name string, r Registry) Timer { if nil == r { r = DefaultRegistry @@ -31,15 +33,18 @@ func GetOrRegisterTimer(name string, r Registry) Timer { return r.GetOrRegister(name, NewTimer()).(Timer) } -// Create a new timer with the given Histogram and Meter. +// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. func NewCustomTimer(h Histogram, m Meter) Timer { if UseNilMetrics { return NilTimer{} } - return &StandardTimer{h, m} + return &StandardTimer{ + histogram: h, + meter: m, + } } -// Create and register a new Timer. +// NewRegisteredTimer constructs and registers a new StandardTimer. func NewRegisteredTimer(name string, r Registry) Timer { c := NewTimer() if nil == r { @@ -49,128 +54,133 @@ func NewRegisteredTimer(name string, r Registry) Timer { return c } -// Create a new timer with a standard histogram and meter. The histogram -// will use an exponentially-decaying sample with the same reservoir size -// and alpha as UNIX load averages. +// NewTimer constructs a new StandardTimer using an exponentially-decaying +// sample with the same reservoir size and alpha as UNIX load averages. func NewTimer() Timer { if UseNilMetrics { return NilTimer{} } return &StandardTimer{ - NewHistogram(NewExpDecaySample(1028, 0.015)), - NewMeter(), + histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), + meter: NewMeter(), } } -// No-op Timer. +// NilTimer is a no-op Timer. type NilTimer struct { h Histogram m Meter } -// No-op. +// Count is a no-op. func (NilTimer) Count() int64 { return 0 } -// No-op. +// Max is a no-op. func (NilTimer) Max() int64 { return 0 } -// No-op. +// Mean is a no-op. func (NilTimer) Mean() float64 { return 0.0 } -// No-op. +// Min is a no-op. func (NilTimer) Min() int64 { return 0 } -// No-op. +// Percentile is a no-op. func (NilTimer) Percentile(p float64) float64 { return 0.0 } -// No-op. +// Percentiles is a no-op. func (NilTimer) Percentiles(ps []float64) []float64 { return make([]float64, len(ps)) } -// No-op. +// Rate1 is a no-op. func (NilTimer) Rate1() float64 { return 0.0 } -// No-op. +// Rate5 is a no-op. func (NilTimer) Rate5() float64 { return 0.0 } -// No-op. +// Rate15 is a no-op. func (NilTimer) Rate15() float64 { return 0.0 } -// No-op. +// RateMean is a no-op. func (NilTimer) RateMean() float64 { return 0.0 } // No-op. func (NilTimer) StdDev() float64 { return 0.0 } -// No-op. -func (NilTimer) Time(f func()) {} +// Time is a no-op. +func (NilTimer) Time(func()) {} -// No-op. -func (NilTimer) Update(d time.Duration) {} +// Update is a no-op. +func (NilTimer) Update(time.Duration) {} -// No-op. -func (NilTimer) UpdateSince(ts time.Time) {} +// UpdateSince is a no-op. +func (NilTimer) UpdateSince(time.Time) {} + +// Variance is a no-op. +func (NilTimer) Variance() float64 { return 0.0 } -// The standard implementation of a Timer uses a Histogram and Meter directly. +// StandardTimer is the standard implementation of a Timer and uses a Histogram +// and Meter. type StandardTimer struct { - h Histogram - m Meter + histogram Histogram + meter Meter + mutex sync.Mutex } -// Return the count of inputs. +// Count returns the number of events recorded. func (t *StandardTimer) Count() int64 { - return t.h.Count() + return t.histogram.Count() } -// Return the maximal value seen. +// Max returns the maximum value in the sample. func (t *StandardTimer) Max() int64 { - return t.h.Max() + return t.histogram.Max() } -// Return the mean of all values seen. +// Mean returns the mean of the values in the sample. func (t *StandardTimer) Mean() float64 { - return t.h.Mean() + return t.histogram.Mean() } -// Return the minimal value seen. +// Min returns the minimum value in the sample. func (t *StandardTimer) Min() int64 { - return t.h.Min() + return t.histogram.Min() } -// Return an arbitrary percentile of all values seen. +// Percentile returns an arbitrary percentile of the values in the sample. func (t *StandardTimer) Percentile(p float64) float64 { - return t.h.Percentile(p) + return t.histogram.Percentile(p) } -// Return a slice of arbitrary percentiles of all values seen. +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. func (t *StandardTimer) Percentiles(ps []float64) []float64 { - return t.h.Percentiles(ps) + return t.histogram.Percentiles(ps) } -// Return the meter's one-minute moving average rate of events. +// Rate1 returns the one-minute moving average rate of events per second. func (t *StandardTimer) Rate1() float64 { - return t.m.Rate1() + return t.meter.Rate1() } -// Return the meter's five-minute moving average rate of events. +// Rate5 returns the five-minute moving average rate of events per second. func (t *StandardTimer) Rate5() float64 { - return t.m.Rate5() + return t.meter.Rate5() } -// Return the meter's fifteen-minute moving average rate of events. +// Rate15 returns the fifteen-minute moving average rate of events per second. func (t *StandardTimer) Rate15() float64 { - return t.m.Rate15() + return t.meter.Rate15() } -// Return the meter's mean rate of events. +// RateMean returns the meter's mean rate of events per second. func (t *StandardTimer) RateMean() float64 { return t.m.RateMean() } -// Return the standard deviation of all values seen. +// StdDev returns the standard deviation of the values in the sample. func (t *StandardTimer) StdDev() float64 { - return t.h.StdDev() + return t.histogram.StdDev() } // Record the duration of the execution of the given function.