Skip to content

Commit bea91f6

Browse files
committed
Use hash to make receiver name consistent
1 parent 6b7511b commit bea91f6

4 files changed

Lines changed: 136 additions & 50 deletions

File tree

translator/tocwconfig/sampleConfig/opentelemetry/default_otel_config_windows.yaml

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -194,15 +194,15 @@ processors:
194194
send_batch_max_size: 10000
195195
send_batch_size: 10000
196196
timeout: 30s
197-
filter/windows_events_application:
197+
filter/windows_events_application_1:
198198
error_mode: ignore
199199
logs:
200200
log_record:
201201
- not((severity_number == 17 or severity_number == 13))
202202
metrics: {}
203203
spans: {}
204204
traces: {}
205-
filter/windows_events_system:
205+
filter/windows_events_system_0:
206206
error_mode: ignore
207207
logs:
208208
log_record:
@@ -613,7 +613,7 @@ receivers:
613613
read_header_timeout: 0s
614614
traces_url_path: /v1/traces
615615
write_timeout: 0s
616-
windowseventlog/application:
616+
windowseventlog/application_2122316671:
617617
channel: Application
618618
id: windows_eventlog_input
619619
max_reads: 100
@@ -630,7 +630,7 @@ receivers:
630630
start_at: end
631631
storage: file_storage/opentelemetry
632632
type: windows_eventlog_input
633-
windowseventlog/system:
633+
windowseventlog/system_653521908:
634634
channel: System
635635
id: windows_eventlog_input
636636
max_reads: 100
@@ -680,22 +680,22 @@ service:
680680
receivers:
681681
- otlp/grpc_127_0_0_1_4317
682682
- otlp/http_127_0_0_1_4318
683-
logs/windows_events_application:
683+
logs/windows_events_application_1:
684684
exporters:
685685
- forward/opentelemetry
686686
processors:
687-
- filter/windows_events_application
687+
- filter/windows_events_application_1
688688
- transform/windows_events_scope
689689
receivers:
690-
- windowseventlog/application
691-
logs/windows_events_system:
690+
- windowseventlog/application_2122316671
691+
logs/windows_events_system_0:
692692
exporters:
693693
- forward/opentelemetry
694694
processors:
695-
- filter/windows_events_system
695+
- filter/windows_events_system_0
696696
- transform/windows_events_scope
697697
receivers:
698-
- windowseventlog/system
698+
- windowseventlog/system_653521908
699699
metrics/opentelemetry:
700700
exporters:
701701
- otlphttp/metrics

translator/tocwconfig/sampleConfig/opentelemetry/windows_events_config.yaml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,23 +89,23 @@ processors:
8989
send_batch_max_size: 10000
9090
send_batch_size: 10000
9191
timeout: 30s
92-
filter/windows_events_application:
92+
filter/windows_events_application_1:
9393
error_mode: ignore
9494
logs:
9595
log_record:
9696
- not((severity_number == 17 or severity_number == 13))
9797
metrics: {}
9898
spans: {}
9999
traces: {}
100-
filter/windows_events_system:
100+
filter/windows_events_system_0:
101101
error_mode: ignore
102102
logs:
103103
log_record:
104104
- not((severity_number == 17 or severity_number == 13 or severity_number == 9))
105105
metrics: {}
106106
spans: {}
107107
traces: {}
108-
resource/windows_events_system:
108+
resource/windows_events_system_0:
109109
attributes:
110110
- action: upsert
111111
converted_type: ""
@@ -466,7 +466,7 @@ processors:
466466
metric_statements: []
467467
trace_statements: []
468468
receivers:
469-
windowseventlog/application:
469+
windowseventlog/application_2122316671:
470470
channel: Application
471471
id: windows_eventlog_input
472472
max_reads: 100
@@ -483,7 +483,7 @@ receivers:
483483
start_at: end
484484
storage: file_storage/opentelemetry
485485
type: windows_eventlog_input
486-
windowseventlog/system:
486+
windowseventlog/system_653521908:
487487
channel: System
488488
id: windows_eventlog_input
489489
max_reads: 100
@@ -520,23 +520,23 @@ service:
520520
- batch/opentelemetry_logs
521521
receivers:
522522
- forward/opentelemetry
523-
logs/windows_events_application:
523+
logs/windows_events_application_1:
524524
exporters:
525525
- forward/opentelemetry
526526
processors:
527-
- filter/windows_events_application
527+
- filter/windows_events_application_1
528528
- transform/windows_events_scope
529529
receivers:
530-
- windowseventlog/application
531-
logs/windows_events_system:
530+
- windowseventlog/application_2122316671
531+
logs/windows_events_system_0:
532532
exporters:
533533
- forward/opentelemetry
534534
processors:
535-
- filter/windows_events_system
536-
- resource/windows_events_system
535+
- filter/windows_events_system_0
536+
- resource/windows_events_system_0
537537
- transform/windows_events_scope
538538
receivers:
539-
- windowseventlog/system
539+
- windowseventlog/system_653521908
540540
telemetry:
541541
logs:
542542
encoding: console

