forked from mirrors/statsd_exporter
DRY up tag-parsing code
Signed-off-by: Tony Wooster <twooster@gmail.com>
This commit is contained in:
parent
0e000fe833
commit
a4b92689bc
2 changed files with 41 additions and 66 deletions
103
exporter.go
103
exporter.go
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue