README

✔️ tally GoDoc Build Status Coverage Status

Fast, buffered, hierarchical stats collection in Go.

Installation

go get -u github.com/uber-go/tally

Abstract

Tally provides a common interface for emitting metrics, while letting you not worry about the velocity of metrics emission.

By default it buffers counters and gauges at a specified interval but does not buffer timer values. This is primarily so timer values can have all their values sampled if desired and if not they can be sampled as histograms.

Structure

  • Scope: Keeps track of metrics, and their common metadata.
  • Metrics: Counters, Gauges, Timers.
  • Reporter: Implemented by you. Accepts aggregated values from the scope. Forwards the aggregated values to your metrics ingestion pipeline.
Acquire a Scope
reporter = NewMyStatsReporter()  // Implement as you will
tags := map[string]string{
	"dc": "east-1",
	"type": "master",
}
reportEvery := 1 * time.Second
scope := tally.NewRootScope("some_prefix", tags, reporter, reportEvery, tally.DefaultSeparator)
Get/Create a metric, use it
// Get a counter, increment a counter
reqCounter := scope.Counter("requests")  // cache me
reqCounter.Inc(1)

queueGauge := scope.Gauge("queue_length")  // cache me
queueGauge.Update(42)
Report your metrics

Use the inbuilt statsd reporter:

import (
	"github.com/cactus/go-statsd-client/statsd"
	"github.com/uber-go/tally"
	tallystatsd "github.com/uber-go/tally/statsd"
	// ...
)

client, err := statsd.NewClient("statsd.aggregator.local:1234", "")
// ...

opts := tallystatsd.NewOptions().SetSampleRate(1.0)
reporter = tallystatsd.NewStatsdReporter(client, opts)
tags := map[string]string{
	"dc": "east-1",
	"type": "master",
}
reportEvery := 1 * time.Second
scope := tally.NewRootScope("some_prefix", tags, reporter, reportEvery, tally.DefaultSeparator)

Implement your own reporter using the StatsReporter interface:

type StatsReporter interface {
	// ReportCounter reports a counter value
	ReportCounter(name string, tags map[string]string, value int64)

	// ReportGauge reports a gauge value
	ReportGauge(name string, tags map[string]string, value float64)

	// ReportTimer reports a timer value
	ReportTimer(name string, tags map[string]string, interval time.Duration)

	// Capabilities returns a description of metrics reporting capabilities
	Capabilities() Capabilities

	// Flush is expected to be called by a Scope when it completes a round or reporting
	Flush()
}

Or implement your own metrics implementation that matches the tally Scope interface to use different buffering semantics:

type Scope interface {
	// Counter returns the Counter object corresponding to the name
	Counter(name string) Counter

	// Gauge returns the Gauge object corresponding to the name
	Gauge(name string) Gauge

	// Timer returns the Timer object corresponding to the name
	Timer(name string) Timer

	// Tagged returns a new child scope with the given tags and current tags
	Tagged(tags map[string]string) Scope

	// SubScope returns a new child scope appending a further name prefix
	SubScope(name string) Scope

	// Capabilities returns a description of metrics reporting capabilities
	Capabilities() Capabilities
}

Performance

This stuff needs to be fast. With that in mind, we avoid locks and unnecessary memory allocations.

BenchmarkCounterInc-8               	200000000	         7.68 ns/op
BenchmarkReportCounterNoData-8      	300000000	         4.88 ns/op
BenchmarkReportCounterWithData-8    	100000000	        21.6 ns/op
BenchmarkGaugeSet-8                 	100000000	        16.0 ns/op
BenchmarkReportGaugeNoData-8        	100000000	        10.4 ns/op
BenchmarkReportGaugeWithData-8      	50000000	        27.6 ns/op
BenchmarkTimerInterval-8            	50000000	        37.7 ns/op
BenchmarkTimerReport-8              	300000000	         5.69 ns/op

Released under the [MIT License](LICENSE).
Expand ▾ Collapse ▴

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// NoopScope is a scope that does nothing
	NoopScope, _ = NewRootScope("", nil, NullStatsReporter, 0, "")
	// DefaultSeparator is the default separator used to join nested scopes
	DefaultSeparator = "."
)

Functions

func KeyForPrefixedStringMap

func KeyForPrefixedStringMap(
	prefix string,
	stringMap map[string]string,
) string

KeyForPrefixedStringMap generates a unique key for a a prefix and a map string set combination.

func KeyForStringMap

func KeyForStringMap(
	stringMap map[string]string,
) string

KeyForStringMap generates a unique key for a map string set combination.

Types

type BaseStatsReporter

type BaseStatsReporter interface {
	Capabilities() Capabilities
	Flush()
}

BaseStatsReporter implements the shared reporter methods

type CachedCount

type CachedCount interface {
	ReportCount(value int64)
}

CachedCount interface for reporting an individual counter

type CachedGauge

type CachedGauge interface {
	ReportGauge(value float64)
}

CachedGauge interface for reporting an individual gauge

type CachedStatsReporter

type CachedStatsReporter interface {
	BaseStatsReporter

	// AllocateCounter pre allocates a counter data structure with name & tags.
	AllocateCounter(name string, tags map[string]string) CachedCount

	// AllocateGauge pre allocates a gauge data structure with name & tags.
	AllocateGauge(name string, tags map[string]string) CachedGauge

	// AllocateTimer pre allocates a timer data structure with name & tags.
	AllocateTimer(name string, tags map[string]string) CachedTimer
}

