feat(processors.parser): Add option to parse tags (#11228)
This commit is contained in:
parent
b59066ba44
commit
d976158fa5
|
|
@ -1,21 +1,25 @@
|
||||||
# Parser Processor Plugin
|
# Parser Processor Plugin
|
||||||
|
|
||||||
This plugin parses defined fields containing the specified data format and
|
This plugin parses defined fields or tags containing the specified data format
|
||||||
creates new metrics based on the contents of the field.
|
and creates new metrics based on the contents of the field or tag.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
```toml @sample.conf
|
```toml @sample.conf
|
||||||
# Parse a value in a specified field/tag(s) and add the result in a new metric
|
# Parse a value in a specified field(s)/tag(s) and add the result in a new metric
|
||||||
[[processors.parser]]
|
[[processors.parser]]
|
||||||
## The name of the fields whose value will be parsed.
|
## The name of the fields whose value will be parsed.
|
||||||
parse_fields = ["message"]
|
parse_fields = ["message"]
|
||||||
|
|
||||||
|
## The name of the tags whose value will be parsed.
|
||||||
|
# parse_tags = []
|
||||||
|
|
||||||
## If true, incoming metrics are not emitted.
|
## If true, incoming metrics are not emitted.
|
||||||
drop_original = false
|
# drop_original = false
|
||||||
|
|
||||||
## If set to override, emitted metrics will be merged by overriding the
|
## If set to override, emitted metrics will be merged by overriding the
|
||||||
## original metric using the newly parsed metrics.
|
## original metric using the newly parsed metrics.
|
||||||
|
## Only has effect when drop_original is set to false.
|
||||||
merge = "override"
|
merge = "override"
|
||||||
|
|
||||||
## The dataformat to be read from files
|
## The dataformat to be read from files
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ type Parser struct {
|
||||||
DropOriginal bool `toml:"drop_original"`
|
DropOriginal bool `toml:"drop_original"`
|
||||||
Merge string `toml:"merge"`
|
Merge string `toml:"merge"`
|
||||||
ParseFields []string `toml:"parse_fields"`
|
ParseFields []string `toml:"parse_fields"`
|
||||||
|
ParseTags []string `toml:"parse_tags"`
|
||||||
Log telegraf.Logger `toml:"-"`
|
Log telegraf.Logger `toml:"-"`
|
||||||
parser telegraf.Parser
|
parser telegraf.Parser
|
||||||
}
|
}
|
||||||
|
|
@ -47,12 +48,13 @@ func (p *Parser) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
|
||||||
newMetrics = append(newMetrics, metric)
|
newMetrics = append(newMetrics, metric)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse fields
|
||||||
for _, key := range p.ParseFields {
|
for _, key := range p.ParseFields {
|
||||||
for _, field := range metric.FieldList() {
|
for _, field := range metric.FieldList() {
|
||||||
if field.Key == key {
|
if field.Key == key {
|
||||||
switch value := field.Value.(type) {
|
switch value := field.Value.(type) {
|
||||||
case string:
|
case string:
|
||||||
fromFieldMetric, err := p.parseField(value)
|
fromFieldMetric, err := p.parseValue(value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.Log.Errorf("could not parse field %s: %v", key, err)
|
p.Log.Errorf("could not parse field %s: %v", key, err)
|
||||||
}
|
}
|
||||||
|
|
@ -74,6 +76,24 @@ func (p *Parser) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse tags
|
||||||
|
for _, key := range p.ParseTags {
|
||||||
|
if value, ok := metric.GetTag(key); ok {
|
||||||
|
fromTagMetric, err := p.parseValue(value)
|
||||||
|
if err != nil {
|
||||||
|
p.Log.Errorf("could not parse tag %s: %v", key, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, m := range fromTagMetric {
|
||||||
|
if m.Name() == "" {
|
||||||
|
m.SetName(metric.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newMetrics = append(newMetrics, fromTagMetric...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(newMetrics) == 0 {
|
if len(newMetrics) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -100,7 +120,7 @@ func merge(base telegraf.Metric, metrics []telegraf.Metric) telegraf.Metric {
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseField(value string) ([]telegraf.Metric, error) {
|
func (p *Parser) parseValue(value string) ([]telegraf.Metric, error) {
|
||||||
return p.parser.Parse([]byte(value))
|
return p.parser.Parse([]byte(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,24 +12,13 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/parsers/all"
|
_ "github.com/influxdata/telegraf/plugins/parsers/all"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// compares metrics without comparing time
|
|
||||||
func compareMetrics(t *testing.T, expected, actual []telegraf.Metric) {
|
|
||||||
require.Equal(t, len(expected), len(actual))
|
|
||||||
for i, m := range actual {
|
|
||||||
require.Equal(t, expected[i].Name(), m.Name())
|
|
||||||
require.Equal(t, expected[i].Fields(), m.Fields())
|
|
||||||
require.Equal(t, expected[i].Tags(), m.Tags())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestApply(t *testing.T) {
|
func TestApply(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
parseFields []string
|
parseFields []string
|
||||||
|
parseTags []string
|
||||||
config parsers.Config
|
config parsers.Config
|
||||||
dropOriginal bool
|
dropOriginal bool
|
||||||
merge string
|
merge string
|
||||||
|
|
@ -361,6 +350,93 @@ func TestApply(t *testing.T) {
|
||||||
time.Unix(0, 0)),
|
time.Unix(0, 0)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "parse one tag drop original",
|
||||||
|
parseTags: []string{"sample"},
|
||||||
|
dropOriginal: true,
|
||||||
|
config: parsers.Config{
|
||||||
|
DataFormat: "logfmt",
|
||||||
|
},
|
||||||
|
input: metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{
|
||||||
|
"some": "tag",
|
||||||
|
"sample": `ts=2018-07-24T19:43:40.275Z`,
|
||||||
|
},
|
||||||
|
map[string]interface{}{},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"ts": "2018-07-24T19:43:40.275Z",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parse one tag with merge",
|
||||||
|
parseTags: []string{"sample"},
|
||||||
|
dropOriginal: false,
|
||||||
|
merge: "override",
|
||||||
|
config: parsers.Config{
|
||||||
|
DataFormat: "logfmt",
|
||||||
|
},
|
||||||
|
input: metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{
|
||||||
|
"some": "tag",
|
||||||
|
"sample": `ts=2018-07-24T19:43:40.275Z`,
|
||||||
|
},
|
||||||
|
map[string]interface{}{},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{
|
||||||
|
"some": "tag",
|
||||||
|
"sample": `ts=2018-07-24T19:43:40.275Z`,
|
||||||
|
},
|
||||||
|
map[string]interface{}{
|
||||||
|
"ts": "2018-07-24T19:43:40.275Z",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "parse one tag keep",
|
||||||
|
parseTags: []string{"sample"},
|
||||||
|
dropOriginal: false,
|
||||||
|
config: parsers.Config{
|
||||||
|
DataFormat: "logfmt",
|
||||||
|
},
|
||||||
|
input: metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{
|
||||||
|
"some": "tag",
|
||||||
|
"sample": `ts=2018-07-24T19:43:40.275Z`,
|
||||||
|
},
|
||||||
|
map[string]interface{}{},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
expected: []telegraf.Metric{
|
||||||
|
metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{
|
||||||
|
"some": "tag",
|
||||||
|
"sample": `ts=2018-07-24T19:43:40.275Z`,
|
||||||
|
},
|
||||||
|
map[string]interface{}{},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
metric.New(
|
||||||
|
"singleTag",
|
||||||
|
map[string]string{},
|
||||||
|
map[string]interface{}{
|
||||||
|
"ts": "2018-07-24T19:43:40.275Z",
|
||||||
|
},
|
||||||
|
time.Unix(0, 0)),
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "Fail to parse one field but parses other [keep]",
|
name: "Fail to parse one field but parses other [keep]",
|
||||||
parseFields: []string{"good", "bad"},
|
parseFields: []string{"good", "bad"},
|
||||||
|
|
@ -506,6 +582,7 @@ func TestApply(t *testing.T) {
|
||||||
parser := Parser{
|
parser := Parser{
|
||||||
Config: tt.config,
|
Config: tt.config,
|
||||||
ParseFields: tt.parseFields,
|
ParseFields: tt.parseFields,
|
||||||
|
ParseTags: tt.parseTags,
|
||||||
DropOriginal: tt.dropOriginal,
|
DropOriginal: tt.dropOriginal,
|
||||||
Merge: tt.merge,
|
Merge: tt.merge,
|
||||||
Log: testutil.Logger{Name: "processor.parser"},
|
Log: testutil.Logger{Name: "processor.parser"},
|
||||||
|
|
@ -513,7 +590,7 @@ func TestApply(t *testing.T) {
|
||||||
|
|
||||||
output := parser.Apply(tt.input)
|
output := parser.Apply(tt.input)
|
||||||
t.Logf("Testing: %s", tt.name)
|
t.Logf("Testing: %s", tt.name)
|
||||||
compareMetrics(t, tt.expected, output)
|
testutil.RequireMetricsEqual(t, tt.expected, output, testutil.IgnoreTime())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -584,7 +661,7 @@ func TestBadApply(t *testing.T) {
|
||||||
|
|
||||||
output := parser.Apply(tt.input)
|
output := parser.Apply(tt.input)
|
||||||
|
|
||||||
compareMetrics(t, output, tt.expected)
|
testutil.RequireMetricsEqual(t, tt.expected, output, testutil.IgnoreTime())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
# Parse a value in a specified field/tag(s) and add the result in a new metric
|
# Parse a value in a specified field(s)/tag(s) and add the result in a new metric
|
||||||
[[processors.parser]]
|
[[processors.parser]]
|
||||||
## The name of the fields whose value will be parsed.
|
## The name of the fields whose value will be parsed.
|
||||||
parse_fields = ["message"]
|
parse_fields = ["message"]
|
||||||
|
|
||||||
|
## The name of the tags whose value will be parsed.
|
||||||
|
# parse_tags = []
|
||||||
|
|
||||||
## If true, incoming metrics are not emitted.
|
## If true, incoming metrics are not emitted.
|
||||||
drop_original = false
|
# drop_original = false
|
||||||
|
|
||||||
## If set to override, emitted metrics will be merged by overriding the
|
## If set to override, emitted metrics will be merged by overriding the
|
||||||
## original metric using the newly parsed metrics.
|
## original metric using the newly parsed metrics.
|
||||||
|
## Only has effect when drop_original is set to false.
|
||||||
merge = "override"
|
merge = "override"
|
||||||
|
|
||||||
## The dataformat to be read from files
|
## The dataformat to be read from files
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue