Support tags in enum processor (#5855)
This commit is contained in:
parent
43c6d13c33
commit
10fd5b35f0
|
@ -1,13 +1,13 @@
|
||||||
# Enum Processor Plugin
|
# Enum Processor Plugin
|
||||||
|
|
||||||
The Enum Processor allows the configuration of value mappings for metric fields.
|
The Enum Processor allows the configuration of value mappings for metric tags or fields.
|
||||||
The main use-case for this is to rewrite status codes such as _red_, _amber_ and
|
The main use-case for this is to rewrite status codes such as _red_, _amber_ and
|
||||||
_green_ by numeric values such as 0, 1, 2. The plugin supports string and bool
|
_green_ by numeric values such as 0, 1, 2. The plugin supports string and bool
|
||||||
types for the field values. Multiple Fields can be configured with separate
|
types for the field values. Multiple tags or fields can be configured with separate
|
||||||
value mappings for each field. Default mapping values can be configured to be
|
value mappings for each. Default mapping values can be configured to be
|
||||||
used for all values, which are not contained in the value_mappings. The
|
used for all values, which are not contained in the value_mappings. The
|
||||||
processor supports explicit configuration of a destination field. By default the
|
processor supports explicit configuration of a destination tag or field. By default the
|
||||||
source field is overwritten.
|
source tag or field is overwritten.
|
||||||
|
|
||||||
### Configuration:
|
### Configuration:
|
||||||
|
|
||||||
|
@ -17,8 +17,11 @@ source field is overwritten.
|
||||||
## Name of the field to map
|
## Name of the field to map
|
||||||
field = "status"
|
field = "status"
|
||||||
|
|
||||||
## Destination field to be used for the mapped value. By default the source
|
## Name of the tag to map
|
||||||
## field is used, overwriting the original value.
|
# tag = "status"
|
||||||
|
|
||||||
|
## Destination tag or field to be used for the mapped value. By default the
|
||||||
|
## source tag or field is used, overwriting the original value.
|
||||||
dest = "status_code"
|
dest = "status_code"
|
||||||
|
|
||||||
## Default value to be used for all values not contained in the mapping
|
## Default value to be used for all values not contained in the mapping
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package enum
|
package enum
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
@ -12,9 +13,12 @@ var sampleConfig = `
|
||||||
## Name of the field to map
|
## Name of the field to map
|
||||||
field = "status"
|
field = "status"
|
||||||
|
|
||||||
## Destination field to be used for the mapped value. By default the source
|
## Name of the tag to map
|
||||||
## field is used, overwriting the original value.
|
# tag = "status"
|
||||||
# dest = "status_code"
|
|
||||||
|
## Destination tag or field to be used for the mapped value. By default the
|
||||||
|
## source tag or field is used, overwriting the original value.
|
||||||
|
dest = "status_code"
|
||||||
|
|
||||||
## Default value to be used for all values not contained in the mapping
|
## Default value to be used for all values not contained in the mapping
|
||||||
## table. When unset, the unmodified value for the field will be used if no
|
## table. When unset, the unmodified value for the field will be used if no
|
||||||
|
@ -24,7 +28,7 @@ var sampleConfig = `
|
||||||
## Table of mappings
|
## Table of mappings
|
||||||
[processors.enum.mapping.value_mappings]
|
[processors.enum.mapping.value_mappings]
|
||||||
green = 1
|
green = 1
|
||||||
yellow = 2
|
amber = 2
|
||||||
red = 3
|
red = 3
|
||||||
`
|
`
|
||||||
|
|
||||||
|
@ -33,6 +37,7 @@ type EnumMapper struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mapping struct {
|
type Mapping struct {
|
||||||
|
Tag string
|
||||||
Field string
|
Field string
|
||||||
Dest string
|
Dest string
|
||||||
Default interface{}
|
Default interface{}
|
||||||
|
@ -56,14 +61,28 @@ func (mapper *EnumMapper) Apply(in ...telegraf.Metric) []telegraf.Metric {
|
||||||
|
|
||||||
func (mapper *EnumMapper) applyMappings(metric telegraf.Metric) telegraf.Metric {
|
func (mapper *EnumMapper) applyMappings(metric telegraf.Metric) telegraf.Metric {
|
||||||
for _, mapping := range mapper.Mappings {
|
for _, mapping := range mapper.Mappings {
|
||||||
if originalValue, isPresent := metric.GetField(mapping.Field); isPresent == true {
|
if mapping.Field != "" {
|
||||||
if adjustedValue, isString := adjustBoolValue(originalValue).(string); isString == true {
|
if originalValue, isPresent := metric.GetField(mapping.Field); isPresent {
|
||||||
if mappedValue, isMappedValuePresent := mapping.mapValue(adjustedValue); isMappedValuePresent == true {
|
if adjustedValue, isString := adjustBoolValue(originalValue).(string); isString {
|
||||||
|
if mappedValue, isMappedValuePresent := mapping.mapValue(adjustedValue); isMappedValuePresent {
|
||||||
writeField(metric, mapping.getDestination(), mappedValue)
|
writeField(metric, mapping.getDestination(), mappedValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if mapping.Tag != "" {
|
||||||
|
if originalValue, isPresent := metric.GetTag(mapping.Tag); isPresent {
|
||||||
|
if mappedValue, isMappedValuePresent := mapping.mapValue(originalValue); isMappedValuePresent {
|
||||||
|
switch val := mappedValue.(type) {
|
||||||
|
case string:
|
||||||
|
writeTag(metric, mapping.getDestinationTag(), val)
|
||||||
|
default:
|
||||||
|
writeTag(metric, mapping.getDestinationTag(), fmt.Sprintf("%v", val))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return metric
|
return metric
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,11 +110,23 @@ func (mapping *Mapping) getDestination() string {
|
||||||
return mapping.Field
|
return mapping.Field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mapping *Mapping) getDestinationTag() string {
|
||||||
|
if mapping.Dest != "" {
|
||||||
|
return mapping.Dest
|
||||||
|
}
|
||||||
|
return mapping.Tag
|
||||||
|
}
|
||||||
|
|
||||||
func writeField(metric telegraf.Metric, name string, value interface{}) {
|
func writeField(metric telegraf.Metric, name string, value interface{}) {
|
||||||
metric.RemoveField(name)
|
metric.RemoveField(name)
|
||||||
metric.AddField(name, value)
|
metric.AddField(name, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func writeTag(metric telegraf.Metric, name string, value string) {
|
||||||
|
metric.RemoveTag(name)
|
||||||
|
metric.AddTag(name, value)
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
processors.Add("enum", func() telegraf.Processor {
|
processors.Add("enum", func() telegraf.Processor {
|
||||||
return &EnumMapper{}
|
return &EnumMapper{}
|
||||||
|
|
|
@ -27,12 +27,23 @@ func calculateProcessedValues(mapper EnumMapper, metric telegraf.Metric) map[str
|
||||||
return processed[0].Fields()
|
return processed[0].Fields()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calculateProcessedTags(mapper EnumMapper, metric telegraf.Metric) map[string]string {
|
||||||
|
processed := mapper.Apply(metric)
|
||||||
|
return processed[0].Tags()
|
||||||
|
}
|
||||||
|
|
||||||
func assertFieldValue(t *testing.T, expected interface{}, field string, fields map[string]interface{}) {
|
func assertFieldValue(t *testing.T, expected interface{}, field string, fields map[string]interface{}) {
|
||||||
value, present := fields[field]
|
value, present := fields[field]
|
||||||
assert.True(t, present, "value of field '"+field+"' was not present")
|
assert.True(t, present, "value of field '"+field+"' was not present")
|
||||||
assert.EqualValues(t, expected, value)
|
assert.EqualValues(t, expected, value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func assertTagValue(t *testing.T, expected interface{}, tag string, tags map[string]string) {
|
||||||
|
value, present := tags[tag]
|
||||||
|
assert.True(t, present, "value of tag '"+tag+"' was not present")
|
||||||
|
assert.EqualValues(t, expected, value)
|
||||||
|
}
|
||||||
|
|
||||||
func TestRetainsMetric(t *testing.T) {
|
func TestRetainsMetric(t *testing.T) {
|
||||||
mapper := EnumMapper{}
|
mapper := EnumMapper{}
|
||||||
source := createTestMetric()
|
source := createTestMetric()
|
||||||
|
@ -56,6 +67,14 @@ func TestMapsSingleStringValue(t *testing.T) {
|
||||||
assertFieldValue(t, 1, "string_value", fields)
|
assertFieldValue(t, 1, "string_value", fields)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapsSingleStringValueTag(t *testing.T) {
|
||||||
|
mapper := EnumMapper{Mappings: []Mapping{{Tag: "tag", ValueMappings: map[string]interface{}{"tag_value": "valuable"}}}}
|
||||||
|
|
||||||
|
tags := calculateProcessedTags(mapper, createTestMetric())
|
||||||
|
|
||||||
|
assertTagValue(t, "valuable", "tag", tags)
|
||||||
|
}
|
||||||
|
|
||||||
func TestNoFailureOnMappingsOnNonStringValuedFields(t *testing.T) {
|
func TestNoFailureOnMappingsOnNonStringValuedFields(t *testing.T) {
|
||||||
mapper := EnumMapper{Mappings: []Mapping{{Field: "int_value", ValueMappings: map[string]interface{}{"13i": int64(7)}}}}
|
mapper := EnumMapper{Mappings: []Mapping{{Field: "int_value", ValueMappings: map[string]interface{}{"13i": int64(7)}}}}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue