feat(processors.template): Unify template metric (#13606)

This commit is contained in:
Sven Rebhan 2023-07-12 17:00:19 +02:00 committed by GitHub
parent d8f8d3cbc5
commit f001b29eee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 110 additions and 12 deletions

View File

@ -129,6 +129,10 @@ type Metric interface {
// e.g. '{{.Neasurement}}-{{.Tag "foo"}}-{{.Field "bar"}}' // e.g. '{{.Neasurement}}-{{.Tag "foo"}}-{{.Field "bar"}}'
type TemplateMetric interface { type TemplateMetric interface {
Name() string Name() string
Tag(key string) string
Field(key string) interface{} Field(key string) interface{}
Fields() map[string]interface{}
Tag(key string) string
Tags() map[string]string
Time() time.Time
String() string
} }

View File

@ -88,12 +88,12 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
Sometimes it is usefull to pass all fields with their values into a single Sometimes it is usefull to pass all fields with their values into a single
message for sending it to a monitoring system (e.g. Syslog, GroundWork), then message for sending it to a monitoring system (e.g. Syslog, GroundWork), then
you can use `.FieldList` or `.TagList`: you can use `.Fields` or `.Tags`:
```toml ```toml
[[processors.template]] [[processors.template]]
tag = "message" tag = "message"
template = 'Message about {{.Name}} fields: {{.FieldList}}' template = 'Message about {{.Name}} fields: {{.Fields}}'
``` ```
```diff ```diff
@ -107,7 +107,7 @@ More advanced example, which might make more sense:
[[processors.template]] [[processors.template]]
tag = "message" tag = "message"
template = '''Message about {{.Name}} fields: template = '''Message about {{.Name}} fields:
{{ range $field, $value := .FieldList -}} {{ range $field, $value := .Fields -}}
{{$field}}:{{$value}} {{$field}}:{{$value}}
{{ end }}''' {{ end }}'''
``` ```

View File

@ -30,7 +30,12 @@ func (*TemplateProcessor) SampleConfig() string {
func (r *TemplateProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric { func (r *TemplateProcessor) Apply(in ...telegraf.Metric) []telegraf.Metric {
// for each metric in "in" array // for each metric in "in" array
for _, metric := range in { for _, metric := range in {
newM := TemplateMetric{metric} m, ok := metric.(telegraf.TemplateMetric)
if !ok {
r.Log.Errorf("metric of type %T is not a template metric", metric)
continue
}
newM := TemplateMetric{m}
var b strings.Builder var b strings.Builder
if err := r.tmplTag.Execute(&b, &newM); err != nil { if err := r.tmplTag.Execute(&b, &newM); err != nil {

View File

@ -1,14 +1,20 @@
package template package template
import ( import (
"fmt" "sync"
"time" "time"
"github.com/influxdata/telegraf" "github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/models"
)
var (
onceTagList sync.Once
onceFieldList sync.Once
) )
type TemplateMetric struct { type TemplateMetric struct {
metric telegraf.Metric metric telegraf.TemplateMetric
} }
func (m *TemplateMetric) Name() string { func (m *TemplateMetric) Name() string {
@ -16,27 +22,53 @@ func (m *TemplateMetric) Name() string {
} }
func (m *TemplateMetric) Tag(key string) string { func (m *TemplateMetric) Tag(key string) string {
tagString, _ := m.metric.GetTag(key) return m.metric.Tag(key)
return tagString
} }
func (m *TemplateMetric) Field(key string) interface{} { func (m *TemplateMetric) Field(key string) interface{} {
field, _ := m.metric.GetField(key) return m.metric.Field(key)
return field
} }
func (m *TemplateMetric) Time() time.Time { func (m *TemplateMetric) Time() time.Time {
return m.metric.Time() return m.metric.Time()
} }
func (m *TemplateMetric) Tags() map[string]string {
return m.metric.Tags()
}
func (m *TemplateMetric) Fields() map[string]interface{} {
return m.metric.Fields()
}
func (m *TemplateMetric) String() string { func (m *TemplateMetric) String() string {
return fmt.Sprint(m.metric) return m.metric.String()
} }
func (m *TemplateMetric) TagList() map[string]string { func (m *TemplateMetric) TagList() map[string]string {
onceTagList.Do(func() {
models.PrintOptionValueDeprecationNotice(
telegraf.Warn, "processors.template", "template", "{{.TagList}}",
telegraf.DeprecationInfo{
Since: "1.28.0",
RemovalIn: "1.34.0",
Notice: "use '{{.Tags}}' instead",
},
)
})
return m.metric.Tags() return m.metric.Tags()
} }
func (m *TemplateMetric) FieldList() map[string]interface{} { func (m *TemplateMetric) FieldList() map[string]interface{} {
onceFieldList.Do(func() {
models.PrintOptionValueDeprecationNotice(
telegraf.Warn, "processors.template", "template", "{{.FieldList}}",
telegraf.DeprecationInfo{
Since: "1.28.0",
RemovalIn: "1.34.0",
Notice: "use '{{.Fields}}' instead",
},
)
})
return m.metric.Fields() return m.metric.Fields()
} }

View File

@ -192,6 +192,63 @@ func TestTagList(t *testing.T) {
testutil.RequireMetricsEqual(t, []telegraf.Metric{expected}, actual) testutil.RequireMetricsEqual(t, []telegraf.Metric{expected}, actual)
} }
func TestFields(t *testing.T) {
// Prepare
plugin := TemplateProcessor{
Tag: "fields",
Template: "{{.Fields}}",
Log: testutil.Logger{},
}
require.NoError(t, plugin.Init())
// Run
m := testutil.TestMetric(1.23)
actual := plugin.Apply(m)
// Verify
expected := m.Copy()
expected.AddTag("fields", "map[value:1.23]")
testutil.RequireMetricsEqual(t, []telegraf.Metric{expected}, actual)
}
func TestTags(t *testing.T) {
// Prepare
plugin := TemplateProcessor{
Tag: "tags",
Template: "{{.Tags}}",
Log: testutil.Logger{},
}
require.NoError(t, plugin.Init())
// Run
m := testutil.TestMetric(1.23)
actual := plugin.Apply(m)
// Verify
expected := m.Copy()
expected.AddTag("tags", "map[tag1:value1]")
testutil.RequireMetricsEqual(t, []telegraf.Metric{expected}, actual)
}
func TestString(t *testing.T) {
// Prepare
plugin := TemplateProcessor{
Tag: "tags",
Template: "{{.}}",
Log: testutil.Logger{},
}
require.NoError(t, plugin.Init())
// Run
m := testutil.TestMetric(1.23)
actual := plugin.Apply(m)
// Verify
expected := m.Copy()
expected.AddTag("tags", "test1 map[tag1:value1] map[value:1.23] 1257894000000000000")
testutil.RequireMetricsEqual(t, []telegraf.Metric{expected}, actual)
}
func TestDot(t *testing.T) { func TestDot(t *testing.T) {
// Prepare // Prepare
plugin := TemplateProcessor{Tag: "metric", Template: "{{.}}"} plugin := TemplateProcessor{Tag: "metric", Template: "{{.}}"}