From b20cf0bede4b543d720231b5de7c97c0955e3fe2 Mon Sep 17 00:00:00 2001 From: Bora Tanrikulu Date: Tue, 27 Jan 2026 18:29:34 +0300 Subject: [PATCH] [SC-23705] fix(sematext): prevent field name corruption on re-processing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fallback in Rename.Process() was using ChangeNames(key) where key included the measurement prefix. This caused field names to accumulate prefixes when metrics were re-processed: BEFORE: measurement="etcd", field="slow_requests" → "etcd.slow.requests" AFTER: measurement="etcd", field="slow_requests" → "slow.requests" For mapped fields (apache, nginx, etc.) there is no change - they use fieldReplaces mappings. The fix ensures idempotency: processing a metric multiple times no longer corrupts field names. --- plugins/outputs/sematext/processors/rename.go | 4 +++- .../sematext/processors/rename_test.go | 24 ++++++++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/plugins/outputs/sematext/processors/rename.go b/plugins/outputs/sematext/processors/rename.go index aa7dbbdcbd272..6e0b5fbaaa36c 100644 --- a/plugins/outputs/sematext/processors/rename.go +++ b/plugins/outputs/sematext/processors/rename.go @@ -229,7 +229,9 @@ func (r *Rename) Process(points []telegraf.Metric) []telegraf.Metric { replace, ok := fieldReplaces[key] if !ok { - replace = ChangeNames(key) + // Use field.Key directly to avoid prepending measurement name. + // This prevents corruption on re-processing and ensures idempotency. + replace = ChangeNames(field.Key) } // we can't remove the fields // while iterating because it diff --git a/plugins/outputs/sematext/processors/rename_test.go b/plugins/outputs/sematext/processors/rename_test.go index 8f8e335f5ca10..3a729d4fd7630 100644 --- a/plugins/outputs/sematext/processors/rename_test.go +++ b/plugins/outputs/sematext/processors/rename_test.go @@ -25,7 +25,7 @@ func TestRename(t *testing.T) { assert.Equal(t, "apache", results[0].Name()) assert.Equal(t, "php", results[1].Name()) assert.Equal(t, "workers.dns", results[2].FieldList()[0].Key) - assert.Equal(t, "etcd.slow.requests", results[3].FieldList()[0].Key) + assert.Equal(t, "slow.requests", results[3].FieldList()[0].Key) assert.Equal(t, "fpm.requests.slow", results[4].FieldList()[0].Key) assert.Equal(t, "mongo", results[5].Name()) assert.Equal(t, "mongodb_col_stats.ok", results[5].FieldList()[0].Key) @@ -34,6 +34,28 @@ func TestRename(t *testing.T) { assert.Equal(t, "mongodb_shard_stats.in_use", results[8].FieldList()[0].Key) } +func TestRenameIdempotent(t *testing.T) { + r := Rename{} + + // Process a metric multiple times - field names should remain stable + m := newMetric("apache", nil, map[string]interface{}{"scboard_dnslookup": 120}) + + // First pass + results := r.Process([]telegraf.Metric{m}) + assert.Equal(t, "apache", results[0].Name()) + assert.Equal(t, "workers.dns", results[0].FieldList()[0].Key) + + // Second pass - should NOT corrupt field names + results = r.Process(results) + assert.Equal(t, "apache", results[0].Name()) + assert.Equal(t, "workers.dns", results[0].FieldList()[0].Key, "Field should remain 'workers.dns', not 'apache.workers.dns'") + + // Third pass - still stable + results = r.Process(results) + assert.Equal(t, "apache", results[0].Name()) + assert.Equal(t, "workers.dns", results[0].FieldList()[0].Key, "Field should remain 'workers.dns' after multiple passes") +} + func newMetric(name string, tags map[string]string, fields map[string]interface{}) telegraf.Metric { if tags == nil { tags = map[string]string{}