Skip to content

Commit

Permalink
graphite parser, handle multiple templates empty filter
Browse files Browse the repository at this point in the history
Previously, the graphite parser would simply overwrite any template that
had an identical filter to a previous template. This included the empty
filter.

This change automatically creates a "*." filter for any template
specified with an empty filter. This allows users to specify only a
template, and Telegraf will auto-match based on the most specific
template.

closes #1731
  • Loading branch information
sparrc committed Oct 10, 2016
1 parent d627bdb commit 1b8861e
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 20 deletions.
81 changes: 61 additions & 20 deletions plugins/parsers/graphite/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]
}
}

// 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]
tmplt.filter = parts[0]
tmplt.template = parts[1]
if len(parts) > 2 {
tmplt.tagstring = parts[2]
}
}
}
tmplts = append(tmplts, tmplt)
}

tmpl, err1 := NewTemplate(template, tags, p.Separator)
if err1 != nil {
err = err1
break
sort.Sort(tmplts)
for _, tmplt := range tmplts {
if err := p.addToMatcher(tmplt); err != nil {
return nil, err
}
matcher.Add(filter, tmpl)
}

if err != nil {
Expand All @@ -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"))
Expand Down Expand Up @@ -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) }
42 changes: 42 additions & 0 deletions plugins/parsers/graphite/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 1b8861e

Please sign in to comment.