diff --git a/CHANGELOG.md b/CHANGELOG.md index c9ed13b29333..b93176326637 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## 💡 Enhancements 💡 - `tailsampling` processor: Add new policy `latency` (#3750) +- `splunkhec` exporter: Include `trace_id` and `span_id` if set (#3850) ## v0.28.0 diff --git a/exporter/splunkhecexporter/logdata_to_splunk.go b/exporter/splunkhecexporter/logdata_to_splunk.go index 320b7d088818..e5d4ee6a649d 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk.go +++ b/exporter/splunkhecexporter/logdata_to_splunk.go @@ -24,6 +24,14 @@ import ( "github.com/open-telemetry/opentelemetry-collector-contrib/internal/splunk" ) +const ( + // Keys are taken from https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/overview.md#trace-context-in-legacy-formats. + // spanIDFieldKey is the key used in log event for the span id (if any). + spanIDFieldKey = "span_id" + // traceIDFieldKey is the key used in the log event for the trace id (if any). + traceIDFieldKey = "trace_id" +) + // Composite index of a log record in pdata.Logs. type logIndex struct { // Index in orig list (i.e. root parent index). @@ -47,6 +55,12 @@ func mapLogRecordToSplunkEvent(res pdata.Resource, lr pdata.LogRecord, config *C if lr.Name() != "" { fields[splunk.NameLabel] = lr.Name() } + if spanID := lr.SpanID().HexString(); spanID != "" { + fields[spanIDFieldKey] = spanID + } + if traceID := lr.TraceID().HexString(); traceID != "" { + fields[traceIDFieldKey] = traceID + } res.Attributes().Range(func(k string, v pdata.AttributeValue) bool { switch k { case conventions.AttributeHostName: diff --git a/exporter/splunkhecexporter/logdata_to_splunk_test.go b/exporter/splunkhecexporter/logdata_to_splunk_test.go index 5976e2152f6b..c7469bbfa825 100644 --- a/exporter/splunkhecexporter/logdata_to_splunk_test.go +++ b/exporter/splunkhecexporter/logdata_to_splunk_test.go @@ -145,6 +145,28 @@ func Test_mapLogRecordToSplunkEvent(t *testing.T) { commonLogSplunkEvent(nil, 0, map[string]interface{}{}, "unknown", "source", "sourcetype"), }, }, + { + name: "with span and trace id", + logRecordFn: func() pdata.LogRecord { + logRecord := pdata.NewLogRecord() + logRecord.SetSpanID(pdata.NewSpanID([8]byte{0, 0, 0, 0, 0, 0, 0, 50})) + logRecord.SetTraceID(pdata.NewTraceID([16]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100})) + return logRecord + }, + logResourceFn: pdata.NewResource, + configDataFn: func() *Config { + return &Config{ + Source: "source", + SourceType: "sourcetype", + } + }, + wantSplunkEvents: func() []*splunk.Event { + event := commonLogSplunkEvent(nil, 0, map[string]interface{}{}, "unknown", "source", "sourcetype") + event.Fields["span_id"] = "0000000000000032" + event.Fields["trace_id"] = "00000000000000000000000000000064" + return []*splunk.Event{event} + }(), + }, { name: "with double body", logRecordFn: func() pdata.LogRecord {