From 7e3f7461ef5f81202ae533c860822abd6e9eb883 Mon Sep 17 00:00:00 2001 From: Matthew Pound Date: Fri, 22 May 2020 14:16:31 -0700 Subject: [PATCH] add ability to expose go-metrics as sfxclient.Collector --- go-metrics/exporter.go | 91 +++++++++++++++++++++++++++++++++++++ go-metrics/exporter_test.go | 24 ++++++++++ go.mod | 1 + go.sum | 2 + 4 files changed, 118 insertions(+) create mode 100644 go-metrics/exporter.go create mode 100644 go-metrics/exporter_test.go diff --git a/go-metrics/exporter.go b/go-metrics/exporter.go new file mode 100644 index 0000000..3957d16 --- /dev/null +++ b/go-metrics/exporter.go @@ -0,0 +1,91 @@ +package go_metrics + +import ( + "github.com/rcrowley/go-metrics" + "github.com/signalfx/golib/v3/datapoint" + "github.com/signalfx/golib/v3/sfxclient" +) + +var defaultQuantiles = []float64{.25, .5, .9, .99} + +// Exporter wraps the go-metrics registry as an sfxclient.Collector +type Exporter struct { + r metrics.Registry + lastSize int + dims map[string]string +} + +func (e *Exporter) metricToDatapoints(dps []*datapoint.Datapoint, name string, i interface{}) []*datapoint.Datapoint { + switch metric := i.(type) { + case metrics.Counter: + dps = append(dps, sfxclient.Counter(name, e.dims, metric.Count())) + + case metrics.Gauge: + dps = append(dps, sfxclient.Gauge(name, e.dims, metric.Value())) + + case metrics.GaugeFloat64: + dps = append(dps, sfxclient.GaugeF(name, e.dims, metric.Value())) + + case metrics.Histogram: + h := metric.Snapshot() + dps = e.harvestHistoTypeThing(dps, name, h) + + case metrics.Meter: + m := metric.Snapshot() + dps = append(dps, + sfxclient.Counter(name+".count", e.dims, m.Count()), + sfxclient.GaugeF(name+".1m", e.dims, m.Rate1()), + sfxclient.GaugeF(name+".5m", e.dims, m.Rate5()), + sfxclient.GaugeF(name+".15m", e.dims, m.Rate15()), + sfxclient.GaugeF(name+".meanRate", e.dims, m.RateMean()), + ) + + case metrics.Timer: + t := metric.Snapshot() + dps = e.harvestHistoTypeThing(dps, name, t) + } + return dps +} + +type histoTypeThing interface { + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + StdDev() float64 + Sum() int64 + Variance() float64 +} + +func (e *Exporter) harvestHistoTypeThing(dps []*datapoint.Datapoint, name string, h histoTypeThing) []*datapoint.Datapoint { + ps := h.Percentiles(defaultQuantiles) + dps = append(dps, + sfxclient.Counter(name+".count", e.dims, h.Count()), + sfxclient.Gauge(name+".min", e.dims, h.Min()), + sfxclient.Gauge(name+".max", e.dims, h.Max()), + sfxclient.GaugeF(name+".mean", e.dims, h.Mean()), + sfxclient.GaugeF(name+".stdDev", e.dims, h.StdDev()), + sfxclient.GaugeF(name+".p25", e.dims, ps[0]), + sfxclient.GaugeF(name+".median", e.dims, ps[1]), + sfxclient.GaugeF(name+".p90", e.dims, ps[2]), + sfxclient.GaugeF(name+".p99", e.dims, ps[3]), + ) + return dps +} + +// Datapoints implements sfxclient.Collector +func (e *Exporter) Datapoints() []*datapoint.Datapoint { + dps := make([]*datapoint.Datapoint, 0, e.lastSize) + e.r.Each(func(name string, i interface{}) { + dps = e.metricToDatapoints(dps, name, i) + }) + e.lastSize = len(dps) + return dps +} + +// New returns a new &Exporter +func New(r metrics.Registry, dims map[string]string) *Exporter { + return &Exporter{r: r, dims: dims} +} diff --git a/go-metrics/exporter_test.go b/go-metrics/exporter_test.go new file mode 100644 index 0000000..dbae877 --- /dev/null +++ b/go-metrics/exporter_test.go @@ -0,0 +1,24 @@ +package go_metrics + +import ( + "testing" + + "github.com/rcrowley/go-metrics" + . "github.com/smartystreets/goconvey/convey" +) + +func Test(t *testing.T) { + Convey("test the go-metrics exporter", t, func() { + r := metrics.NewRegistry() + e := New(r, map[string]string{"foo": "bar"}) + r.Register("timer", metrics.NewTimer()) + r.Register("histo", metrics.NewHistogram(metrics.NewUniformSample(10))) + r.Register("gague", metrics.NewGauge()) + r.Register("f64", metrics.NewGaugeFloat64()) + r.Register("meter", metrics.NewMeter()) + r.Register("counter", metrics.NewCounter()) + + dps := e.Datapoints() + So(len(dps), ShouldEqual, 26) + }) +} diff --git a/go.mod b/go.mod index 8c0ec21..6e38688 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/juju/testing v0.0.0-20191001232224-ce9dec17d28b // indirect github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e github.com/opentracing/opentracing-go v1.1.0 + github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75 github.com/shirou/gopsutil v2.18.10+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect diff --git a/go.sum b/go.sum index 5776dc2..2fe045a 100644 --- a/go.sum +++ b/go.sum @@ -71,6 +71,8 @@ github.com/pavius/impi v0.0.0-20180302134524-c1cbdcb8df2b h1:yS0+/i6mwRZCdssUd+M github.com/pavius/impi v0.0.0-20180302134524-c1cbdcb8df2b/go.mod h1:x/hU0bfdWIhuOT1SKwiJg++yvkk6EuOtJk8WtDZqgr8= 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-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75 h1:cA+Ubq9qEVIQhIWvP2kNuSZ2CmnfBJFSRq+kO1pu2cc= github.com/samuel/go-zookeeper v0.0.0-20190810000440-0ceca61e4d75/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=