Browse Source

Mostly documentation and naming changes.

Undoubtedly this commit doesn't build or test but it's part of a larger series of intertwined changes.
Richard Crowley 12 năm trước cách đây
mục cha
commit
6bd8d76861
10 tập tin đã thay đổi với 237 bổ sung267 xóa
  1. 13 14
      counter.go
  2. 12 14
      ewma.go
  3. 8 10
      gauge.go
  4. 16 18
      healthcheck.go
  5. 61 59
      histogram.go
  6. 1 3
      json.go
  7. 18 20
      meter.go
  8. 44 65
      sample.go
  9. 0 10
      sample_test.go
  10. 64 54
      timer.go

+ 13 - 14
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)
 }

+ 12 - 14
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)
 }

+ 8 - 10
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)
 }

+ 16 - 18
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
 }

+ 61 - 59
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) }

+ 1 - 3
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.

+ 18 - 20
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
 }

+ 44 - 65
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()

+ 0 - 10
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,

+ 64 - 54
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.