translator/translate/otel/pipeline/opentelemetry/windowsevents/translators.go

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"go.opentelemetry.io/collector/confmap"
1212
"go.opentelemetry.io/collector/pipeline"
1313

14+
"github.com/aws/amazon-cloudwatch-agent/internal/util/hash"
1415
translatorconfig "github.com/aws/amazon-cloudwatch-agent/translator/config"
1516
translatorcontext "github.com/aws/amazon-cloudwatch-agent/translator/context"
1617
"github.com/aws/amazon-cloudwatch-agent/translator/translate/otel/common"
@@ -52,9 +53,7 @@ func parseEntries(conf *confmap.Conf) []eventEntry {
5253
}
5354

5455
var entries []eventEntry
55-
seen := map[string]int{}
56-
channelReceiver := map[string]string{}
57-
for _, item := range list {
56+
for i, item := range list {
5857
m, ok := item.(map[string]any)
5958
if !ok {
6059
continue
@@ -97,22 +96,9 @@ func parseEntries(conf *confmap.Conf) []eventEntry {
9796
}
9897

9998
sanitized := sanitizeName(channel)
100-
name := sanitized
101-
if n := seen[sanitized]; n > 0 {
102-
name = fmt.Sprintf("%s_%d", sanitized, n)
103-
}
104-
seen[sanitized]++
105-
106-
receiverName := name
107-
if rn, ok := channelReceiver[channel]; ok {
108-
receiverName = rn
109-
} else {
110-
channelReceiver[channel] = name
111-
}
112-
11399
entries = append(entries, eventEntry{
114-
name: name,
115-
receiverName: receiverName,
100+
name: fmt.Sprintf("%s_%d", sanitized, i),
101+
receiverName: fmt.Sprintf("%s_%s", sanitized, receiverHash(channel, format)),
116102
channel: channel,
117103
raw: raw,
118104
resource: resource,
@@ -125,6 +111,10 @@ func parseEntries(conf *confmap.Conf) []eventEntry {
125111
return entries
126112
}
127113

114+
func receiverHash(channel, format string) string {
115+
return hash.HashName(channel + "\x00" + format)
116+
}
117+
128118
func sanitizeName(channel string) string {
129119
return strings.Map(func(r rune) rune {
130120
if 'a' <= r && r <= 'z' || '0' <= r && r <= '9' || r == '-' {

translator/translate/otel/pipeline/opentelemetry/windowsevents/translators_test.go

Lines changed: 106 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,43 @@ func TestParseEntries(t *testing.T) {
7676
entries := parseEntries(conf)
7777
require.Len(t, entries, 3)
7878

79-
assert.Equal(t, "system", entries[0].name)
79+
assert.Equal(t, "system_0", entries[0].name)
8080
assert.Equal(t, "System", entries[0].channel)
8181
assert.True(t, entries[0].raw)
8282
assert.Equal(t, "/custom/system", entries[0].logGroupName)
8383
assert.Equal(t, []string{"ERROR"}, entries[0].eventLevels)
8484

85-
assert.Equal(t, "application", entries[1].name)
85+
assert.Equal(t, "application_1", entries[1].name)
8686
assert.Equal(t, "Application", entries[1].channel)
8787
assert.False(t, entries[1].raw)
8888
assert.Equal(t, []int{1001}, entries[1].eventIDs)
8989

90-
assert.Equal(t, "microsoft-windows-powershell_operational", entries[2].name)
90+
assert.Equal(t, "microsoft-windows-powershell_operational_2", entries[2].name)
9191
assert.Equal(t, "Microsoft-Windows-PowerShell/Operational", entries[2].channel)
9292
assert.Equal(t, []string{"WARNING"}, entries[2].eventLevels)
9393
}
9494

95+
func TestParseEntries_ReceiverNames(t *testing.T) {
96+
conf := confmap.NewFromStringMap(map[string]any{
97+
"opentelemetry": map[string]any{
98+
"collect": map[string]any{
99+
"windows_events": map[string]any{
100+
"collect_list": []any{
101+
map[string]any{"event_name": "System", "event_levels": []any{"ERROR", "WARNING", "INFORMATION"}, "log_group_name": "/aws/cwagent/windows-events/System"},
102+
map[string]any{"event_name": "Application", "event_levels": []any{"ERROR", "WARNING"}},
103+
},
104+
},
105+
},
106+
},
107+
})
108+
entries := parseEntries(conf)
109+
require.Len(t, entries, 2)
110+
111+
// Receiver names are hash-based on (channel, format) and stable
112+
assert.Equal(t, "system_653521908", entries[0].receiverName)
113+
assert.Equal(t, "application_2122316671", entries[1].receiverName)
114+
}
115+
95116
func TestParseEntries_DuplicateChannels(t *testing.T) {
96117
conf := confmap.NewFromStringMap(map[string]any{
97118
"opentelemetry": map[string]any{
@@ -108,14 +129,91 @@ func TestParseEntries_DuplicateChannels(t *testing.T) {
108129
})
109130
entries := parseEntries(conf)
110131
require.Len(t, entries, 3)
111-
assert.Equal(t, "system", entries[0].name)
132+
assert.Equal(t, "system_0", entries[0].name)
112133
assert.Equal(t, "system_1", entries[1].name)
113134
assert.Equal(t, "system_2", entries[2].name)
114135

115-
// All entries share the same receiver (same channel = same checkpoint)
116-
assert.Equal(t, "system", entries[0].receiverName)
117-
assert.Equal(t, "system", entries[1].receiverName)
118-
assert.Equal(t, "system", entries[2].receiverName)
136+
// All entries share the same receiver (same channel + same format = same checkpoint)
137+
assert.Equal(t, entries[0].receiverName, entries[1].receiverName)
138+
assert.Equal(t, entries[1].receiverName, entries[2].receiverName)
139+
}
140+
141+
func TestParseEntries_ReceiverNameStableAcrossReorder(t *testing.T) {
142+
confAB := confmap.NewFromStringMap(map[string]any{
143+
"opentelemetry": map[string]any{
144+
"collect": map[string]any{
145+
"windows_events": map[string]any{
146+
"collect_list": []any{
147+
map[string]any{"event_name": "System", "event_levels": []any{"ERROR"}},
148+
map[string]any{"event_name": "Application"},
149+
},
150+
},
151+
},
152+
},
153+
})
154+
confBA := confmap.NewFromStringMap(map[string]any{
155+
"opentelemetry": map[string]any{
156+
"collect": map[string]any{
157+
"windows_events": map[string]any{
158+
"collect_list": []any{
159+
map[string]any{"event_name": "Application"},
160+
map[string]any{"event_name": "System", "event_levels": []any{"ERROR"}},
161+
},
162+
},
163+
},
164+
},
165+
})
166+
167+
entriesAB := parseEntries(confAB)
168+
entriesBA := parseEntries(confBA)
169+
require.Len(t, entriesAB, 2)
170+
require.Len(t, entriesBA, 2)
171+
172+
// Receiver name is order-independent (hash-based)
173+
assert.Equal(t, entriesAB[0].receiverName, entriesBA[1].receiverName)
174+
assert.Equal(t, entriesAB[1].receiverName, entriesBA[0].receiverName)
175+
}
176+
177+
func TestParseEntries_DuplicateChannelsDifferentFormat(t *testing.T) {
178+
conf := confmap.NewFromStringMap(map[string]any{
179+
"opentelemetry": map[string]any{
180+
"collect": map[string]any{
181+
"windows_events": map[string]any{
182+
"collect_list": []any{
183+
map[string]any{"event_name": "System", "event_format": "xml"},
184+
map[string]any{"event_name": "System", "event_levels": []any{"ERROR"}},
185+
},
186+
},
187+
},
188+
},
189+
})
190+
entries := parseEntries(conf)
191+
require.Len(t, entries, 2)
192+
193+
// Different formats get separate receivers even for the same channel
194+
assert.NotEqual(t, entries[0].receiverName, entries[1].receiverName)
195+
assert.True(t, entries[0].raw)
196+
assert.False(t, entries[1].raw)
197+
}
198+
199+
func TestParseEntries_DuplicateChannelsSameFormatShareReceiver(t *testing.T) {
200+
conf := confmap.NewFromStringMap(map[string]any{
201+
"opentelemetry": map[string]any{
202+
"collect": map[string]any{
203+
"windows_events": map[string]any{
204+
"collect_list": []any{
205+
map[string]any{"event_name": "System", "event_format": "xml"},
206+
map[string]any{"event_name": "System", "event_format": "xml"},
207+
},
208+
},
209+
},
210+
},
211+
})
212+
entries := parseEntries(conf)
213+
require.Len(t, entries, 2)
214+
215+
// Same format + same channel still share a receiver
216+
assert.Equal(t, entries[0].receiverName, entries[1].receiverName)
119217
}
120218

121219
func TestParseEntries_DifferentChannelsSanitizeCollision(t *testing.T) {
@@ -136,6 +234,4 @@ func TestParseEntries_DifferentChannelsSanitizeCollision(t *testing.T) {
136234

137235
// Different channels get different receiver names even if they collide after sanitization
138236
assert.NotEqual(t, entries[0].receiverName, entries[1].receiverName)
139-
assert.Equal(t, "a_b", entries[0].receiverName)
140-
assert.Equal(t, "a_b_1", entries[1].receiverName)
141237
}

0 commit comments

Comments
 (0)