graphite parser, handle multiple templates empty filter

Previously, the graphite parser would simply overwrite any template that
had an identical filter to a previous template. This included the empty
filter.

Now we will still overwrite, but first we will sort to make sure that
the most "specific" template always matches.

closes #1731
This commit is contained in:
Cameron Sparr
2016-10-10 15:35:15 +01:00
parent 2bbb6aa6f2
commit e96f7a9b12
4 changed files with 114 additions and 20 deletions

View File

@@ -57,38 +57,34 @@ func NewGraphiteParser(
defaultTemplate, _ := NewTemplate("measurement*", nil, p.Separator)
matcher.AddDefaultTemplate(defaultTemplate)
tmplts := parsedTemplates{}
for _, pattern := range p.Templates {
template := pattern
filter := ""
tmplt := parsedTemplate{}
tmplt.template = pattern
// Format is [filter] <template> [tag1=value1,tag2=value2]
parts := strings.Fields(pattern)
if len(parts) < 1 {
continue
} else if len(parts) >= 2 {
if strings.Contains(parts[1], "=") {
template = parts[0]
tmplt.template = parts[0]
tmplt.tagstring = parts[1]
} else {
filter = parts[0]
template = parts[1]
tmplt.filter = parts[0]
tmplt.template = parts[1]
if len(parts) > 2 {
tmplt.tagstring = parts[2]
}
}
}
tmplts = append(tmplts, tmplt)
}
// Parse out the default tags specific to this template
tags := map[string]string{}
if strings.Contains(parts[len(parts)-1], "=") {
tagStrs := strings.Split(parts[len(parts)-1], ",")
for _, kv := range tagStrs {
parts := strings.Split(kv, "=")
tags[parts[0]] = parts[1]
}
sort.Sort(tmplts)
for _, tmplt := range tmplts {
if err := p.addToMatcher(tmplt); err != nil {
return nil, err
}
tmpl, err1 := NewTemplate(template, tags, p.Separator)
if err1 != nil {
err = err1
break
}
matcher.Add(filter, tmpl)
}
if err != nil {
@@ -98,6 +94,24 @@ func NewGraphiteParser(
}
}
func (p *GraphiteParser) addToMatcher(tmplt parsedTemplate) error {
// Parse out the default tags specific to this template
tags := map[string]string{}
if tmplt.tagstring != "" {
for _, kv := range strings.Split(tmplt.tagstring, ",") {
parts := strings.Split(kv, "=")
tags[parts[0]] = parts[1]
}
}
tmpl, err := NewTemplate(tmplt.template, tags, p.Separator)
if err != nil {
return err
}
p.matcher.Add(tmplt.filter, tmpl)
return nil
}
func (p *GraphiteParser) Parse(buf []byte) ([]telegraf.Metric, error) {
// parse even if the buffer begins with a newline
buf = bytes.TrimPrefix(buf, []byte("\n"))
@@ -465,3 +479,30 @@ func (n *nodes) Less(j, k int) bool {
func (n *nodes) Swap(i, j int) { (*n)[i], (*n)[j] = (*n)[j], (*n)[i] }
func (n *nodes) Len() int { return len(*n) }
type parsedTemplate struct {
template string
filter string
tagstring string
}
type parsedTemplates []parsedTemplate
func (e parsedTemplates) Less(j, k int) bool {
if len(e[j].filter) == 0 && len(e[k].filter) == 0 {
nj := len(strings.Split(e[j].template, "."))
nk := len(strings.Split(e[k].template, "."))
return nj < nk
}
if len(e[j].filter) == 0 {
return true
}
if len(e[k].filter) == 0 {
return false
}
nj := len(strings.Split(e[j].template, "."))
nk := len(strings.Split(e[k].template, "."))
return nj < nk
}
func (e parsedTemplates) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
func (e parsedTemplates) Len() int { return len(e) }

View File

@@ -747,6 +747,48 @@ func TestApplyTemplateGreedyField(t *testing.T) {
}
}
func TestApplyTemplateOverSpecific(t *testing.T) {
p, err := NewGraphiteParser(
".",
[]string{
"measurement.host.metric.metric.metric",
},
nil,
)
assert.NoError(t, err)
measurement, tags, _, err := p.ApplyTemplate("net.server001.a.b 2")
assert.Equal(t, "net", measurement)
assert.Equal(t,
map[string]string{"host": "server001", "metric": "a.b"},
tags)
}
func TestApplyTemplateMostSpecificTemplate(t *testing.T) {
p, err := NewGraphiteParser(
".",
[]string{
"measurement.host.metric",
"measurement.host.metric.metric.metric",
"measurement.host.metric.metric",
},
nil,
)
assert.NoError(t, err)
measurement, tags, _, err := p.ApplyTemplate("net.server001.a.b.c 2")
assert.Equal(t, "net", measurement)
assert.Equal(t,
map[string]string{"host": "server001", "metric": "a.b.c"},
tags)
measurement, tags, _, err = p.ApplyTemplate("net.server001.a.b 2")
assert.Equal(t, "net", measurement)
assert.Equal(t,
map[string]string{"host": "server001", "metric": "a.b"},
tags)
}
// Test Helpers
func errstr(err error) string {
if err != nil {