feat(processors.template): Allow `tag` to be a template (#13253)

This commit is contained in:
Sven Rebhan 2023-05-17 23:55:30 +02:00 committed by GitHub
parent d7dfe4ed48
commit 7da5d68315
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 19 deletions

View File

@ -24,12 +24,14 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
```toml @sample.conf
# Uses a Go template to create a new tag
[[processors.template]]
## Tag to set with the output of the template.
## Go template used to create the tag name of the output. In order to
## ease TOML escaping requirements, you should use single quotes around
## the template string.
tag = "topic"
## Go template used to create the tag value. In order to ease TOML
## escaping requirements, you may wish to use single quotes around the
## template string.
## Go template used to create the tag value of the output. In order to
## ease TOML escaping requirements, you should use single quotes around
## the template string.
template = '{{ .Tag "hostname" }}.{{ .Tag "level" }}'
```
@ -48,6 +50,19 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
+ cpu,level=debug,hostname=localhost,topic=localhost.debug time_idle=42
```
### Use a field value as tag name
```toml
[[processors.template]]
tag = '{{ .Field "type" }}'
template = '{{ .Name }}'
```
```diff
- cpu,level=debug,hostname=localhost time_idle=42,type=sensor
+ cpu,level=debug,hostname=localhost,sensor=cpu time_idle=42,type=sensor
```
### Add measurement name as a tag
```toml

View File

@ -1,9 +1,11 @@
# Uses a Go template to create a new tag
[[processors.template]]
## Tag to set with the output of the template.
## Go template used to create the tag name of the output. In order to
## ease TOML escaping requirements, you should use single quotes around
## the template string.
tag = "topic"
## Go template used to create the tag value. In order to ease TOML
## escaping requirements, you may wish to use single quotes around the
## template string.
## Go template used to create the tag value of the output. In order to
## ease TOML escaping requirements, you should use single quotes around
## the template string.
template = '{{ .Tag "hostname" }}.{{ .Tag "level" }}'

View File

@ -3,6 +3,7 @@ package template
import (
_ "embed"
"fmt"
"strings"
"text/template"
@ -17,7 +18,9 @@ type TemplateProcessor struct {
Tag string `toml:"tag"`
Template string `toml:"template"`
Log telegraf.Logger `toml:"-"`
tmpl *template.Template
tmplTag *template.Template
tmplValue *template.Template
}
func (*TemplateProcessor) SampleConfig() string {
@ -27,27 +30,40 @@ func (*TemplateProcessor) SampleConfig() string {
func (r *TemplateProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
// for each metric in "in" array
for _, metric := range in {
var b strings.Builder
newM := TemplateMetric{metric}
// supply TemplateMetric and Template from configuration to Template.Execute
err := r.tmpl.Execute(&b, &newM)
if err != nil {
r.Log.Errorf("failed to execute template: %v", err)
var b strings.Builder
if err := r.tmplTag.Execute(&b, &newM); err != nil {
r.Log.Errorf("failed to execute tag name template: %v", err)
continue
}
tag := b.String()
metric.AddTag(r.Tag, b.String())
b.Reset()
if err := r.tmplValue.Execute(&b, &newM); err != nil {
r.Log.Errorf("failed to execute value template: %v", err)
continue
}
value := b.String()
metric.AddTag(tag, value)
}
return in
}
func (r *TemplateProcessor) Init() error {
// create template
t, err := template.New("configured_template").Parse(r.Template)
var err error
r.tmpl = t
return err
r.tmplTag, err = template.New("tag template").Parse(r.Tag)
if err != nil {
return fmt.Errorf("creating tag name template failed: %w", err)
}
r.tmplValue, err = template.New("value template").Parse(r.Template)
if err != nil {
return fmt.Errorf("creating value template failed: %w", err)
}
return nil
}
func init() {

View File

@ -46,6 +46,43 @@ func TestName(t *testing.T) {
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestNameTemplate(t *testing.T) {
plugin := TemplateProcessor{
Tag: `{{ .Tag "foo" }}`,
Template: `{{ .Name }}`,
}
err := plugin.Init()
require.NoError(t, err)
input := []telegraf.Metric{
testutil.MustMetric(
"cpu",
map[string]string{"foo": "measurement"},
map[string]interface{}{
"time_idle": 42,
},
time.Unix(0, 0),
),
}
actual := plugin.Apply(input...)
expected := []telegraf.Metric{
testutil.MustMetric(
"cpu",
map[string]string{
"foo": "measurement",
"measurement": "cpu",
},
map[string]interface{}{
"time_idle": 42,
},
time.Unix(0, 0),
),
}
testutil.RequireMetricsEqual(t, expected, actual)
}
func TestTagTemplateConcatenate(t *testing.T) {
now := time.Now()