瀏覽代碼

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 年之前
父節點
當前提交
6bd8d76861
共有 10 個文件被更改,包括 237 次插入267 次删除
  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.