From 75282f14a7972c4d9c72e43abc972f58c2a79e7f Mon Sep 17 00:00:00 2001 From: Jack Lindamood Date: Tue, 13 Feb 2018 15:54:56 -0800 Subject: [PATCH] Allow people to sanitize their own statsd names. (#37) People can pick how they want to sanitize statsd metric names --- metrics/statsdmetrics/statsd.go | 48 +++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/metrics/statsdmetrics/statsd.go b/metrics/statsdmetrics/statsd.go index 4cbe1f6..dfe849f 100644 --- a/metrics/statsdmetrics/statsd.go +++ b/metrics/statsdmetrics/statsd.go @@ -26,6 +26,9 @@ type StatSender interface { type CommandFactory struct { StatSender StatSender SampleRate float32 + // This function will sanitize the circuit name. If you leave it empty, we will use a default implementation. + // You are free to make this however you want to sanitize your circuit names for statsd. + SanitizeStatsdFunction func(name string) string } func (c *CommandFactory) sampleRate() float32 { @@ -35,21 +38,38 @@ func (c *CommandFactory) sampleRate() float32 { return c.SampleRate } -// We can do better than this. Some kind of whitelist thing -func sanitizeStatsd(name string) string { - if len(name) > 32 { - name = name[0:32] +func (c *CommandFactory) sanitizeStatsdFunction() func(name string) string { + if c.SanitizeStatsdFunction == nil { + return sanitizeStatsd } - name = strings.Replace(name, "/", "-", -1) - name = strings.Replace(name, ":", "-", -1) - name = strings.Replace(name, ".", "-", -1) - return strings.Replace(name, ".", "_", -1) + return c.SanitizeStatsdFunction } -func appendStatsdParts(parts ...string) string { +// sanitizeStatsd borrowed from github.com/twitchtv/twirp +func sanitizeStatsd(s string) string { + if len(s) > 64 { + s = s[:64] + } + return strings.Map(sanitizeRune, s) +} + +func sanitizeRune(r rune) rune { + switch { + case 'a' <= r && r <= 'z': + return r + case '0' <= r && r <= '9': + return r + case 'A' <= r && r <= 'Z': + return r + default: + return '_' + } +} + +func appendStatsdParts(sanitizeStatsdFunction func(name string) string, parts ...string) string { nonEmpty := make([]string, 0, len(parts)) for _, part := range parts { - part = sanitizeStatsd(part) + part = sanitizeStatsdFunction(part) part = strings.Trim(part, ".") if len(part) <= 0 { continue @@ -63,7 +83,7 @@ func appendStatsdParts(parts ...string) string { func (c *CommandFactory) SLOCollector(circuitName string) responsetimeslo.Collector { return &SLOCollector{ prefixedStatSender: prefixedStatSender{ - prefix: appendStatsdParts(circuitName, "slo"), + prefix: appendStatsdParts(c.sanitizeStatsdFunction(), circuitName, "slo"), sendTo: c.StatSender, }, SampleRate: c.sampleRate(), @@ -77,7 +97,7 @@ func (c *CommandFactory) CommandProperties(circuitName string) circuit.Config { Run: []circuit.RunMetrics{ &RunMetricsCollector{ prefixedStatSender: prefixedStatSender{ - prefix: appendStatsdParts(circuitName, "run"), + prefix: appendStatsdParts(c.sanitizeStatsdFunction(), circuitName, "run"), sendTo: c.StatSender, }, SampleRate: c.sampleRate(), @@ -86,7 +106,7 @@ func (c *CommandFactory) CommandProperties(circuitName string) circuit.Config { Fallback: []circuit.FallbackMetrics{ &FallbackMetricsCollector{ prefixedStatSender: prefixedStatSender{ - prefix: appendStatsdParts(circuitName, "fallback"), + prefix: appendStatsdParts(c.sanitizeStatsdFunction(), circuitName, "fallback"), sendTo: c.StatSender, }, SampleRate: c.sampleRate(), @@ -95,7 +115,7 @@ func (c *CommandFactory) CommandProperties(circuitName string) circuit.Config { Circuit: []circuit.Metrics{ &CircuitMetricsCollector{ prefixedStatSender: prefixedStatSender{ - prefix: appendStatsdParts(circuitName, "circuit"), + prefix: appendStatsdParts(c.sanitizeStatsdFunction(), circuitName, "circuit"), sendTo: c.StatSender, }, SampleRate: c.sampleRate(),