From c31d402b7295e07a7279da8d16bf9f93367d98ba Mon Sep 17 00:00:00 2001 From: elv-gilles <40674218+elv-gilles@users.noreply.github.com> Date: Thu, 8 May 2025 13:07:01 +0200 Subject: [PATCH] fix(parsers.json_v2): Handle measurements with multiple objects correctly (#16878) --- plugins/parsers/json_v2/parser.go | 16 ++++++++----- .../testdata/object_multiple/expected.out | 5 ++++ .../testdata/object_multiple/input.json | 21 +++++++++++++++++ .../testdata/object_multiple/telegraf.conf | 23 +++++++++++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 plugins/parsers/json_v2/testdata/object_multiple/expected.out create mode 100644 plugins/parsers/json_v2/testdata/object_multiple/input.json create mode 100644 plugins/parsers/json_v2/testdata/object_multiple/telegraf.conf diff --git a/plugins/parsers/json_v2/parser.go b/plugins/parsers/json_v2/parser.go index cafe10796..0f9d4aa5b 100644 --- a/plugins/parsers/json_v2/parser.go +++ b/plugins/parsers/json_v2/parser.go @@ -151,6 +151,8 @@ func (p *Parser) parseCriticalPath(input []byte) ([]telegraf.Metric, error) { } var metrics []telegraf.Metric + // timestamp defaults to current time + now := time.Now() for _, c := range p.Configs { // Measurement name can either be hardcoded, or parsed from the JSON using a GJSON path expression @@ -162,8 +164,8 @@ func (p *Parser) parseCriticalPath(input []byte) ([]telegraf.Metric, error) { } } - // timestamp defaults to current time, or can be parsed from the JSON using a GJSON path expression - timestamp := time.Now() + // timestamp can be parsed from the JSON using a GJSON path expression + timestamp := now if c.TimestampPath != "" { result := gjson.GetBytes(input, c.TimestampPath) @@ -200,13 +202,15 @@ func (p *Parser) parseCriticalPath(input []byte) ([]telegraf.Metric, error) { return nil, err } - metrics = append(metrics, cartesianProduct(tags, fields)...) + cmetrics := cartesianProduct(tags, fields) - if len(objects) != 0 && len(metrics) != 0 { - metrics = cartesianProduct(objects, metrics) + if len(objects) != 0 && len(cmetrics) != 0 { + cmetrics = cartesianProduct(objects, cmetrics) } else { - metrics = append(metrics, objects...) + cmetrics = append(cmetrics, objects...) } + + metrics = append(metrics, cmetrics...) } for k, v := range p.DefaultTags { diff --git a/plugins/parsers/json_v2/testdata/object_multiple/expected.out b/plugins/parsers/json_v2/testdata/object_multiple/expected.out new file mode 100644 index 000000000..1ef4e7af8 --- /dev/null +++ b/plugins/parsers/json_v2/testdata/object_multiple/expected.out @@ -0,0 +1,5 @@ +api_http_status_codes 200=11586,201=16,202=14,204=8,404=43,500=0,503=0 1741532840501119000 +api_requests requests=11668 1741532840501225000 +api_responses responses=11667 1741532840501263000 +api_av sessions_added=0,sessions_removed=0 1741544068286646000 + diff --git a/plugins/parsers/json_v2/testdata/object_multiple/input.json b/plugins/parsers/json_v2/testdata/object_multiple/input.json new file mode 100644 index 000000000..d24393433 --- /dev/null +++ b/plugins/parsers/json_v2/testdata/object_multiple/input.json @@ -0,0 +1,21 @@ +{ + "api": { + "http_status_codes": { + "200": 11586, + "201": 16, + "202": 14, + "204": 8, + "404": 43, + "500": 0, + "503": 0 + }, + "requests": 11668, + "responses": 11667, + "av": { + "sessions": { + "added": 0, + "removed": 0 + } + } + } +} \ No newline at end of file diff --git a/plugins/parsers/json_v2/testdata/object_multiple/telegraf.conf b/plugins/parsers/json_v2/testdata/object_multiple/telegraf.conf new file mode 100644 index 000000000..11e0a0963 --- /dev/null +++ b/plugins/parsers/json_v2/testdata/object_multiple/telegraf.conf @@ -0,0 +1,23 @@ +[[inputs.file]] + files = ["./testdata/object_multiple/input.json"] + data_format = "json_v2" + + [[inputs.file.json_v2]] + measurement_name = "api_http_status_codes" + [[inputs.file.json_v2.object]] + path = "api.http_status_codes" + + [[inputs.file.json_v2]] + measurement_name = "api_requests" + [[inputs.file.json_v2.field]] + path = "api.requests" + + [[inputs.file.json_v2]] + measurement_name = "api_responses" + [[inputs.file.json_v2.field]] + path = "api.responses" + + [[inputs.file.json_v2]] + measurement_name = "api_av" + [[inputs.file.json_v2.object]] + path = "api.av"