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 years ago
parent
commit
6bd8d76861
10 changed files with 237 additions and 267 deletions
  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"
 import "sync/atomic"
 
 
 // Counters hold an int64 value that can be incremented and decremented.
 // 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 {
 type Counter interface {
 	Clear()
 	Clear()
 	Count() int64
 	Count() int64
 	Dec(int64)
 	Dec(int64)
 	Inc(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 {
 func GetOrRegisterCounter(name string, r Registry) Counter {
 	if nil == r {
 	if nil == r {
 		r = DefaultRegistry
 		r = DefaultRegistry
@@ -21,7 +20,7 @@ func GetOrRegisterCounter(name string, r Registry) Counter {
 	return r.GetOrRegister(name, NewCounter()).(Counter)
 	return r.GetOrRegister(name, NewCounter()).(Counter)
 }
 }
 
 
-// Create a new Counter.
+// NewCounter constructs a new StandardCounter.
 func NewCounter() Counter {
 func NewCounter() Counter {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilCounter{}
 		return NilCounter{}
@@ -29,7 +28,7 @@ func NewCounter() Counter {
 	return &StandardCounter{0}
 	return &StandardCounter{0}
 }
 }
 
 
-// Create and register a new Counter.
+// NewRegisteredCounter constructs and registers a new StandardCounter.
 func NewRegisteredCounter(name string, r Registry) Counter {
 func NewRegisteredCounter(name string, r Registry) Counter {
 	c := NewCounter()
 	c := NewCounter()
 	if nil == r {
 	if nil == r {
@@ -42,16 +41,16 @@ func NewRegisteredCounter(name string, r Registry) Counter {
 // No-op Counter.
 // No-op Counter.
 type NilCounter struct{}
 type NilCounter struct{}
 
 
-// No-op.
+// Clear is a no-op.
 func (NilCounter) Clear() {}
 func (NilCounter) Clear() {}
 
 
-// No-op.
+// Count is a no-op.
 func (NilCounter) Count() int64 { return 0 }
 func (NilCounter) Count() int64 { return 0 }
 
 
-// No-op.
+// Dec is a no-op.
 func (NilCounter) Dec(i int64) {}
 func (NilCounter) Dec(i int64) {}
 
 
-// No-op.
+// Inc is a no-op.
 func (NilCounter) Inc(i int64) {}
 func (NilCounter) Inc(i int64) {}
 
 
 // The standard implementation of a Counter uses the sync/atomic package
 // The standard implementation of a Counter uses the sync/atomic package
@@ -60,22 +59,22 @@ type StandardCounter struct {
 	count int64
 	count int64
 }
 }
 
 
-// Clear the counter: set it to zero.
+// Clear sets the counter to zero.
 func (c *StandardCounter) Clear() {
 func (c *StandardCounter) Clear() {
 	atomic.StoreInt64(&c.count, 0)
 	atomic.StoreInt64(&c.count, 0)
 }
 }
 
 
-// Return the current count.
+// Count returns the current count.
 func (c *StandardCounter) Count() int64 {
 func (c *StandardCounter) Count() int64 {
 	return atomic.LoadInt64(&c.count)
 	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) {
 func (c *StandardCounter) Dec(i int64) {
 	atomic.AddInt64(&c.count, -i)
 	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) {
 func (c *StandardCounter) Inc(i int64) {
 	atomic.AddInt64(&c.count, i)
 	atomic.AddInt64(&c.count, i)
 }
 }

+ 12 - 14
ewma.go

@@ -8,16 +8,14 @@ import (
 
 
 // EWMAs continuously calculate an exponentially-weighted moving average
 // EWMAs continuously calculate an exponentially-weighted moving average
 // based on an outside source of clock ticks.
 // 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 {
 type EWMA interface {
 	Rate() float64
 	Rate() float64
+	Snapshot() EWMA
 	Tick()
 	Tick()
 	Update(int64)
 	Update(int64)
 }
 }
 
 
-// Create a new EWMA with the given alpha.
+// NewEWMA constructs a new EWMA with the given alpha.
 func NewEWMA(alpha float64) EWMA {
 func NewEWMA(alpha float64) EWMA {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilEWMA{}
 		return NilEWMA{}
@@ -25,17 +23,17 @@ func NewEWMA(alpha float64) EWMA {
 	return &StandardEWMA{alpha: alpha}
 	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 {
 func NewEWMA1() EWMA {
 	return NewEWMA(1 - math.Exp(-5.0/60.0/1))
 	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 {
 func NewEWMA5() EWMA {
 	return NewEWMA(1 - math.Exp(-5.0/60.0/5))
 	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 {
 func NewEWMA15() EWMA {
 	return NewEWMA(1 - math.Exp(-5.0/60.0/15))
 	return NewEWMA(1 - math.Exp(-5.0/60.0/15))
 }
 }
@@ -43,18 +41,18 @@ func NewEWMA15() EWMA {
 // No-op EWMA.
 // No-op EWMA.
 type NilEWMA struct{}
 type NilEWMA struct{}
 
 
-// No-op.
+// Rate is a no-op.
 func (NilEWMA) Rate() float64 { return 0.0 }
 func (NilEWMA) Rate() float64 { return 0.0 }
 
 
 // No-op.
 // No-op.
 func (NilEWMA) Tick() {}
 func (NilEWMA) Tick() {}
 
 
-// No-op.
+// Update is a no-op.
 func (NilEWMA) Update(n int64) {}
 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 {
 type StandardEWMA struct {
 	alpha     float64
 	alpha     float64
 	init      bool
 	init      bool
@@ -63,7 +61,7 @@ type StandardEWMA struct {
 	uncounted int64
 	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 {
 func (a *StandardEWMA) Rate() float64 {
 	a.mutex.Lock()
 	a.mutex.Lock()
 	defer a.mutex.Unlock()
 	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) {
 func (a *StandardEWMA) Update(n int64) {
 	atomic.AddInt64(&a.uncounted, n)
 	atomic.AddInt64(&a.uncounted, n)
 }
 }

+ 8 - 10
gauge.go

@@ -3,15 +3,13 @@ package metrics
 import "sync/atomic"
 import "sync/atomic"
 
 
 // Gauges hold an int64 value that can be set arbitrarily.
 // 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 {
 type Gauge interface {
 	Update(int64)
 	Update(int64)
 	Value() 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 {
 func GetOrRegisterGauge(name string, r Registry) Gauge {
 	if nil == r {
 	if nil == r {
 		r = DefaultRegistry
 		r = DefaultRegistry
@@ -19,7 +17,7 @@ func GetOrRegisterGauge(name string, r Registry) Gauge {
 	return r.GetOrRegister(name, NewGauge()).(Gauge)
 	return r.GetOrRegister(name, NewGauge()).(Gauge)
 }
 }
 
 
-// Create a new Gauge.
+// NewGauge constructs a new StandardGauge.
 func NewGauge() Gauge {
 func NewGauge() Gauge {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilGauge{}
 		return NilGauge{}
@@ -27,7 +25,7 @@ func NewGauge() Gauge {
 	return &StandardGauge{0}
 	return &StandardGauge{0}
 }
 }
 
 
-// Create and register a new Gauge.
+// NewRegisteredGauge constructs and registers a new StandardGauge.
 func NewRegisteredGauge(name string, r Registry) Gauge {
 func NewRegisteredGauge(name string, r Registry) Gauge {
 	c := NewGauge()
 	c := NewGauge()
 	if nil == r {
 	if nil == r {
@@ -43,11 +41,11 @@ type NilGauge struct{}
 // No-op.
 // No-op.
 func (NilGauge) Update(v int64) {}
 func (NilGauge) Update(v int64) {}
 
 
-// No-op.
+// Value is a no-op.
 func (NilGauge) Value() int64 { return 0 }
 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 {
 type StandardGauge struct {
 	value int64
 	value int64
 }
 }
@@ -57,7 +55,7 @@ func (g *StandardGauge) Update(v int64) {
 	atomic.StoreInt64(&g.value, v)
 	atomic.StoreInt64(&g.value, v)
 }
 }
 
 
-// Return the gauge's current value.
+// Value returns the gauge's current value.
 func (g *StandardGauge) Value() int64 {
 func (g *StandardGauge) Value() int64 {
 	return atomic.LoadInt64(&g.value)
 	return atomic.LoadInt64(&g.value)
 }
 }

+ 16 - 18
healthcheck.go

@@ -1,9 +1,6 @@
 package metrics
 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 {
 type Healthcheck interface {
 	Check()
 	Check()
 	Error() error
 	Error() error
@@ -11,8 +8,8 @@ type Healthcheck interface {
 	Unhealthy(error)
 	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 {
 func NewHealthcheck(f func(Healthcheck)) Healthcheck {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilHealthcheck{}
 		return NilHealthcheck{}
@@ -20,44 +17,45 @@ func NewHealthcheck(f func(Healthcheck)) Healthcheck {
 	return &StandardHealthcheck{nil, f}
 	return &StandardHealthcheck{nil, f}
 }
 }
 
 
-// No-op Healthcheck.
+// NilHealthcheck is a no-op.
 type NilHealthcheck struct{}
 type NilHealthcheck struct{}
 
 
-// No-op.
+// Check is a no-op.
 func (NilHealthcheck) Check() {}
 func (NilHealthcheck) Check() {}
 
 
-// No-op.
+// Error is a no-op.
 func (NilHealthcheck) Error() error { return nil }
 func (NilHealthcheck) Error() error { return nil }
 
 
-// No-op.
+// Healthy is a no-op.
 func (NilHealthcheck) Healthy() {}
 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 {
 type StandardHealthcheck struct {
 	err error
 	err error
 	f   func(Healthcheck)
 	f   func(Healthcheck)
 }
 }
 
 
-// Update the healthcheck's status.
+// Check runs the healthcheck function to update the healthcheck's status.
 func (h *StandardHealthcheck) Check() {
 func (h *StandardHealthcheck) Check() {
 	h.f(h)
 	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 {
 func (h *StandardHealthcheck) Error() error {
 	return h.err
 	return h.err
 }
 }
 
 
-// Mark the healthcheck as healthy.
+// Healthy marks the healthcheck as healthy.
 func (h *StandardHealthcheck) Healthy() {
 func (h *StandardHealthcheck) Healthy() {
 	h.err = nil
 	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) {
 func (h *StandardHealthcheck) Unhealthy(err error) {
 	h.err = err
 	h.err = err
 }
 }

+ 61 - 59
histogram.go

@@ -6,10 +6,7 @@ import (
 	"sync/atomic"
 	"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 {
 type Histogram interface {
 	Clear()
 	Clear()
 	Count() int64
 	Count() int64
@@ -19,13 +16,14 @@ type Histogram interface {
 	Percentile(float64) float64
 	Percentile(float64) float64
 	Percentiles([]float64) []float64
 	Percentiles([]float64) []float64
 	Sample() Sample
 	Sample() Sample
+	Snapshot() Histogram
 	StdDev() float64
 	StdDev() float64
-	Sum() int64
 	Update(int64)
 	Update(int64)
 	Variance() float64
 	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 {
 func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
 	if nil == r {
 	if nil == r {
 		r = DefaultRegistry
 		r = DefaultRegistry
@@ -33,22 +31,21 @@ func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram {
 	return r.GetOrRegister(name, NewHistogram(s)).(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 {
 func NewHistogram(s Sample) Histogram {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilHistogram{}
 		return NilHistogram{}
 	}
 	}
 	return &StandardHistogram{
 	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 {
 func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
 	c := NewHistogram(s)
 	c := NewHistogram(s)
 	if nil == r {
 	if nil == r {
@@ -61,71 +58,73 @@ func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram {
 // No-op Histogram.
 // No-op Histogram.
 type NilHistogram struct{}
 type NilHistogram struct{}
 
 
-// No-op.
+// Clear is a no-op.
 func (NilHistogram) Clear() {}
 func (NilHistogram) Clear() {}
 
 
-// No-op.
+// Count is a no-op.
 func (NilHistogram) Count() int64 { return 0 }
 func (NilHistogram) Count() int64 { return 0 }
 
 
-// No-op.
+// Max is a no-op.
 func (NilHistogram) Max() int64 { return 0 }
 func (NilHistogram) Max() int64 { return 0 }
 
 
-// No-op.
+// Mean is a no-op.
 func (NilHistogram) Mean() float64 { return 0.0 }
 func (NilHistogram) Mean() float64 { return 0.0 }
 
 
-// No-op.
+// Min is a no-op.
 func (NilHistogram) Min() int64 { return 0 }
 func (NilHistogram) Min() int64 { return 0 }
 
 
-// No-op.
+// Percentile is a no-op.
 func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
 func (NilHistogram) Percentile(p float64) float64 { return 0.0 }
 
 
-// No-op.
+// Percentiles is a no-op.
 func (NilHistogram) Percentiles(ps []float64) []float64 {
 func (NilHistogram) Percentiles(ps []float64) []float64 {
 	return make([]float64, len(ps))
 	return make([]float64, len(ps))
 }
 }
 
 
-// No-op.
+// Sample is a no-op.
 func (NilHistogram) Sample() Sample { return NilSample{} }
 func (NilHistogram) Sample() Sample { return NilSample{} }
 
 
 // No-op.
 // No-op.
 func (NilHistogram) StdDev() float64 { return 0.0 }
 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) {}
 func (NilHistogram) Update(v int64) {}
 
 
-// No-op.
+// Variance is a no-op.
 func (NilHistogram) Variance() float64 { return 0.0 }
 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 {
 type StandardHistogram struct {
-	count, sum, min, max int64
+	count, max, min, sum int64
 	mutex                sync.Mutex
 	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() {
 func (h *StandardHistogram) Clear() {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	defer h.mutex.Unlock()
 	h.count = 0
 	h.count = 0
 	h.max = math.MinInt64
 	h.max = math.MinInt64
 	h.min = math.MaxInt64
 	h.min = math.MaxInt64
-	h.s.Clear()
+	h.sample.Clear()
 	h.sum = 0
 	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 {
 func (h *StandardHistogram) Count() int64 {
 	return atomic.LoadInt64(&h.count)
 	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 {
 func (h *StandardHistogram) Max() int64 {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	defer h.mutex.Unlock()
@@ -135,7 +134,8 @@ func (h *StandardHistogram) Max() int64 {
 	return h.max
 	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 {
 func (h *StandardHistogram) Mean() float64 {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	defer h.mutex.Unlock()
@@ -145,7 +145,7 @@ func (h *StandardHistogram) Mean() float64 {
 	return float64(h.sum) / float64(h.count)
 	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 {
 func (h *StandardHistogram) Min() int64 {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	defer h.mutex.Unlock()
@@ -155,12 +155,13 @@ func (h *StandardHistogram) Min() int64 {
 	return h.min
 	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 {
 func (h *StandardHistogram) Percentile(p float64) float64 {
 	return h.s.Percentile(p)
 	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 {
 func (h *StandardHistogram) Percentiles(ps []float64) []float64 {
 	return h.s.Percentiles(ps)
 	return h.s.Percentiles(ps)
 }
 }
@@ -170,22 +171,17 @@ func (h *StandardHistogram) Sample() Sample {
 	return h.s.Dup()
 	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 {
 func (h *StandardHistogram) StdDev() float64 {
 	return math.Sqrt(h.Variance())
 	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) {
 func (h *StandardHistogram) Update(v int64) {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	defer h.mutex.Unlock()
-	h.s.Update(v)
+	h.sample.Update(v)
 	h.count++
 	h.count++
 	if v < h.min {
 	if v < h.min {
 		h.min = v
 		h.min = v
@@ -195,28 +191,34 @@ func (h *StandardHistogram) Update(v int64) {
 	}
 	}
 	h.sum += v
 	h.sum += v
 	fv := float64(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 {
 	} 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 {
 func (h *StandardHistogram) Variance() float64 {
 	h.mutex.Lock()
 	h.mutex.Lock()
 	defer h.mutex.Unlock()
 	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 {
 	if 1 >= h.count {
 		return 0.0
 		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
 type int64Slice []int64
 
 
 func (p int64Slice) Len() int           { return len(p) }
 func (p int64Slice) Len() int           { return len(p) }

+ 1 - 3
json.go

@@ -1,8 +1,6 @@
 package metrics
 package metrics
 
 
-import (
-	"encoding/json"
-)
+import "encoding/json"
 
 
 // MarshalJSON returns a byte slice containing a JSON representation of all
 // MarshalJSON returns a byte slice containing a JSON representation of all
 // the metrics in the Registry.
 // 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
 // Meters count events to produce exponentially-weighted moving average rates
 // at one-, five-, and fifteen-minutes and a mean rate.
 // 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 {
 type Meter interface {
 	Count() int64
 	Count() int64
 	Mark(int64)
 	Mark(int64)
@@ -16,7 +13,8 @@ type Meter interface {
 	RateMean() float64
 	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 {
 func GetOrRegisterMeter(name string, r Registry) Meter {
 	if nil == r {
 	if nil == r {
 		r = DefaultRegistry
 		r = DefaultRegistry
@@ -24,22 +22,22 @@ func GetOrRegisterMeter(name string, r Registry) Meter {
 	return r.GetOrRegister(name, NewMeter()).(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 {
 func NewMeter() Meter {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilMeter{}
 		return NilMeter{}
 	}
 	}
 	m := &StandardMeter{
 	m := &StandardMeter{
 		make(chan int64),
 		make(chan int64),
-		make(chan meterV),
+		make(chan *MeterSnapshot),
 		time.NewTicker(5e9),
 		time.NewTicker(5e9),
 	}
 	}
 	go m.arbiter()
 	go m.arbiter()
 	return m
 	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 {
 func NewRegisteredMeter(name string, r Registry) Meter {
 	c := NewMeter()
 	c := NewMeter()
 	if nil == r {
 	if nil == r {
@@ -52,22 +50,22 @@ func NewRegisteredMeter(name string, r Registry) Meter {
 // No-op Meter.
 // No-op Meter.
 type NilMeter struct{}
 type NilMeter struct{}
 
 
-// No-op.
+// Count is a no-op.
 func (NilMeter) Count() int64 { return 0 }
 func (NilMeter) Count() int64 { return 0 }
 
 
-// No-op.
+// Mark is a no-op.
 func (NilMeter) Mark(n int64) {}
 func (NilMeter) Mark(n int64) {}
 
 
-// No-op.
+// Rate1 is a no-op.
 func (NilMeter) Rate1() float64 { return 0.0 }
 func (NilMeter) Rate1() float64 { return 0.0 }
 
 
-// No-op.
+// Rate5 is a no-op.
 func (NilMeter) Rate5() float64 { return 0.0 }
 func (NilMeter) Rate5() float64 { return 0.0 }
 
 
-// No-op.
+// Rate15is a no-op.
 func (NilMeter) Rate15() float64 { return 0.0 }
 func (NilMeter) Rate15() float64 { return 0.0 }
 
 
-// No-op.
+// RateMean is a no-op.
 func (NilMeter) RateMean() float64 { return 0.0 }
 func (NilMeter) RateMean() float64 { return 0.0 }
 
 
 // The standard implementation of a Meter uses a goroutine to synchronize
 // The standard implementation of a Meter uses a goroutine to synchronize
@@ -79,32 +77,32 @@ type StandardMeter struct {
 	ticker *time.Ticker
 	ticker *time.Ticker
 }
 }
 
 
-// Return the count of events seen.
+// Count returns the number of events recorded.
 func (m *StandardMeter) Count() int64 {
 func (m *StandardMeter) Count() int64 {
 	return (<-m.out).count
 	return (<-m.out).count
 }
 }
 
 
-// Mark the occurance of n events.
+// Mark records the occurance of n events.
 func (m *StandardMeter) Mark(n int64) {
 func (m *StandardMeter) Mark(n int64) {
 	m.in <- n
 	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 {
 func (m *StandardMeter) Rate1() float64 {
 	return (<-m.out).rate1
 	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 {
 func (m *StandardMeter) Rate5() float64 {
 	return (<-m.out).rate5
 	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 {
 func (m *StandardMeter) Rate15() float64 {
 	return (<-m.out).rate15
 	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 {
 func (m *StandardMeter) RateMean() float64 {
 	return (<-m.out).rateMean
 	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
 // Samples maintain a statistically-significant selection of values from
 // a stream.
 // a stream.
-//
-// This is an interface so as to encourage other structs to implement
-// the Sample API as appropriate.
 type Sample interface {
 type Sample interface {
 	Clear()
 	Clear()
 	Count() int64
 	Count() int64
-	Dup() Sample
 	Max() int64
 	Max() int64
 	Mean() float64
 	Mean() float64
 	Min() int64
 	Min() int64
@@ -74,22 +70,6 @@ func (s *ExpDecaySample) Clear() {
 	s.values = make(expDecaySampleHeap, 0, s.reservoirSize)
 	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
 // Count returns the number of samples recorded, which may exceed the
 // reservoir size.
 // reservoir size.
 func (s *ExpDecaySample) Count() int64 {
 func (s *ExpDecaySample) Count() int64 {
@@ -102,7 +82,7 @@ func (s *ExpDecaySample) Max() int64 {
 	return SampleMax(s.Values())
 	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 {
 func (s *ExpDecaySample) Mean() float64 {
 	return SampleMean(s.Values())
 	return SampleMean(s.Values())
 }
 }
@@ -113,12 +93,13 @@ func (s *ExpDecaySample) Min() int64 {
 	return SampleMin(s.Values())
 	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 {
 func (s *ExpDecaySample) Percentile(p float64) float64 {
 	return SamplePercentile(s.Values(), p)
 	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 {
 func (s *ExpDecaySample) Percentiles(ps []float64) []float64 {
 	return SamplePercentiles(s.Values(), ps)
 	return SamplePercentiles(s.Values(), ps)
 }
 }
@@ -135,7 +116,7 @@ func (s *ExpDecaySample) StdDev() float64 {
 	return SampleStdDev(s.Values())
 	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 {
 func (s *ExpDecaySample) Sum() int64 {
 	return SampleSum(s.Values())
 	return SampleSum(s.Values())
 }
 }
@@ -156,7 +137,7 @@ func (s *ExpDecaySample) Values() []int64 {
 	return values
 	return values
 }
 }
 
 
-// Variance returns the variance of the sample.
+// Variance returns the variance of the values in the sample.
 func (s *ExpDecaySample) Variance() float64 {
 func (s *ExpDecaySample) Variance() float64 {
 	return SampleVariance(s.Values())
 	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{}
 type NilSample struct{}
 
 
-// No-op.
+// Clear is a no-op.
 func (NilSample) Clear() {}
 func (NilSample) Clear() {}
 
 
-// No-op.
+// Count is a no-op.
 func (NilSample) Count() int64 { return 0 }
 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 }
 func (NilSample) Max() int64 { return 0 }
 
 
-// No-op.
+// Mean is a no-op.
 func (NilSample) Mean() float64 { return 0.0 }
 func (NilSample) Mean() float64 { return 0.0 }
 
 
-// No-op.
+// Min is a no-op.
 func (NilSample) Min() int64 { return 0 }
 func (NilSample) Min() int64 { return 0 }
 
 
-// No-op.
+// Percentile is a no-op.
 func (NilSample) Percentile(p float64) float64 { return 0.0 }
 func (NilSample) Percentile(p float64) float64 { return 0.0 }
 
 
-// No-op.
+// Percentiles is a no-op.
 func (NilSample) Percentiles(ps []float64) []float64 {
 func (NilSample) Percentiles(ps []float64) []float64 {
 	return make([]float64, len(ps))
 	return make([]float64, len(ps))
 }
 }
 
 
-// No-op.
+// Size is a no-op.
 func (NilSample) Size() int { return 0 }
 func (NilSample) Size() int { return 0 }
 
 
 // No-op.
 // No-op.
 func (NilSample) StdDev() float64 { return 0.0 }
 func (NilSample) StdDev() float64 { return 0.0 }
 
 
-// No-op.
+// Sum is a no-op.
 func (NilSample) Sum() int64 { return 0 }
 func (NilSample) Sum() int64 { return 0 }
 
 
-// No-op.
+// Update is a no-op.
 func (NilSample) Update(v int64) {}
 func (NilSample) Update(v int64) {}
 
 
-// No-op.
+// Values is a no-op.
 func (NilSample) Values() []int64 { return []int64{} }
 func (NilSample) Values() []int64 { return []int64{} }
 
 
-// No-op.
+// Variance is a no-op.
 func (NilSample) Variance() float64 { return 0.0 }
 func (NilSample) Variance() float64 { return 0.0 }
 
 
 // SampleMax returns the maximum value of the slice of int64.
 // SampleMax returns the maximum value of the slice of int64.
@@ -333,7 +311,8 @@ type UniformSample struct {
 	values        []int64
 	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 {
 func NewUniformSample(reservoirSize int) Sample {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilSample{}
 		return NilSample{}
@@ -341,7 +320,7 @@ func NewUniformSample(reservoirSize int) Sample {
 	return &UniformSample{reservoirSize: reservoirSize}
 	return &UniformSample{reservoirSize: reservoirSize}
 }
 }
 
 
-// Clear all samples.
+// Clear clears all samples.
 func (s *UniformSample) Clear() {
 func (s *UniformSample) Clear() {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
@@ -355,19 +334,6 @@ func (s *UniformSample) Count() int64 {
 	return atomic.LoadInt64(&s.count)
 	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
 // Max returns the maximum value in the sample, which may not be the maximum
 // value ever to be part of the sample.
 // value ever to be part of the sample.
 func (s *UniformSample) Max() int64 {
 func (s *UniformSample) Max() int64 {
@@ -376,7 +342,7 @@ func (s *UniformSample) Max() int64 {
 	return SampleMax(s.values)
 	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 {
 func (s *UniformSample) Mean() float64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
@@ -391,40 +357,53 @@ func (s *UniformSample) Min() int64 {
 	return SampleMin(s.values)
 	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 {
 func (s *UniformSample) Percentile(p float64) float64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
 	return SamplePercentile(s.values, p)
 	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 {
 func (s *UniformSample) Percentiles(ps []float64) []float64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
 	return SamplePercentiles(s.values, ps)
 	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 {
 func (s *UniformSample) Size() int {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
 	return len(s.values)
 	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 {
 func (s *UniformSample) StdDev() float64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
 	return SampleStdDev(s.values)
 	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 {
 func (s *UniformSample) Sum() int64 {
 	return SampleSum(s.values)
 	return SampleSum(s.values)
 }
 }
 
 
-// Update the sample with a new value.
+// Update samples a new value.
 func (s *UniformSample) Update(v int64) {
 func (s *UniformSample) Update(v int64) {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	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 {
 func (s *UniformSample) Values() []int64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	defer s.mutex.Unlock()
@@ -445,7 +424,7 @@ func (s *UniformSample) Values() []int64 {
 	return values
 	return values
 }
 }
 
 
-// Variance returns the variance of the sample.
+// Variance returns the variance of the values in the sample.
 func (s *UniformSample) Variance() float64 {
 func (s *UniformSample) Variance() float64 {
 	s.mutex.Lock()
 	s.mutex.Lock()
 	defer s.mutex.Unlock()
 	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
 // This test makes sure that the sample's priority is not amplified by using
 // nanosecond duration since start rather than second duration since start.
 // nanosecond duration since start rather than second duration since start.
 // The priority becomes +Inf quickly after starting if this is done,
 // The priority becomes +Inf quickly after starting if this is done,

+ 64 - 54
timer.go

@@ -1,11 +1,11 @@
 package metrics
 package metrics
 
 
-import "time"
+import (
+	"sync"
+	"time"
+)
 
 
 // Timers capture the duration and rate of events.
 // 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 {
 type Timer interface {
 	Count() int64
 	Count() int64
 	Max() int64
 	Max() int64
@@ -21,9 +21,11 @@ type Timer interface {
 	Time(func())
 	Time(func())
 	Update(time.Duration)
 	Update(time.Duration)
 	UpdateSince(time.Time)
 	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 {
 func GetOrRegisterTimer(name string, r Registry) Timer {
 	if nil == r {
 	if nil == r {
 		r = DefaultRegistry
 		r = DefaultRegistry
@@ -31,15 +33,18 @@ func GetOrRegisterTimer(name string, r Registry) Timer {
 	return r.GetOrRegister(name, NewTimer()).(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 {
 func NewCustomTimer(h Histogram, m Meter) Timer {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilTimer{}
 		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 {
 func NewRegisteredTimer(name string, r Registry) Timer {
 	c := NewTimer()
 	c := NewTimer()
 	if nil == r {
 	if nil == r {
@@ -49,128 +54,133 @@ func NewRegisteredTimer(name string, r Registry) Timer {
 	return c
 	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 {
 func NewTimer() Timer {
 	if UseNilMetrics {
 	if UseNilMetrics {
 		return NilTimer{}
 		return NilTimer{}
 	}
 	}
 	return &StandardTimer{
 	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 {
 type NilTimer struct {
 	h Histogram
 	h Histogram
 	m Meter
 	m Meter
 }
 }
 
 
-// No-op.
+// Count is a no-op.
 func (NilTimer) Count() int64 { return 0 }
 func (NilTimer) Count() int64 { return 0 }
 
 
-// No-op.
+// Max is a no-op.
 func (NilTimer) Max() int64 { return 0 }
 func (NilTimer) Max() int64 { return 0 }
 
 
-// No-op.
+// Mean is a no-op.
 func (NilTimer) Mean() float64 { return 0.0 }
 func (NilTimer) Mean() float64 { return 0.0 }
 
 
-// No-op.
+// Min is a no-op.
 func (NilTimer) Min() int64 { return 0 }
 func (NilTimer) Min() int64 { return 0 }
 
 
-// No-op.
+// Percentile is a no-op.
 func (NilTimer) Percentile(p float64) float64 { return 0.0 }
 func (NilTimer) Percentile(p float64) float64 { return 0.0 }
 
 
-// No-op.
+// Percentiles is a no-op.
 func (NilTimer) Percentiles(ps []float64) []float64 {
 func (NilTimer) Percentiles(ps []float64) []float64 {
 	return make([]float64, len(ps))
 	return make([]float64, len(ps))
 }
 }
 
 
-// No-op.
+// Rate1 is a no-op.
 func (NilTimer) Rate1() float64 { return 0.0 }
 func (NilTimer) Rate1() float64 { return 0.0 }
 
 
-// No-op.
+// Rate5 is a no-op.
 func (NilTimer) Rate5() float64 { return 0.0 }
 func (NilTimer) Rate5() float64 { return 0.0 }
 
 
-// No-op.
+// Rate15 is a no-op.
 func (NilTimer) Rate15() float64 { return 0.0 }
 func (NilTimer) Rate15() float64 { return 0.0 }
 
 
-// No-op.
+// RateMean is a no-op.
 func (NilTimer) RateMean() float64 { return 0.0 }
 func (NilTimer) RateMean() float64 { return 0.0 }
 
 
 // No-op.
 // No-op.
 func (NilTimer) StdDev() float64 { return 0.0 }
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 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 {
 func (t *StandardTimer) RateMean() float64 {
 	return t.m.RateMean()
 	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 {
 func (t *StandardTimer) StdDev() float64 {
-	return t.h.StdDev()
+	return t.histogram.StdDev()
 }
 }
 
 
 // Record the duration of the execution of the given function.
 // Record the duration of the execution of the given function.