diff --git a/.chloggen/drop_empty_events.yaml b/.chloggen/drop_empty_events.yaml new file mode 100644 index 000000000000..406eac1c9406 --- /dev/null +++ b/.chloggen/drop_empty_events.yaml @@ -0,0 +1,34 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: splunkhecexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Drop empty log events + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [34871] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: | + Log records with no body are dropped by Splunk on reception + as they contain no log message, albeit they may have attributes. + + This PR removes those logs from consideration to be exported. + + This is in tune with the behavior of splunkhecreceiver, which refuses HEC events with no event (#19769) + + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [] diff --git a/exporter/splunkhecexporter/client.go b/exporter/splunkhecexporter/client.go index 00f080f29b5f..3e63487840d5 100644 --- a/exporter/splunkhecexporter/client.go +++ b/exporter/splunkhecexporter/client.go @@ -205,6 +205,10 @@ func (c *client) fillLogsBuffer(logs plog.Logs, buf buffer, is iterState) (iterS } else { // Parsing log record to Splunk event. event := mapLogRecordToSplunkEvent(rl.Resource(), logRecord, c.config) + if event == nil { + // TODO record this drop as a metric + continue + } // JSON encoding event and writing to buffer. var err error diff --git a/exporter/splunkhecexporter/logdata_to_splunk.go b/exporter/splunkhecexporter/logdata_to_splunk.go index 88c55635df30..a27f4eb0d509 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk.go +++ b/exporter/splunkhecexporter/logdata_to_splunk.go @@ -24,6 +24,12 @@ const ( ) func mapLogRecordToSplunkEvent(res pcommon.Resource, lr plog.LogRecord, config *Config) *splunk.Event { + body := lr.Body().AsRaw() + if body == nil || body == "" { + // events with no body are rejected by Splunk. + return nil + } + host := unknownHostName source := config.Source sourcetype := config.SourceType @@ -83,11 +89,6 @@ func mapLogRecordToSplunkEvent(res pcommon.Resource, lr plog.LogRecord, config * return true }) - body := lr.Body().AsRaw() - if body == nil { - body = "" - } - return &splunk.Event{ Time: nanoTimestampToEpochMilliseconds(lr.Timestamp()), Host: host, diff --git a/exporter/splunkhecexporter/logdata_to_splunk_test.go b/exporter/splunkhecexporter/logdata_to_splunk_test.go index 7896affb4cbb..ab42878d0d4e 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk_test.go +++ b/exporter/splunkhecexporter/logdata_to_splunk_test.go @@ -187,14 +187,13 @@ func Test_mapLogRecordToSplunkEvent(t *testing.T) { config.SourceType = "sourcetype" return config }, - wantSplunkEvents: []*splunk.Event{ - commonLogSplunkEvent("", 0, map[string]any{}, "unknown", "source", "sourcetype"), - }, + wantSplunkEvents: []*splunk.Event{}, }, { name: "with span and trace id", logRecordFn: func() plog.LogRecord { logRecord := plog.NewLogRecord() + logRecord.Body().SetStr("foo") logRecord.SetSpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 50}) logRecord.SetTraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100}) return logRecord @@ -207,7 +206,7 @@ func Test_mapLogRecordToSplunkEvent(t *testing.T) { return config }, wantSplunkEvents: func() []*splunk.Event { - event := commonLogSplunkEvent("", 0, map[string]any{}, "unknown", "source", "sourcetype") + event := commonLogSplunkEvent("foo", 0, map[string]any{}, "unknown", "source", "sourcetype") event.Fields["span_id"] = "0000000000000032" event.Fields["trace_id"] = "00000000000000000000000000000064" return []*splunk.Event{event} @@ -329,10 +328,7 @@ func Test_mapLogRecordToSplunkEvent(t *testing.T) { config.SourceType = "sourcetype" return config }, - wantSplunkEvents: []*splunk.Event{ - commonLogSplunkEvent("", ts, map[string]any{"custom": "custom"}, - "myhost", "myapp", "myapp-type"), - }, + wantSplunkEvents: []*splunk.Event{}, }, { name: "with array body", @@ -449,13 +445,7 @@ func commonLogSplunkEvent( func Test_emptyLogRecord(t *testing.T) { event := mapLogRecordToSplunkEvent(pcommon.NewResource(), plog.NewLogRecord(), &Config{}) - assert.Zero(t, event.Time) - assert.Equal(t, event.Host, "unknown") - assert.Zero(t, event.Source) - assert.Zero(t, event.SourceType) - assert.Zero(t, event.Index) - assert.Equal(t, "", event.Event) - assert.Empty(t, event.Fields) + assert.Nil(t, event) } func Test_nanoTimestampToEpochMilliseconds(t *testing.T) {