DRY up tag-parsing code

Signed-off-by: Tony Wooster <twooster@gmail.com>
This commit is contained in:
Tony Wooster 2019-09-18 15:29:38 +02:00
parent 0e000fe833
commit a4b92689bc
2 changed files with 41 additions and 66 deletions

View file

@ -275,8 +275,8 @@ func buildEvent(statType, metric string, value float64, relative bool, labels ma
} }
} }
func extractNameTag(component, tag string, labels map[string]string) { func parseTag(component, tag string, separator rune, labels map[string]string) {
// Empty tag is an error // Entirely empty tag is an error
if len(tag) == 0 { if len(tag) == 0 {
tagErrors.Inc() tagErrors.Inc()
log.Debugf("Empty name tag in component %s", component) log.Debugf("Empty name tag in component %s", component)
@ -284,12 +284,12 @@ func extractNameTag(component, tag string, labels map[string]string) {
} }
for i, c := range tag { for i, c := range tag {
if c == '=' { if c == separator {
k := tag[:i] k := tag[:i]
v := tag[i+1:] v := tag[i+1:]
if len(k) == 0 || len(v) == 0 { if len(k) == 0 || len(v) == 0 {
// Empty key or value, so it's an error // Empty key or value is an error
tagErrors.Inc() tagErrors.Inc()
log.Debugf("Malformed name tag %s=%s in component %s", k, v, component) log.Debugf("Malformed name tag %s=%s in component %s", k, v, component)
} else { } else {
@ -299,91 +299,66 @@ func extractNameTag(component, tag string, labels map[string]string) {
} }
} }
// Could not find an equals sign, so it's an error // Missing separator (no value) is an error
tagErrors.Inc() tagErrors.Inc()
log.Debugf("Malformed name tag %s in component %s", tag, component) log.Debugf("Malformed name tag %s in component %s", tag, component)
} }
func parseNameTag(component string, labels map[string]string) { func parseNameTags(component string, labels map[string]string) {
lastTagEndIndex := 0 lastTagEndIndex := 0
for i, c := range component { for i, c := range component {
if c == ',' { if c == ',' {
tag := component[lastTagEndIndex:i] tag := component[lastTagEndIndex:i]
lastTagEndIndex = i + 1 lastTagEndIndex = i + 1
extractNameTag(component, tag, labels) parseTag(component, tag, '=', labels)
} }
} }
// If we're not off the end of the string, add the last tag // If we're not off the end of the string, add the last tag
if lastTagEndIndex < len(component) { if lastTagEndIndex < len(component) {
tag := component[lastTagEndIndex:] tag := component[lastTagEndIndex:]
extractNameTag(component, tag, labels) parseTag(component, tag, '=', labels)
} }
} }
func parseNameAndLabels(name string, labels map[string]string) string { func trimLeftHash(s string) string {
if s != "" && s[0] == '#' {
return s[1:]
}
return s
}
func parseDogStatsDTags(component string, labels map[string]string) {
lastTagEndIndex := 0
for i, c := range component {
if c == ',' {
tag := component[lastTagEndIndex:i]
lastTagEndIndex = i + 1
parseTag(component, trimLeftHash(tag), ':', labels)
}
}
// If we're not off the end of the string, add the last tag
if lastTagEndIndex < len(component) {
tag := component[lastTagEndIndex:]
parseTag(component, trimLeftHash(tag), ':', labels)
}
}
func parseNameAndTags(name string, labels map[string]string) string {
for i, c := range name { for i, c := range name {
// # delimiting start of tags by Librato // `#` delimits start of tags by Librato
// https://www.librato.com/docs/kb/collect/collection_agents/stastd/#stat-level-tags // https://www.librato.com/docs/kb/collect/collection_agents/stastd/#stat-level-tags
// , delimiting start of tags by InfluxDB // `,` delimits start of tags by InfluxDB
// https://www.influxdata.com/blog/getting-started-with-sending-statsd-metrics-to-telegraf-influxdb/#introducing-influx-statsd // https://www.influxdata.com/blog/getting-started-with-sending-statsd-metrics-to-telegraf-influxdb/#introducing-influx-statsd
if c == '#' || c == ',' { if c == '#' || c == ',' {
parseNameTag(name[i+1:], labels) parseNameTags(name[i+1:], labels)
return name[:i] return name[:i]
} }
} }
return name return name
} }
func handleDogStatsDTag(component, tag string, labels map[string]string) {
// Bail early if the tag is empty
if len(tag) == 0 {
tagErrors.Inc()
log.Debugf("Malformed or empty DogStatsD tag %s in component %s", tag, component)
return
}
// Skip hash if found.
if tag[0] == '#' {
tag = tag[1:]
}
// find the first comma and split the tag into key and value.
var k, v string
for i, c := range tag {
if c == ':' {
k = tag[0:i]
v = tag[(i + 1):]
break
}
}
// If either of them is empty, then either the k or v is empty, or we
// didn't find a colon, either way, throw an error and skip ahead.
if len(k) == 0 || len(v) == 0 {
tagErrors.Inc()
log.Debugf("Malformed or empty DogStatsD tag %s in component %s", tag, component)
return
}
labels[escapeMetricName(k)] = v
}
func parseDogStatsDTagsToLabels(component string, labels map[string]string) {
lastTagEndIndex := 0
for i, c := range component {
if c == ',' {
tag := component[lastTagEndIndex:i]
lastTagEndIndex = i + 1
handleDogStatsDTag(component, tag, labels)
}
}
// If we're not off the end of the string, add the last tag
if lastTagEndIndex < len(component) {
tag := component[lastTagEndIndex:]
handleDogStatsDTag(component, tag, labels)
}
}
func lineToEvents(line string) Events { func lineToEvents(line string) Events {
events := Events{} events := Events{}
if line == "" { if line == "" {
@ -398,11 +373,11 @@ func lineToEvents(line string) Events {
} }
labels := map[string]string{} labels := map[string]string{}
metric := parseNameAndLabels(elements[0], labels) metric := parseNameAndTags(elements[0], labels)
var samples []string var samples []string
if strings.Contains(elements[1], "|#") { if strings.Contains(elements[1], "|#") {
// using datadog extensions // using DogStatsD tags
// don't allow mixed tagging styles // don't allow mixed tagging styles
if len(labels) > 0 { if len(labels) > 0 {
@ -471,7 +446,7 @@ samples:
multiplyEvents = int(1 / samplingFactor) multiplyEvents = int(1 / samplingFactor)
} }
case '#': case '#':
parseDogStatsDTagsToLabels(component[1:], labels) parseDogStatsDTags(component[1:], labels)
default: default:
log.Debugf("Invalid sampling factor or tag section %s on line %s", components[2], line) log.Debugf("Invalid sampling factor or tag section %s on line %s", components[2], line)
sampleErrors.WithLabelValues("invalid_sample_factor").Inc() sampleErrors.WithLabelValues("invalid_sample_factor").Inc()

View file

@ -941,7 +941,7 @@ func BenchmarkEscapeMetricName(b *testing.B) {
} }
} }
func BenchmarkParseDogStatsDTagsToLabels(b *testing.B) { func BenchmarkParseDogStatsDTags(b *testing.B) {
scenarios := map[string]string{ scenarios := map[string]string{
"1 tag w/hash": "#test:tag", "1 tag w/hash": "#test:tag",
"1 tag w/o hash": "test:tag", "1 tag w/o hash": "test:tag",
@ -954,7 +954,7 @@ func BenchmarkParseDogStatsDTagsToLabels(b *testing.B) {
b.Run(name, func(b *testing.B) { b.Run(name, func(b *testing.B) {
for n := 0; n < b.N; n++ { for n := 0; n < b.N; n++ {
labels := map[string]string{} labels := map[string]string{}
parseDogStatsDTagsToLabels(tags, labels) parseDogStatsDTags(tags, labels)
} }
}) })
} }