1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
|
# :heavy_check_mark: tally [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
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, gauges and histograms 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 summaries or histograms independently by a reporter.
## Structure
- Scope: Keeps track of metrics, and their common metadata.
- Metrics: Counters, Gauges, Timers and Histograms.
- Reporter: Implemented by you. Accepts aggregated values from the scope. Forwards the aggregated values to your metrics ingestion pipeline.
- The reporters already available listed alphabetically are:
- `github.com/uber-go/tally/m3`: Report m3 metrics, timers are not sampled and forwarded directly.
- `github.com/uber-go/tally/multi`: Report to multiple reporters, you can multi-write metrics to other reporters simply.
- `github.com/uber-go/tally/prometheus`: Report prometheus metrics, timers by default are made summaries with an option to make them histograms instead.
- `github.com/uber-go/tally/statsd`: Report statsd metrics, no support for tags.
### Basics
- Scopes created with tally provide race-safe registration and use of all metric types `Counter`, `Gauge`, `Timer`, `Histogram`.
- `NewRootScope(...)` returns a `Scope` and `io.Closer`, the second return value is used to stop the scope's goroutine reporting values from the scope to it's reporter. This is to reduce the footprint of `Scope` from the public API for those implementing it themselves to use in Go packages that take a tally `Scope`.
### Acquire a Scope ###
```go
reporter = NewMyStatsReporter() // Implement as you will
tags := map[string]string{
"dc": "east-1",
"type": "master",
}
reportEvery := time.Second
scope := tally.NewRootScope(tally.ScopeOptions{
Tags: tags,
Reporter: reporter,
}, reportEvery)
```
### Get/Create a metric, use it ###
```go
// 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:
```go
import (
"io"
"github.com/cactus/go-statsd-client/v5/statsd"
"github.com/uber-go/tally"
tallystatsd "github.com/uber-go/tally/statsd"
// ...
)
func newScope() (tally.Scope, io.Closer) {
statter, _ := statsd.NewBufferedClient("127.0.0.1:8125",
"stats", 100*time.Millisecond, 1440)
reporter := tallystatsd.NewReporter(statter, tallystatsd.Options{
SampleRate: 1.0,
})
scope, closer := tally.NewRootScope(tally.ScopeOptions{
Prefix: "my-service",
Tags: map[string]string{},
Reporter: reporter,
}, time.Second)
return scope, closer
}
```
Implement your own reporter using the `StatsReporter` interface:
```go
// BaseStatsReporter implements the shared reporter methods.
type BaseStatsReporter interface {
Capabilities() Capabilities
Flush()
}
// StatsReporter is a backend for Scopes to report metrics to.
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,
)
// ReportHistogramValueSamples reports histogram samples for a bucket
ReportHistogramValueSamples(
name string,
tags map[string]string,
buckets Buckets,
bucketLowerBound,
bucketUpperBound float64,
samples int64,
)
// ReportHistogramDurationSamples reports histogram samples for a bucket
ReportHistogramDurationSamples(
name string,
tags map[string]string,
buckets Buckets,
bucketLowerBound,
bucketUpperBound time.Duration,
samples int64,
)
}
```
Or implement your own metrics implementation that matches the tally `Scope` interface to use different buffering semantics:
```go
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
// Histogram returns the Histogram object corresponding to the name.
// To use default value and duration buckets configured for the scope
// simply pass tally.DefaultBuckets or nil.
// You can use tally.ValueBuckets{x, y, ...} for value buckets.
// You can use tally.DurationBuckets{x, y, ...} for duration buckets.
// You can use tally.MustMakeLinearValueBuckets(start, width, count) for linear values.
// You can use tally.MustMakeLinearDurationBuckets(start, width, count) for linear durations.
// You can use tally.MustMakeExponentialValueBuckets(start, factor, count) for exponential values.
// You can use tally.MustMakeExponentialDurationBuckets(start, factor, count) for exponential durations.
Histogram(name string, buckets Buckets) Histogram
// 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
}
// Capabilities is a description of metrics reporting 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
}
```
## 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
```
<hr>
Released under the [MIT License](LICENSE).
[doc-img]: https://godoc.org/github.com/uber-go/tally?status.svg
[doc]: https://godoc.org/github.com/uber-go/tally
[ci-img]: https://travis-ci.org/uber-go/tally.svg?branch=master
[ci]: https://travis-ci.org/uber-go/tally
[cov-img]: https://coveralls.io/repos/github/uber-go/tally/badge.svg?branch=master
[cov]: https://coveralls.io/github/uber-go/tally?branch=master
[glide.lock]: https://github.com/uber-go/tally/blob/master/glide.lock
[v1]: https://github.com/uber-go/tally/milestones
|