diff --git a/util/metric/metric.go b/util/metric/metric.go index 33d5f538c8ac..0e7e92b384d2 100644 --- a/util/metric/metric.go +++ b/util/metric/metric.go @@ -18,6 +18,7 @@ package metric import ( "encoding/json" + "fmt" "sync" "time" @@ -77,6 +78,20 @@ type Histogram struct { nextT time.Time } +// NewHistogram creates a new windowed HDRHistogram with the given parameters. +// Data is kept in the active window for approximately the given duration. +// See the the documentation for hdrhistogram.WindowedHistogram for details. +func NewHistogram(duration time.Duration, maxVal int64, sigFigs int) *Histogram { + const n = 4 + h := &Histogram{} + h.maxVal = int64(maxVal) + h.interval = duration / n + h.nextT = time.Now() + + h.windowed = hdrhistogram.NewWindowed(n, 0, h.maxVal, sigFigs) + return h +} + func (h *Histogram) tick() { h.nextT = h.nextT.Add(h.interval) h.windowed.Rotate() @@ -127,6 +142,11 @@ type Counter struct { metrics.Counter } +// NewCounter creates a counter. +func NewCounter() *Counter { + return &Counter{metrics.NewCounter()} +} + // Each calls the given closure with the empty string and itself. func (c *Counter) Each(f func(string, interface{})) { f("", c) } @@ -140,6 +160,12 @@ type Gauge struct { metrics.Gauge } +// NewGauge creates a Gauge. +func NewGauge() *Gauge { + g := &Gauge{metrics.NewGauge()} + return g +} + // Each calls the given closure with the empty string and itself. func (g *Gauge) Each(f func(string, interface{})) { f("", g) } @@ -157,6 +183,22 @@ type Rate struct { nextT time.Time } +// NewRate creates an EWMA rate on the given timescale. Timescales at +// or below 2s are illegal and will cause a panic. +func NewRate(timescale time.Duration) *Rate { + const tickInterval = time.Second + if timescale <= 2*time.Second { + panic(fmt.Sprintf("EWMA with per-second ticks makes no sense on timescale %s", timescale)) + } + avgAge := float64(timescale) / float64(2*tickInterval) + + return &Rate{ + interval: tickInterval, + nextT: time.Now(), + wrapped: ewma.NewMovingAverage(avgAge), + } +} + func (e *Rate) nextTick() time.Time { return e.nextT } diff --git a/util/metric/registry.go b/util/metric/registry.go index 60108da2d379..afaf0f765735 100644 --- a/util/metric/registry.go +++ b/util/metric/registry.go @@ -22,10 +22,6 @@ import ( "fmt" "sync" "time" - - "github.com/VividCortex/ewma" - "github.com/codahale/hdrhistogram" - "github.com/rcrowley/go-metrics" ) // A Registry bundles up various iterables (i.e. typically metrics or other @@ -46,6 +42,9 @@ func NewRegistry() *Registry { // string. The individual items in the registry will be formatted via // fmt.Sprintf(format, ). As a special case, *Registry implements // Iterable and can thus be added. +// Metric types in this package have helpers that allow them to be created +// and registered in a single step. Add is called manually only when adding +// a registry to another, or when integrating metrics defined elsewhere. func (r *Registry) Add(format string, item Iterable) error { r.Lock() defer r.Unlock() @@ -87,17 +86,11 @@ func (r *Registry) MarshalJSON() ([]byte, error) { return json.Marshal(m) } -// Histogram registers a new windowed HDRHistogram with the given -// parameters. Data is kept in the active window for approximately the given -// duration. -func (r *Registry) Histogram(name string, duration time.Duration, maxVal int64, sigFigs int) *Histogram { - const n = 4 - h := &Histogram{} - h.maxVal = int64(maxVal) - h.interval = duration / n - h.nextT = time.Now() - - h.windowed = hdrhistogram.NewWindowed(n, 0, h.maxVal, sigFigs) +// Histogram registers a new windowed HDRHistogram with the given parameters. +// Data is kept in the active window for approximately the given duration. +func (r *Registry) Histogram(name string, duration time.Duration, maxVal int64, + sigFigs int) *Histogram { + h := NewHistogram(duration, maxVal, sigFigs) r.MustAdd(name, h) return h } @@ -117,34 +110,24 @@ func (r *Registry) Latency(prefix string) Histograms { return hs } -// Counter registers a new counter under the given name. +// Counter registers new counter to the registry. func (r *Registry) Counter(name string) *Counter { - c := &Counter{metrics.NewCounter()} + c := NewCounter() r.MustAdd(name, c) return c } // Gauge registers a new Gauge with the given name. func (r *Registry) Gauge(name string) *Gauge { - g := &Gauge{metrics.NewGauge()} + g := NewGauge() r.MustAdd(name, g) return g } -// Rate registers an EWMA rate over the given timescale. Timescales at -// or below 2s are illegal and will cause a panic. +// Rate creates an EWMA rate over the given timescale. The comments on NewRate +// apply. func (r *Registry) Rate(name string, timescale time.Duration) *Rate { - const tickInterval = time.Second - if timescale <= 2*time.Second { - panic(fmt.Sprintf("EWMA with per-second ticks makes no sense on timescale %s", timescale)) - } - avgAge := float64(timescale) / float64(2*tickInterval) - - e := &Rate{ - interval: tickInterval, - nextT: time.Now(), - wrapped: ewma.NewMovingAverage(avgAge), - } + e := NewRate(timescale) r.MustAdd(name, e) return e }