Skip to content

Commit 964ef5a

Browse files
committed
BUGFIX/FEATURE: Sanitize StatsD jobs/events beacuse some StatsD servers can't accept keys with colons or pipes. Provide the option to write your own sanitizer in case your StatsD implementation doesn't suck.
1 parent d3631cc commit 964ef5a

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

statsd_sink.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ import (
77
"time"
88
)
99

10+
type StatsDSinkSantizationFunc func(string) string
11+
1012
// This sink emits to a StatsD deaemon by sending it a UDP packet.
1113
type StatsDSink struct {
14+
SanitizationFunc StatsDSinkSantizationFunc
15+
1216
conn net.Conn
1317

1418
// Prefix is something like "metroid"
@@ -25,8 +29,9 @@ func NewStatsDSink(addr, prefix string) (Sink, error) {
2529
}
2630

2731
sink := &StatsDSink{
28-
conn: c,
29-
prefix: prefix,
32+
SanitizationFunc: sanitizeKey,
33+
conn: c,
34+
prefix: prefix,
3035
}
3136

3237
return sink, nil
@@ -65,7 +70,7 @@ func (s *StatsDSink) EmitComplete(job string, status CompletionStatus, nanos int
6570
b.WriteString(s.prefix)
6671
b.WriteRune('.')
6772
}
68-
b.WriteString(job)
73+
b.WriteString(s.SanitizationFunc(job))
6974
b.WriteRune('.')
7075
b.WriteString(completionStatusToString[status])
7176

@@ -83,10 +88,10 @@ func (s *StatsDSink) eventKeys(job, event, suffix string) (string, string) {
8388
key2.WriteRune('.')
8489
}
8590

86-
key1.WriteString(event)
87-
key2.WriteString(job)
91+
key1.WriteString(s.SanitizationFunc(event))
92+
key2.WriteString(s.SanitizationFunc(job))
8893
key2.WriteRune('.')
89-
key2.WriteString(event)
94+
key2.WriteString(s.SanitizationFunc(event))
9095

9196
if suffix != "" {
9297
key1.WriteRune('.')
@@ -117,3 +122,15 @@ func (s *StatsDSink) measure(key string, nanos int64) {
117122
func (s *StatsDSink) send(msg []byte) {
118123
s.conn.Write(msg)
119124
}
125+
126+
func sanitizeKey(k string) string {
127+
var key bytes.Buffer
128+
for _, c := range k {
129+
if c == '|' || c == ':' {
130+
key.WriteRune('$')
131+
} else {
132+
key.WriteRune(c)
133+
}
134+
}
135+
return key.String()
136+
}

statsd_sink_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ func TestStatsDSinkEmitEventPrefix(t *testing.T) {
5454
})
5555
}
5656

57+
func TestStatsDSinkEmitEventShouldSanitize(t *testing.T) {
58+
sink, err := NewStatsDSink(testAddr, "metroid")
59+
assert.NoError(t, err)
60+
listenFor(t, []string{"metroid.my$event:1|c\n", "metroid.my$job.my$event:1|c\n"}, func() {
61+
sink.EmitEvent("my|job", "my:event", nil)
62+
})
63+
}
64+
5765
func TestStatsDSinkEmitEventNoPrefix(t *testing.T) {
5866
sink, err := NewStatsDSink(testAddr, "")
5967
assert.NoError(t, err)

0 commit comments

Comments
 (0)