DogStatsD: review changes

This commit is contained in:
Ilya Margolin 2016-04-23 23:50:41 +02:00 committed by Ilya Margolin
parent 8f36baf045
commit 0b792e0be6
3 changed files with 80 additions and 30 deletions

View file

@ -26,6 +26,16 @@ We recommend this only as an intermediate solution and recommend switching to
[native Prometheus instrumentation](http://prometheus.io/docs/instrumenting/clientlibs/)
in the long term.
### DogStatsD extensions
The exporter will convert DogStatsD-style tags to prometheus labels. See
[Tags](http://docs.datadoghq.com/guides/dogstatsd/#tags) in the DogStatsD
documentation for the concept description and
[Datagram Format](http://docs.datadoghq.com/guides/dogstatsd/#datagram-format)
for specifics. It boils down to appending
`|#tag:value,another_tag:another_value` to the normal StatsD format. Tags
without values (`#some_tag`) are not supported.
## Building and Running
$ go build

View file

@ -58,42 +58,82 @@ func TestHandlePacket(t *testing.T) {
},
}, {
name: "datadog tag extension",
in: "foo:100|c|#tag1:bar,tag2:baz,tag3,tag4",
in: "foo:100|c|#tag1:bar,tag2:baz",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{"tag1": "bar", "tag2": "baz", "tag3": ".", "tag4": "."},
labels: map[string]string{"tag1": "bar", "tag2": "baz"},
},
},
}, {
name: "datadog tag extension with # in all keys (as sent by datadog php client)",
in: "foo:100|c|#tag1:bar,#tag2:baz,#tag3,#tag4",
in: "foo:100|c|#tag1:bar,#tag2:baz",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{"tag1": "bar", "tag2": "baz", "tag3": ".", "tag4": "."},
labels: map[string]string{"tag1": "bar", "tag2": "baz"},
},
},
}, {
name: "datadog tag extension with tags unsupported by prometheus",
in: "foo:100|c|#09digits:0,tag.with.dots,tag_with_empty_value:",
name: "datadog tag extension with tag keys unsupported by prometheus",
in: "foo:100|c|#09digits:0,tag.with.dots:1",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{"_09digits": "0", "tag_with_dots": ".", "tag_with_empty_value": "."},
labels: map[string]string{"_09digits": "0", "tag_with_dots": "1"},
},
},
}, {
name: "datadog tag extension with valueless tags: ignored",
in: "foo:100|c|#tag_without_a_value",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{},
},
},
}, {
name: "datadog tag extension with valueless tags (edge case)",
in: "foo:100|c|#tag_without_a_value,tag:value",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{"tag": "value"},
},
},
}, {
name: "datadog tag extension with empty tags (edge case)",
in: "foo:100|c|#tag:value,,",
out: Events{
&CounterEvent{
metricName: "foo",
value: 100,
labels: map[string]string{"tag": "value"},
},
},
}, {
name: "datadog tag extension with sampling",
in: "foo:100|c|@0.1|#tag1:bar,tag2,tag3:baz",
in: "foo:100|c|@0.1|#tag1:bar,#tag2:baz",
out: Events{
&CounterEvent{
metricName: "foo",
value: 1000,
labels: map[string]string{"tag1": "bar", "tag2": ".", "tag3": "baz"},
labels: map[string]string{"tag1": "bar", "tag2": "baz"},
},
},
}, {
name: "datadog tag extension with multiple colons",
in: "foo:100|c|@0.1|#tag1:foo:bar",
out: Events{
&CounterEvent{
metricName: "foo",
value: 1000,
labels: map[string]string{"tag1": "foo:bar"},
},
},
}, {

View file

@ -303,6 +303,25 @@ func (l *StatsDListener) Listen(e chan<- Events) {
}
}
func parseDogStatsDTagsToLabels(component string) map[string]string {
labels := map[string]string{}
networkStats.WithLabelValues("dogstatsd_tags").Inc()
tags := strings.Split(component, ",")
for _, t := range tags {
t = strings.TrimPrefix(t, "#")
kv := strings.SplitN(t, ":", 2)
if len(kv) < 2 || len(kv[1]) == 0 {
networkStats.WithLabelValues("malformed_dogstatsd_tag").Inc()
log.Printf("Malformed or empty DogStatsD tag %s in component %s", t, component)
continue
}
labels[escapeMetricName(kv[0])] = kv[1]
}
return labels
}
func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
lines := strings.Split(string(packet), "\n")
events := Events{}
@ -334,7 +353,6 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
continue
}
valueStr, statType := components[0], components[1]
labels := map[string]string{}
value, err := strconv.ParseFloat(valueStr, 64)
if err != nil {
log.Printf("Bad value %s on line: %s", valueStr, line)
@ -342,6 +360,7 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
continue
}
labels := map[string]string{}
if len(components) >= 3 {
for _, component := range components[2:] {
switch component[0] {
@ -360,26 +379,7 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
}
value /= samplingFactor
case '#':
networkStats.WithLabelValues("dogstatsd_tags").Inc()
tags := strings.Split(component, ",")
for _, t := range tags {
t = strings.TrimPrefix(t, "#")
kv := strings.Split(t, ":")
tag_key := kv[0]
tag_key = escapeMetricName(tag_key)
var tag_value string
if len(kv) == 2 {
if len(kv[1]) > 0 {
tag_value = kv[1]
} else {
tag_value = "."
}
} else if len(kv) == 1 {
tag_value = "."
}
labels[tag_key] = tag_value
}
labels = parseDogStatsDTagsToLabels(component)
default:
log.Printf("Invalid sampling factor or tag section %s on line %s", components[2], line)
networkStats.WithLabelValues("invalid_sample_factor").Inc()