Ver código fonte

Merge branch 'observer-effect'

Conflicts:
	ewma.go
Richard Crowley 12 anos atrás
pai
commit
8ed1d3723e
9 arquivos alterados com 311 adições e 78 exclusões
  1. 26 5
      counter.go
  2. 36 18
      ewma.go
  3. 20 5
      gauge.go
  4. 27 6
      healthcheck.go
  5. 53 12
      histogram.go
  6. 46 19
      meter.go
  7. 7 0
      metrics.go
  8. 26 2
      sample.go
  9. 70 11
      timer.go

+ 26 - 5
counter.go

@@ -13,6 +13,32 @@ type Counter interface {
 	Inc(int64)
 }
 
+// Create a new Counter.
+func NewCounter() Counter {
+	if UseNilMetrics {
+		return NilCounter{}
+	}
+	return &StandardCounter{0}
+}
+
+// No-op Counter.
+type NilCounter struct{}
+
+// Force the compiler to check that NilCounter implements Counter.
+var _ Counter = NilCounter{}
+
+// No-op.
+func (c NilCounter) Clear() {}
+
+// No-op.
+func (c NilCounter) Count() int64 { return 0 }
+
+// No-op.
+func (c NilCounter) Dec(i int64) {}
+
+// No-op.
+func (c NilCounter) Inc(i int64) {}
+
 // The standard implementation of a Counter uses the sync/atomic package
 // to manage a single int64 value.
 type StandardCounter struct {
@@ -22,11 +48,6 @@ type StandardCounter struct {
 // Force the compiler to check that StandardCounter implements Counter.
 var _ Counter = &StandardCounter{}
 
-// Create a new counter.
-func NewCounter() *StandardCounter {
-	return &StandardCounter{0}
-}
-
 // Clear the counter: set it to zero.
 func (c *StandardCounter) Clear() {
 	atomic.StoreInt64(&c.count, 0)

+ 36 - 18
ewma.go

@@ -17,40 +17,58 @@ type EWMA interface {
 	Update(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.
-type StandardEWMA struct {
-	alpha     float64
-	rate      float64
-	uncounted int64
-	init      bool
-	mutex     sync.Mutex
-}
-
-// Force the compiler to check that StandardEWMA implements EWMA.
-var _ EWMA = &StandardEWMA{}
-
 // Create a new EWMA with the given alpha.
-func NewEWMA(alpha float64) *StandardEWMA {
+func NewEWMA(alpha float64) EWMA {
+	if UseNilMetrics {
+		return NilEWMA{}
+	}
 	return &StandardEWMA{alpha: alpha}
 }
 
 // Create a new EWMA with alpha set for a one-minute moving average.
-func NewEWMA1() *StandardEWMA {
+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.
-func NewEWMA5() *StandardEWMA {
+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.
-func NewEWMA15() *StandardEWMA {
+func NewEWMA15() EWMA {
 	return NewEWMA(1 - math.Exp(-5.0/60.0/15))
 }
 
+// No-op EWMA.
+type NilEWMA struct{}
+
+// Force the compiler to check that NilEWMA implements EWMA.
+var _ EWMA = NilEWMA{}
+
+// No-op.
+func (a NilEWMA) Rate() float64 { return 0.0 }
+
+// No-op.
+func (a NilEWMA) Tick() {}
+
+// No-op.
+func (a 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.
+type StandardEWMA struct {
+	alpha     float64
+	init      bool
+	mutex     sync.Mutex
+	rate      float64
+	uncounted int64
+}
+
+// Force the compiler to check that StandardEWMA implements EWMA.
+var _ EWMA = &StandardEWMA{}
+
 // Return the moving average rate of events per second.
 func (a *StandardEWMA) Rate() float64 {
 	a.mutex.Lock()

+ 20 - 5
gauge.go

@@ -11,6 +11,26 @@ type Gauge interface {
 	Value() int64
 }
 
+// Create a new Gauge.
+func NewGauge() Gauge {
+	if UseNilMetrics {
+		return NilGauge{}
+	}
+	return &StandardGauge{0}
+}
+
+// No-op Gauge.
+type NilGauge struct{}
+
+// Force the compiler to check that NilGauge implements Gauge.
+var _ Gauge = NilGauge{}
+
+// No-op.
+func (g NilGauge) Update(v int64) {}
+
+// No-op.
+func (g NilGauge) Value() int64 { return 0 }
+
 // The standard implementation of a Gauge uses the sync/atomic package
 // to manage a single int64 value.
 type StandardGauge struct {
@@ -20,11 +40,6 @@ type StandardGauge struct {
 // Force the compiler to check that StandardGauge implements Gauge.
 var _ Gauge = &StandardGauge{}
 
-// Create a new gauge.
-func NewGauge() *StandardGauge {
-	return &StandardGauge{0}
-}
-
 // Update the gauge's value.
 func (g *StandardGauge) Update(v int64) {
 	atomic.StoreInt64(&g.value, v)

+ 27 - 6
healthcheck.go

@@ -11,6 +11,33 @@ type Healthcheck interface {
 	Unhealthy(error)
 }
 
+// Create a new Healthcheck, which will use the given function to update
+// its status.
+func NewHealthcheck(f func(Healthcheck)) Healthcheck {
+	if UseNilMetrics {
+		return NilHealthcheck{}
+	}
+	return &StandardHealthcheck{nil, f}
+}
+
+// No-op Healthcheck.
+type NilHealthcheck struct{}
+
+// Force the compiler to check that NilHealthcheck implements Healthcheck.
+var _ Healthcheck = NilHealthcheck{}
+
+// No-op.
+func (h NilHealthcheck) Check() {}
+
+// No-op.
+func (h NilHealthcheck) Error() error { return nil }
+
+// No-op.
+func (h NilHealthcheck) Healthy() {}
+
+// No-op.
+func (h NilHealthcheck) Unhealthy(err error) {}
+
 // The standard implementation of a Healthcheck stores the status and a
 // function to call to update the status.
 type StandardHealthcheck struct {
@@ -21,12 +48,6 @@ type StandardHealthcheck struct {
 // Force the compiler to check that StandardHealthcheck implements Healthcheck.
 var _ Healthcheck = &StandardHealthcheck{}
 
-// Create a new healthcheck, which will use the given function to update
-// its status.
-func NewHealthcheck(f func(Healthcheck)) *StandardHealthcheck {
-	return &StandardHealthcheck{nil, f}
-}
-
 // Update the healthcheck's status.
 func (h *StandardHealthcheck) Check() {
 	h.f(h)

+ 53 - 12
histogram.go

@@ -24,6 +24,59 @@ type Histogram interface {
 	Variance() float64
 }
 
+// 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.
+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},
+	}
+}
+
+// No-op Histogram.
+type NilHistogram struct{}
+
+// Force the compiler to check that NilHistogram implements Histogram.
+var _ Histogram = NilHistogram{}
+
+// No-op.
+func (h NilHistogram) Clear() {}
+
+// No-op.
+func (h NilHistogram) Count() int64 { return 0 }
+
+// No-op.
+func (h NilHistogram) Max() int64 { return 0 }
+
+// No-op.
+func (h NilHistogram) Mean() float64 { return 0.0 }
+
+// No-op.
+func (h NilHistogram) Min() int64 { return 0 }
+
+// No-op.
+func (h NilHistogram) Percentile(p float64) float64 { return 0.0 }
+
+// No-op.
+func (h NilHistogram) Percentiles(ps []float64) []float64 {
+	return make([]float64, len(ps))
+}
+
+// No-op.
+func (h NilHistogram) StdDev() float64 { return 0.0 }
+
+// No-op.
+func (h NilHistogram) Update(v int64) {}
+
+// No-op.
+func (h NilHistogram) Variance() float64 { return 0.0 }
+
 // The standard implementation of a Histogram uses a Sample and a goroutine
 // to synchronize its calculations.
 type StandardHistogram struct {
@@ -36,18 +89,6 @@ type StandardHistogram struct {
 // Force the compiler to check that StandardHistogram implements Histogram.
 var _ Histogram = &StandardHistogram{}
 
-// 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.
-func NewHistogram(s Sample) *StandardHistogram {
-	return &StandardHistogram{
-		max:      math.MinInt64,
-		min:      math.MaxInt64,
-		s:        s,
-		variance: [2]float64{-1.0, 0.0},
-	}
-}
-
 // Clear the histogram.
 func (h *StandardHistogram) Clear() {
 	h.mutex.Lock()

+ 46 - 19
meter.go

@@ -16,6 +16,45 @@ type Meter interface {
 	RateMean() float64
 }
 
+// Create a new Meter.  Create the communication channels and start the
+// synchronizing goroutine.
+func NewMeter() Meter {
+	if UseNilMetrics {
+		return NilMeter{}
+	}
+	m := &StandardMeter{
+		make(chan int64),
+		make(chan meterV),
+		time.NewTicker(5e9),
+	}
+	go m.arbiter()
+	return m
+}
+
+// No-op Meter.
+type NilMeter struct{}
+
+// Force the compiler to check that NilMeter implements Meter.
+var _ Meter = NilMeter{}
+
+// No-op.
+func (m NilMeter) Count() int64 { return 0 }
+
+// No-op.
+func (m NilMeter) Mark(n int64) {}
+
+// No-op.
+func (m NilMeter) Rate1() float64 { return 0.0 }
+
+// No-op.
+func (m NilMeter) Rate5() float64 { return 0.0 }
+
+// No-op.
+func (m NilMeter) Rate15() float64 { return 0.0 }
+
+// No-op.
+func (m NilMeter) RateMean() float64 { return 0.0 }
+
 // The standard implementation of a Meter uses a goroutine to synchronize
 // its calculations and another goroutine (via time.Ticker) to produce
 // clock ticks.
@@ -28,25 +67,6 @@ type StandardMeter struct {
 // Force the compiler to check that StandardMeter implements Meter.
 var _ Meter = &StandardMeter{}
 
-// A meterV contains all the values that would need to be passed back
-// from the synchronizing goroutine.
-type meterV struct {
-	count                          int64
-	rate1, rate5, rate15, rateMean float64
-}
-
-// Create a new meter.  Create the communication channels and start the
-// synchronizing goroutine.
-func NewMeter() *StandardMeter {
-	m := &StandardMeter{
-		make(chan int64),
-		make(chan meterV),
-		time.NewTicker(5e9),
-	}
-	go m.arbiter()
-	return m
-}
-
 // Return the count of events seen.
 func (m *StandardMeter) Count() int64 {
 	return (<-m.out).count
@@ -109,3 +129,10 @@ func (m *StandardMeter) arbiter() {
 		}
 	}
 }
+
+// A meterV contains all the values that would need to be passed back
+// from the synchronizing goroutine.
+type meterV struct {
+	count                          int64
+	rate1, rate5, rate15, rateMean float64
+}

+ 7 - 0
metrics.go

@@ -4,3 +4,10 @@
 //
 // Coda Hale's original work: <https://github.com/codahale/metrics>
 package metrics
+
+// UseNilMetrics is checked by the constructor functions for all of the
+// standard metrics.  If it is true, the metric returned is a stub.
+//
+// This global kill-switch helps quantify the observer effect and makes
+// for less cluttered pprof profiles.
+var UseNilMetrics bool = false

+ 26 - 2
sample.go

@@ -40,7 +40,10 @@ var _ Sample = &ExpDecaySample{}
 
 // Create a new exponentially-decaying sample with the given reservoir size
 // and alpha.
-func NewExpDecaySample(reservoirSize int, alpha float64) *ExpDecaySample {
+func NewExpDecaySample(reservoirSize int, alpha float64) Sample {
+	if UseNilMetrics {
+		return NilSample{}
+	}
 	s := &ExpDecaySample{
 		alpha:         alpha,
 		reservoirSize: reservoirSize,
@@ -103,6 +106,24 @@ func (s *ExpDecaySample) Values() []int64 {
 	return values
 }
 
+// No-op Sample.
+type NilSample struct{}
+
+// Force the compiler to check that ExpDecaySample implements Sample.
+var _ Sample = NilSample{}
+
+// No-op.
+func (s NilSample) Clear() {}
+
+// No-op.
+func (s NilSample) Size() int { return 0 }
+
+// No-op.
+func (s NilSample) Update(v int64) {}
+
+// No-op.
+func (s NilSample) Values() []int64 { return []int64{} }
+
 // A uniform sample using Vitter's Algorithm R.
 //
 // <http://www.cs.umd.edu/~samir/498/vitter.pdf>
@@ -113,7 +134,10 @@ type UniformSample struct {
 }
 
 // Create a new uniform sample with the given reservoir size.
-func NewUniformSample(reservoirSize int) *UniformSample {
+func NewUniformSample(reservoirSize int) Sample {
+	if UseNilMetrics {
+		return NilSample{}
+	}
 	return &UniformSample{reservoirSize: reservoirSize}
 }
 

+ 70 - 11
timer.go

@@ -23,30 +23,89 @@ type Timer interface {
 	UpdateSince(time.Time)
 }
 
-// The standard implementation of a Timer uses a Histogram and Meter directly.
-type StandardTimer struct {
-	h Histogram
-	m Meter
-}
-
-// Force the compiler to check that StandardTimer implements Timer.
-var _ Timer = &StandardTimer{}
-
 // Create a new timer with the given Histogram and Meter.
-func NewCustomTimer(h Histogram, m Meter) *StandardTimer {
+func NewCustomTimer(h Histogram, m Meter) Timer {
+	if UseNilMetrics {
+		return NilTimer{}
+	}
 	return &StandardTimer{h, m}
 }
 
 // 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.
-func NewTimer() *StandardTimer {
+func NewTimer() Timer {
+	if UseNilMetrics {
+		return NilTimer{}
+	}
 	return &StandardTimer{
 		NewHistogram(NewExpDecaySample(1028, 0.015)),
 		NewMeter(),
 	}
 }
 
+// No-op Timer.
+type NilTimer struct {
+	h Histogram
+	m Meter
+}
+
+// Force the compiler to check that NilTimer implements Timer.
+var _ Timer = NilTimer{}
+
+// No-op.
+func (t NilTimer) Count() int64 { return 0 }
+
+// No-op.
+func (t NilTimer) Max() int64 { return 0 }
+
+// No-op.
+func (t NilTimer) Mean() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) Min() int64 { return 0 }
+
+// No-op.
+func (t NilTimer) Percentile(p float64) float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) Percentiles(ps []float64) []float64 {
+	return make([]float64, len(ps))
+}
+
+// No-op.
+func (t NilTimer) Rate1() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) Rate5() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) Rate15() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) RateMean() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) StdDev() float64 { return 0.0 }
+
+// No-op.
+func (t NilTimer) Time(f func()) {}
+
+// No-op.
+func (t NilTimer) Update(d time.Duration) {}
+
+// No-op.
+func (t NilTimer) UpdateSince(ts time.Time) {}
+
+// The standard implementation of a Timer uses a Histogram and Meter directly.
+type StandardTimer struct {
+	h Histogram
+	m Meter
+}
+
+// Force the compiler to check that StandardTimer implements Timer.
+var _ Timer = &StandardTimer{}
+
 // Return the count of inputs.
 func (t *StandardTimer) Count() int64 {
 	return t.h.Count()