Skip to content

Commit

Permalink
Merge pull request #3 from dreamboxlearning/feature/add-per-metric-tag
Browse files Browse the repository at this point in the history
Add per metric tags
  • Loading branch information
syntaqx authored Jul 19, 2021
2 parents 76992c9 + e340705 commit 19b86b3
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 25 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@

# Dependency directories
vendor/

#IntelliJ
.idea/
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,5 @@ require (
github.com/DataDog/datadog-go v4.6.0+incompatible
github.com/Microsoft/go-winio v0.5.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475
github.com/stretchr/testify v1.7.0 // indirect
github.com/stretchr/testify v1.7.0
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
67 changes: 43 additions & 24 deletions reporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package datadog
import (
"fmt"
"log"
"regexp"
"runtime"
"strings"
"time"

"github.com/DataDog/datadog-go/statsd"
Expand All @@ -14,6 +16,10 @@ const (
defaultFlushInterval = time.Second * 10
)

// Expect the tags in the pattern
// namespace.metricName[tag1:value1,tag2:value2,etc....]
var tagPattern = regexp.MustCompile("([\\w\\.]+)\\[([\\w\\W]+)\\]")

// Reporter wraps a metrics registry with a given statsd client.
type Reporter struct {
// Registry matrices that need to be reported to the Client
Expand Down Expand Up @@ -79,61 +85,63 @@ func (r *Reporter) Flush() {
// be used in a loop similarly to FlushWithInterval for custom error handling or
// data submission variations.
func (r *Reporter) FlushOnce() error {
r.Registry.Each(func(name string, i interface{}) {
r.Registry.Each(func(metricName string, i interface{}) {
name, tags := r.splitNameAndTags(metricName)

switch metric := i.(type) {
case metrics.Counter:
v := metric.Count()
l := r.ss[name]
r.Client.Count(name, v-l, r.tags, 1)
r.Client.Count(name, v-l, tags, 1)
r.ss[name] = v

case metrics.Gauge:
r.Client.Gauge(name, float64(metric.Value()), r.tags, 1)
r.Client.Gauge(name, float64(metric.Value()), tags, 1)

case metrics.GaugeFloat64:
r.Client.Gauge(name, metric.Value(), r.tags, 1)
r.Client.Gauge(name, metric.Value(), 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+".sum", float64(ms.Sum()), r.tags, 1)
r.Client.Gauge(name+".var", ms.Variance(), r.tags, 1)
r.Client.Gauge(name+".count", float64(ms.Count()), tags, 1)
r.Client.Gauge(name+".max", float64(ms.Max()), tags, 1)
r.Client.Gauge(name+".min", float64(ms.Min()), tags, 1)
r.Client.Gauge(name+".mean", ms.Mean(), tags, 1)
r.Client.Gauge(name+".stddev", ms.StdDev(), tags, 1)
r.Client.Gauge(name+".sum", float64(ms.Sum()), tags, 1)
r.Client.Gauge(name+".var", ms.Variance(), 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)
r.Client.Gauge(name+p, values[i], tags, 1)
}
}

case metrics.Meter:
ms := metric.Snapshot()

r.Client.Gauge(name+".count", float64(ms.Count()), r.tags, 1)
r.Client.Gauge(name+".rate1", ms.Rate1(), r.tags, 1)
r.Client.Gauge(name+".rate5", ms.Rate5(), r.tags, 1)
r.Client.Gauge(name+".rate15", ms.Rate15(), r.tags, 1)
r.Client.Gauge(name+".mean", ms.RateMean(), r.tags, 1)
r.Client.Gauge(name+".count", float64(ms.Count()), tags, 1)
r.Client.Gauge(name+".rate1", ms.Rate1(), tags, 1)
r.Client.Gauge(name+".rate5", ms.Rate5(), tags, 1)
r.Client.Gauge(name+".rate15", ms.Rate15(), tags, 1)
r.Client.Gauge(name+".mean", ms.RateMean(), 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+".sum", float64(ms.Sum()), r.tags, 1)
r.Client.Gauge(name+".count", float64(ms.Count()), tags, 1)
r.Client.Gauge(name+".max", time.Duration(ms.Max()).Seconds()*1000, tags, 1)
r.Client.Gauge(name+".min", time.Duration(ms.Min()).Seconds()*1000, tags, 1)
r.Client.Gauge(name+".mean", time.Duration(ms.Mean()).Seconds()*1000, tags, 1)
r.Client.Gauge(name+".stddev", time.Duration(ms.StdDev()).Seconds()*1000, tags, 1)
r.Client.Gauge(name+".sum", float64(ms.Sum()), 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)
r.Client.Gauge(name+p, time.Duration(values[i]).Seconds()*1000, tags, 1)
}
}
}
Expand All @@ -153,3 +161,14 @@ func handlePanic(rec interface{}) {
}
log.Printf("Recovered from panic: %#v \n%v", rec, callers)
}

func (r *Reporter) splitNameAndTags(metric string) (string, []string) {
if res := tagPattern.FindStringSubmatch(metric); len(res) == 3 {
if r.tags == nil {
return res[1], append(strings.Split(res[2], ","))
} else {
return res[1], append(strings.Split(res[2], ","), r.tags...)
}
}
return metric, r.tags
}
56 changes: 56 additions & 0 deletions reporter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package datadog

import (
"github.com/rcrowley/go-metrics"
"github.com/stretchr/testify/assert"
"testing"
)

func TestSplitNameAndTagsNoTags(t *testing.T) {
reporter := &Reporter{
Registry: metrics.DefaultRegistry,
}

var metricName = "test.metric_name"
name, tags := reporter.splitNameAndTags(metricName)
assert.NotNil(t, name, "name")
assert.Nil(t, tags, "tags")

assert.Equal(t, "test.metric_name", name)
}

func TestSplitNameAndTagsSingleTag(t *testing.T) {
reporter := &Reporter{
Registry: metrics.DefaultRegistry,
}

var metricName = "test.httpcall[method:GET]"

name, tags := reporter.splitNameAndTags(metricName)
assert.NotNil(t, name, "name")
assert.NotNil(t, tags, "tags")
assert.Equal(t, 1, len(tags))

assert.Equal(t, "test.httpcall", name)
assert.Equal(t, "method:GET", tags[0])
}

func TestSplitNameAndTagsMultipleTag(t *testing.T) {
globalTags := []string{"globaltag:true"}
reporter := &Reporter{
Registry: metrics.DefaultRegistry,
tags: globalTags,
}

var metricName = "test.httpcall[method:GET]"

name, tags := reporter.splitNameAndTags(metricName)
assert.NotNil(t, name, "name")
assert.NotNil(t, tags, "tags")
//expect two tags: the global tag and the metric level tag
assert.Equal(t, 2, len(tags))

assert.Equal(t, "test.httpcall", name)
assert.Equal(t, "method:GET", tags[0])
assert.Equal(t, "globaltag:true", tags[1])
}

0 comments on commit 19b86b3

Please sign in to comment.