forked from mirrors/statsd_exporter
DogStatsD: review changes
This commit is contained in:
parent
8f36baf045
commit
0b792e0be6
3 changed files with 80 additions and 30 deletions
10
README.md
10
README.md
|
@ -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/)
|
[native Prometheus instrumentation](http://prometheus.io/docs/instrumenting/clientlibs/)
|
||||||
in the long term.
|
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
|
## Building and Running
|
||||||
|
|
||||||
$ go build
|
$ go build
|
||||||
|
|
|
@ -58,42 +58,82 @@ func TestHandlePacket(t *testing.T) {
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
name: "datadog tag extension",
|
name: "datadog tag extension",
|
||||||
in: "foo:100|c|#tag1:bar,tag2:baz,tag3,tag4",
|
in: "foo:100|c|#tag1:bar,tag2:baz",
|
||||||
out: Events{
|
out: Events{
|
||||||
&CounterEvent{
|
&CounterEvent{
|
||||||
metricName: "foo",
|
metricName: "foo",
|
||||||
value: 100,
|
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)",
|
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{
|
out: Events{
|
||||||
&CounterEvent{
|
&CounterEvent{
|
||||||
metricName: "foo",
|
metricName: "foo",
|
||||||
value: 100,
|
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",
|
name: "datadog tag extension with tag keys unsupported by prometheus",
|
||||||
in: "foo:100|c|#09digits:0,tag.with.dots,tag_with_empty_value:",
|
in: "foo:100|c|#09digits:0,tag.with.dots:1",
|
||||||
out: Events{
|
out: Events{
|
||||||
&CounterEvent{
|
&CounterEvent{
|
||||||
metricName: "foo",
|
metricName: "foo",
|
||||||
value: 100,
|
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",
|
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{
|
out: Events{
|
||||||
&CounterEvent{
|
&CounterEvent{
|
||||||
metricName: "foo",
|
metricName: "foo",
|
||||||
value: 1000,
|
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"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
|
42
exporter.go
42
exporter.go
|
@ -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) {
|
func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
|
||||||
lines := strings.Split(string(packet), "\n")
|
lines := strings.Split(string(packet), "\n")
|
||||||
events := Events{}
|
events := Events{}
|
||||||
|
@ -334,7 +353,6 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
valueStr, statType := components[0], components[1]
|
valueStr, statType := components[0], components[1]
|
||||||
labels := map[string]string{}
|
|
||||||
value, err := strconv.ParseFloat(valueStr, 64)
|
value, err := strconv.ParseFloat(valueStr, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Bad value %s on line: %s", valueStr, line)
|
log.Printf("Bad value %s on line: %s", valueStr, line)
|
||||||
|
@ -342,6 +360,7 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
labels := map[string]string{}
|
||||||
if len(components) >= 3 {
|
if len(components) >= 3 {
|
||||||
for _, component := range components[2:] {
|
for _, component := range components[2:] {
|
||||||
switch component[0] {
|
switch component[0] {
|
||||||
|
@ -360,26 +379,7 @@ func (l *StatsDListener) handlePacket(packet []byte, e chan<- Events) {
|
||||||
}
|
}
|
||||||
value /= samplingFactor
|
value /= samplingFactor
|
||||||
case '#':
|
case '#':
|
||||||
networkStats.WithLabelValues("dogstatsd_tags").Inc()
|
labels = parseDogStatsDTagsToLabels(component)
|
||||||
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
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
log.Printf("Invalid sampling factor or tag section %s on line %s", components[2], line)
|
log.Printf("Invalid sampling factor or tag section %s on line %s", components[2], line)
|
||||||
networkStats.WithLabelValues("invalid_sample_factor").Inc()
|
networkStats.WithLabelValues("invalid_sample_factor").Inc()
|
||||||
|
|
Loading…
Reference in a new issue