feat(processors.parser): Allow also non-string fields (#13553)
This commit is contained in:
parent
c050c010bc
commit
c28df89c8e
|
|
@ -2,10 +2,13 @@
|
|||
package parser
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
_ "embed"
|
||||
gobin "encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/plugins/processors"
|
||||
)
|
||||
|
||||
|
|
@ -50,34 +53,36 @@ func (p *Parser) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
|
|||
// parse fields
|
||||
for _, key := range p.ParseFields {
|
||||
for _, field := range metric.FieldList() {
|
||||
if field.Key == key {
|
||||
switch value := field.Value.(type) {
|
||||
case string:
|
||||
fromFieldMetric, err := p.parseValue(value)
|
||||
if err != nil {
|
||||
p.Log.Errorf("could not parse field %s: %v", key, err)
|
||||
}
|
||||
if field.Key != key {
|
||||
continue
|
||||
}
|
||||
value, err := p.toBytes(field.Value)
|
||||
if err != nil {
|
||||
p.Log.Errorf("could not convert field %s: %v; skipping", key, err)
|
||||
continue
|
||||
}
|
||||
fromFieldMetric, err := p.parser.Parse(value)
|
||||
if err != nil {
|
||||
p.Log.Errorf("could not parse field %s: %v", key, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, m := range fromFieldMetric {
|
||||
// The parser get the parent plugin's name as
|
||||
// default measurement name. Thus, in case the
|
||||
// parsed metric does not provide a name itself,
|
||||
// the parser will return 'parser' as we are in
|
||||
// processors.parser. In those cases we want to
|
||||
// keep the original metric name.
|
||||
if m.Name() == "" || m.Name() == "parser" {
|
||||
m.SetName(metric.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// multiple parsed fields shouldn't create multiple
|
||||
// metrics so we'll merge tags/fields down into one
|
||||
// prior to returning.
|
||||
newMetrics = append(newMetrics, fromFieldMetric...)
|
||||
default:
|
||||
p.Log.Errorf("field %q not a string, skipping", key)
|
||||
for _, m := range fromFieldMetric {
|
||||
// The parser get the parent plugin's name as
|
||||
// default measurement name. Thus, in case the
|
||||
// parsed metric does not provide a name itself,
|
||||
// the parser will return 'parser' as we are in
|
||||
// processors.parser. In those cases we want to
|
||||
// keep the original metric name.
|
||||
if m.Name() == "" || m.Name() == "parser" {
|
||||
m.SetName(metric.Name())
|
||||
}
|
||||
}
|
||||
|
||||
// multiple parsed fields shouldn't create multiple
|
||||
// metrics so we'll merge tags/fields down into one
|
||||
// prior to returning.
|
||||
newMetrics = append(newMetrics, fromFieldMetric...)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -153,6 +158,19 @@ func (p *Parser) parseValue(value string) ([]telegraf.Metric, error) {
|
|||
return p.parser.Parse([]byte(value))
|
||||
}
|
||||
|
||||
func (p *Parser) toBytes(value interface{}) ([]byte, error) {
|
||||
if v, ok := value.(string); ok {
|
||||
return []byte(v), nil
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := gobin.Write(&buf, internal.HostEndianess, value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
processors.Add("parser", func() telegraf.Processor {
|
||||
return &Parser{DropOriginal: false}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/metric"
|
||||
"github.com/influxdata/telegraf/plugins/parsers"
|
||||
"github.com/influxdata/telegraf/plugins/parsers/binary"
|
||||
"github.com/influxdata/telegraf/plugins/parsers/grok"
|
||||
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
||||
"github.com/influxdata/telegraf/plugins/parsers/json"
|
||||
|
|
@ -628,6 +629,84 @@ func TestApply(t *testing.T) {
|
|||
time.Unix(1593287020, 0)),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "non-string field with binary parser",
|
||||
parseFields: []string{"value"},
|
||||
merge: "override",
|
||||
parser: &binary.Parser{
|
||||
Configs: []binary.Config{
|
||||
{
|
||||
MetricName: "parser",
|
||||
Entries: []binary.Entry{
|
||||
{
|
||||
Name: "alarm_0",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_1",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_2",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_3",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_4",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_5",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_6",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
{
|
||||
Name: "alarm_7",
|
||||
Type: "bool",
|
||||
Bits: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
input: metric.New(
|
||||
"myname",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": uint8(13),
|
||||
},
|
||||
time.Unix(1593287020, 0)),
|
||||
expected: []telegraf.Metric{
|
||||
metric.New(
|
||||
"myname",
|
||||
map[string]string{},
|
||||
map[string]interface{}{
|
||||
"value": uint8(13),
|
||||
"alarm_0": false,
|
||||
"alarm_1": false,
|
||||
"alarm_2": false,
|
||||
"alarm_3": false,
|
||||
"alarm_4": true,
|
||||
"alarm_5": true,
|
||||
"alarm_6": false,
|
||||
"alarm_7": true,
|
||||
},
|
||||
time.Unix(1593287020, 0)),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
|
|
|||
Loading…
Reference in New Issue