CachedStatsReporter is a backend for Scopes that pre allocates all counter, gauges & timers. This is harder to implement but more performant

type CachedTimer

type CachedTimer interface {
	ReportTimer(interval time.Duration)
}

CachedTimer interface for reporting an individual timer

type Capabilities

type Capabilities interface {
	// Reporting returns whether the reporter has the ability to actively report
	Reporting() bool

	// Tagging returns whether the reporter has the capability for tagged metrics
	Tagging() bool
}

Capabilities is a description of metrics reporting capabilities

type Counter

type Counter interface {
	// Inc increments the counter by a delta
	Inc(delta int64)
}

Counter is the interface for emitting counter type metrics

type CounterSnapshot

type CounterSnapshot interface {
	// Name returns the name
	Name() string

	// Tags returns the tags
	Tags() map[string]string

	// Value returns the value
	Value() int64
}

CounterSnapshot is a snapshot of a counter

type Gauge

type Gauge interface {
	// Update sets the gauges absolute value
	Update(value float64)
}

Gauge is the interface for emitting gauge metrics

type GaugeSnapshot

type GaugeSnapshot interface {
	// Name returns the name
	Name() string

	// Tags returns the tags
	Tags() map[string]string

	// Value returns the value
	Value() float64
}

GaugeSnapshot is a snapshot of a gauge

type ObjectPool

type ObjectPool struct {

	// contains filtered or unexported fields

}

ObjectPool is an minimalistic object pool to avoid any circular dependencies on any other object pool.

func NewObjectPool

func NewObjectPool(size int) *ObjectPool

NewObjectPool creates a new pool.

func (*ObjectPool) Get

func (p *ObjectPool) Get() interface{}

Get gets an object from the pool.

func (*ObjectPool) Init

func (p *ObjectPool) Init(alloc func() interface{})

Init initializes the object pool.

func (*ObjectPool) Put

func (p *ObjectPool) Put(obj interface{})

Put puts an object back to the pool.

type Scope

type Scope interface {
	// Counter returns the Counter object corresponding to the name
	Counter(name string) Counter

	// Gauge returns the Gauge object corresponding to the name
	Gauge(name string) Gauge

	// Timer returns the Timer object corresponding to the name
	Timer(name string) Timer

	// Tagged returns a new child scope with the given tags and current tags
	Tagged(tags map[string]string) Scope

	// SubScope returns a new child scope appending a further name prefix
	SubScope(name string) Scope

	// Capabilities returns a description of metrics reporting capabilities
	Capabilities() Capabilities
}

Scope is a namespace wrapper around a stats reporter, ensuring that all emitted values have a given prefix or set of tags

func NewCachedRootScope

func NewCachedRootScope(
	prefix string,
	tags map[string]string,
	reporter CachedStatsReporter,
	interval time.Duration,
	separator string,
) (Scope, io.Closer)

NewCachedRootScope creates a new Scope using a more performant cached stats reporter with the given prefix

func NewRootScope

func NewRootScope(
	prefix string,
	tags map[string]string,
	reporter StatsReporter,
	interval time.Duration,
	separator string,
) (Scope, io.Closer)

NewRootScope creates a new Scope around a given stats reporter with the given prefix

type Snapshot

type Snapshot interface {
	// Counters returns a snapshot of all counter summations since last report execution
	Counters() map[string]CounterSnapshot

	// Gauges returns a snapshot of gauge last values since last report execution
	Gauges() map[string]GaugeSnapshot

	// Timers returns a snapshot of timer values since last report execution
	Timers() map[string]TimerSnapshot
}

Snapshot is a snapshot of values since last report execution

type StatsReporter

type StatsReporter interface {
	BaseStatsReporter

	// ReportCounter reports a counter value
	ReportCounter(name string, tags map[string]string, value int64)

	// ReportGauge reports a gauge value
	ReportGauge(name string, tags map[string]string, value float64)

	// ReportTimer reports a timer value
	ReportTimer(name string, tags map[string]string, interval time.Duration)
}

StatsReporter is a backend for Scopes to report metrics to

var NullStatsReporter StatsReporter = nullStatsReporter{}

NullStatsReporter is an implementatin of StatsReporter than simply does nothing.

type Stopwatch

type Stopwatch struct {

	// contains filtered or unexported fields

}

Stopwatch is a helper convenience struct for nicer tracking of elapsed time

func (Stopwatch) Stop

func (s Stopwatch) Stop() time.Duration

Stop records the difference between the current clock and startTime

type TestScope

type TestScope interface {
	Scope

	// Snapshot returns a copy of all values since the last report execution,
	// this is an expensive operation and should only be use for testing purposes
	Snapshot() Snapshot
}

TestScope is a metrics collector that has no reporting, ensuring that all emitted values have a given prefix or set of tags

func NewTestScope

func NewTestScope(
	prefix string,
	tags map[string]string,
) TestScope

NewTestScope creates a new Scope without a stats reporter with the given prefix and adds the ability to take snapshots of metrics emitted to it

type Timer

type Timer interface {
	// Record a specific duration directly
	Record(time.Duration)

	// Start gives you back a specific point in time to report via Stop()
	Start() Stopwatch
}

Timer is the interface for emitting timer metrics

type TimerSnapshot

type TimerSnapshot interface {
	// Name returns the name
	Name() string

	// Tags returns the tags
	Tags() map[string]string

	// Values returns the values
	Values() []time.Duration
}

TimerSnapshot is a snapshot of a timer