forked from Lotto24/go-datadog
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from matlockx/master
adds statsd reporter
- Loading branch information
Showing
3 changed files
with
190 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,10 @@ | ||
module github.com/eSailors/go-datadog | ||
module github.com/flachnetz/go-datadog | ||
|
||
require ( | ||
github.com/DataDog/datadog-go v2.2.0+incompatible | ||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 | ||
github.com/kr/pretty v0.1.0 // indirect | ||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 | ||
github.com/stretchr/testify v1.3.0 // indirect | ||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,20 @@ | ||
github.com/DataDog/datadog-go v2.2.0+incompatible h1:V5BKkxACZLjzHjSgBbr2gvLA2Ae49yhc6CSY7MLy5k4= | ||
github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= | ||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= | ||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI= | ||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98= | ||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= | ||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= | ||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165 h1:nkcn14uNmFEuGCb2mBZbBb24RdNRL08b/wb+xBOYpuk= | ||
github.com/rcrowley/go-metrics v0.0.0-20180503174638-e2704e165165/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= | ||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= | ||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= | ||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
package datadog | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"runtime" | ||
"strconv" | ||
"time" | ||
|
||
"github.com/DataDog/datadog-go/statsd" | ||
metrics "github.com/rcrowley/go-metrics" | ||
) | ||
|
||
// ReporterOption is function-option used during the construction of a *Reporter | ||
type ReporterOption func(*StatsDReporter) error | ||
|
||
// Reporter represents a metrics registry, and the statsd client the metrics | ||
// will be flushed to | ||
type StatsDReporter struct { | ||
// Registry matrices that need to be reported to the Client | ||
Registry metrics.Registry | ||
|
||
// Client is the configured statsd instance | ||
Client *statsd.Client | ||
|
||
// Time interval between two consecutive Flush calls to store the matrix | ||
// value to the Client. | ||
interval time.Duration | ||
|
||
// Reporter type configuration settings | ||
tags []string | ||
ss map[string]int64 | ||
|
||
// Optional parameters | ||
percentiles []float64 | ||
p []string | ||
} | ||
|
||
// NewReporter creates a new Reporter with a pre-configured statsd client. | ||
func NewReporter(r metrics.Registry, client *statsd.Client, d time.Duration, options ...ReporterOption) (*StatsDReporter, error) { | ||
if r == nil { | ||
r = metrics.DefaultRegistry | ||
} | ||
|
||
percentiles := []float64{0.75, 0.95, 0.99, 0.999} | ||
percentileNames, err := getPercentileNames(percentiles) | ||
if err != nil { | ||
return nil, err | ||
} | ||
reporter := &StatsDReporter{ | ||
Client: client, | ||
Registry: r, | ||
percentiles: percentiles, | ||
p: percentileNames, | ||
interval: d, | ||
ss: make(map[string]int64), | ||
} | ||
for _, option := range options { | ||
if err := option(reporter); err != nil { | ||
return nil, err | ||
} | ||
} | ||
return reporter, nil | ||
} | ||
|
||
func getPercentileNames(percentiles []float64) ([]string, error) { | ||
names := make([]string, len(percentiles)) | ||
for i, percentile := range percentiles { | ||
if percentile <= 0 || percentile >= 1 { | ||
return nil, fmt.Errorf("Percentile must lie in interval (0,1)") | ||
} | ||
names[i] = ".percentile." + strconv.FormatFloat(percentile, 'f', -1, 64)[2:] | ||
} | ||
return names, nil | ||
} | ||
|
||
// Flush is a blocking exporter function which reports metrics in the registry | ||
// to the statsd client, flushing every d duration | ||
func (r *StatsDReporter) Flush() { | ||
defer func() { | ||
if rec := recover(); rec != nil { | ||
handlePanic(rec) | ||
} | ||
}() | ||
|
||
for range time.Tick(r.interval) { | ||
if err := r.FlushOnce(); err != nil { | ||
log.Println(err) | ||
} | ||
} | ||
} | ||
|
||
// FlushOnce submits a snapshot submission of the registry to DataDog. This can | ||
// be used in a loop similarly to FlushWithInterval for custom error handling or | ||
// data submission variations. | ||
func (r *StatsDReporter) FlushOnce() error { | ||
r.Registry.Each(func(name string, i interface{}) { | ||
switch metric := i.(type) { | ||
case metrics.Counter: | ||
v := metric.Count() | ||
l := r.ss[name] | ||
r.Client.Count(name+".count", v-l, r.tags, 1) | ||
r.ss[name] = v | ||
|
||
case metrics.Gauge: | ||
r.Client.Gauge(name+".value", float64(metric.Value()), r.tags, 1) | ||
|
||
case metrics.GaugeFloat64: | ||
r.Client.Gauge(name+".value", metric.Value(), r.tags, 1) | ||
|
||
case metrics.Histogram: | ||
ms := metric.Snapshot() | ||
|
||
r.Client.Gauge(name+".count", float64(ms.Count()), r.tags, 1) | ||
r.Client.Gauge(name+".max", float64(ms.Max()), r.tags, 1) | ||
r.Client.Gauge(name+".min", float64(ms.Min()), r.tags, 1) | ||
r.Client.Gauge(name+".mean", ms.Mean(), r.tags, 1) | ||
r.Client.Gauge(name+".stddev", ms.StdDev(), r.tags, 1) | ||
r.Client.Gauge(name+".median", time.Duration(ms.Percentile(0.5)).Seconds()*1000, r.tags, 1) | ||
|
||
if len(r.percentiles) > 0 { | ||
values := ms.Percentiles(r.percentiles) | ||
for i, p := range r.p { | ||
r.Client.Gauge(name+p, values[i], r.tags, 1) | ||
} | ||
} | ||
|
||
case metrics.Meter: | ||
ms := metric.Snapshot() | ||
|
||
r.Client.Gauge(name+".count", float64(ms.Count()), r.tags, 1) | ||
r.Client.Gauge(name+".rate.1min", ms.Rate1(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.5min", ms.Rate5(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.15min", ms.Rate15(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.mean", ms.RateMean(), r.tags, 1) | ||
|
||
case metrics.Timer: | ||
ms := metric.Snapshot() | ||
|
||
r.Client.Gauge(name+".count", float64(ms.Count()), r.tags, 1) | ||
r.Client.Gauge(name+".max", time.Duration(ms.Max()).Seconds()*1000, r.tags, 1) | ||
r.Client.Gauge(name+".min", time.Duration(ms.Min()).Seconds()*1000, r.tags, 1) | ||
r.Client.Gauge(name+".mean", time.Duration(ms.Mean()).Seconds()*1000, r.tags, 1) | ||
r.Client.Gauge(name+".stddev", time.Duration(ms.StdDev()).Seconds()*1000, r.tags, 1) | ||
|
||
r.Client.Gauge(name+".median", time.Duration(ms.Percentile(0.5)).Seconds()*1000, r.tags, 1) | ||
|
||
r.Client.Gauge(name+".rate.1min", ms.Rate1(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.5min", ms.Rate5(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.15min", ms.Rate15(), r.tags, 1) | ||
r.Client.Gauge(name+".rate.mean", ms.RateMean(), r.tags, 1) | ||
|
||
if len(r.percentiles) > 0 { | ||
values := ms.Percentiles(r.percentiles) | ||
for i, p := range r.p { | ||
r.Client.Gauge(name+p, time.Duration(values[i]).Seconds()*1000, r.tags, 1) | ||
} | ||
} | ||
} | ||
}) | ||
|
||
return nil | ||
} | ||
|
||
func handlePanic(rec interface{}) { | ||
callers := "" | ||
for i := 2; true; i++ { | ||
_, file, line, ok := runtime.Caller(i) | ||
if !ok { | ||
break | ||
} | ||
callers = callers + fmt.Sprintf("%v:%v\n", file, line) | ||
} | ||
log.Printf("Recovered from panic: %#v \n%v", rec, callers) | ||
} |