diff --git a/.chloggen/elasticsearchexporter_event-name.yaml b/.chloggen/elasticsearchexporter_event-name.yaml new file mode 100644 index 000000000000..8887665616e8 --- /dev/null +++ b/.chloggen/elasticsearchexporter_event-name.yaml @@ -0,0 +1,27 @@ +# 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: elasticsearchexporter + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Handle `EventName` for log records in OTel mode + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [37011] + +# (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: + +# 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: [user] diff --git a/exporter/elasticsearchexporter/exporter_test.go b/exporter/elasticsearchexporter/exporter_test.go index 5554c089e02b..33a0cf6d1383 100644 --- a/exporter/elasticsearchexporter/exporter_test.go +++ b/exporter/elasticsearchexporter/exporter_test.go @@ -450,7 +450,7 @@ func TestExporterLogs(t *testing.T) { return vm }(), isEvent: true, - wantDocument: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"attr.foo":"attr.foo.value","event.name":"foo"},"data_stream":{"dataset":"attr.dataset.otel","namespace":"resource.attribute.namespace","type":"logs"},"dropped_attributes_count":0,"observed_timestamp":"1970-01-01T00:00:00.000000000Z","resource":{"attributes":{"resource.attr.foo":"resource.attr.foo.value"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0},"severity_number":0,"body":{"structured":{"true":true,"false":false,"inner":{"foo":"bar"}}}}`), + wantDocument: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"attr.foo":"attr.foo.value","event.name":"foo"},"event_name":"foo","data_stream":{"dataset":"attr.dataset.otel","namespace":"resource.attribute.namespace","type":"logs"},"dropped_attributes_count":0,"observed_timestamp":"1970-01-01T00:00:00.000000000Z","resource":{"attributes":{"resource.attr.foo":"resource.attr.foo.value"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0},"severity_number":0,"body":{"structured":{"true":true,"false":false,"inner":{"foo":"bar"}}}}`), }, { body: func() pcommon.Value { @@ -473,7 +473,7 @@ func TestExporterLogs(t *testing.T) { return vs }(), isEvent: true, - wantDocument: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"attr.foo":"attr.foo.value","event.name":"foo"},"data_stream":{"dataset":"attr.dataset.otel","namespace":"resource.attribute.namespace","type":"logs"},"dropped_attributes_count":0,"observed_timestamp":"1970-01-01T00:00:00.000000000Z","resource":{"attributes":{"resource.attr.foo":"resource.attr.foo.value"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0},"severity_number":0,"body":{"structured":{"value":["foo",false,{"foo":"bar"}]}}}`), + wantDocument: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"attr.foo":"attr.foo.value","event.name":"foo"},"event_name":"foo","data_stream":{"dataset":"attr.dataset.otel","namespace":"resource.attribute.namespace","type":"logs"},"dropped_attributes_count":0,"observed_timestamp":"1970-01-01T00:00:00.000000000Z","resource":{"attributes":{"resource.attr.foo":"resource.attr.foo.value"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0},"severity_number":0,"body":{"structured":{"value":["foo",false,{"foo":"bar"}]}}}`), }, } { rec := newBulkRecorder() @@ -1629,7 +1629,7 @@ func TestExporterTraces(t *testing.T) { }, { Action: []byte(`{"create":{"_index":"logs-generic.otel-default"}}`), - Document: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"event.attr.foo":"event.attr.bar","event.name":"exception"},"data_stream":{"dataset":"generic.otel","namespace":"default","type":"logs"},"dropped_attributes_count":1,"resource":{"attributes":{"resource.foo":"resource.bar"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0}}`), + Document: []byte(`{"@timestamp":"1970-01-01T00:00:00.000000000Z","attributes":{"event.attr.foo":"event.attr.bar","event.name":"exception"},"event_name":"exception","data_stream":{"dataset":"generic.otel","namespace":"default","type":"logs"},"dropped_attributes_count":1,"resource":{"attributes":{"resource.foo":"resource.bar"},"dropped_attributes_count":0},"scope":{"dropped_attributes_count":0}}`), }, } diff --git a/exporter/elasticsearchexporter/model.go b/exporter/elasticsearchexporter/model.go index a5d9fff13892..f1c0e615f8eb 100644 --- a/exporter/elasticsearchexporter/model.go +++ b/exporter/elasticsearchexporter/model.go @@ -178,21 +178,29 @@ func (m *encodeModel) encodeLogOTelMode(resource pcommon.Resource, resourceSchem document.AddInt("severity_number", int64(record.SeverityNumber())) document.AddInt("dropped_attributes_count", int64(record.DroppedAttributesCount())) + if record.EventName() != "" { + document.AddString("event_name", record.EventName()) + } else if eventNameAttr, ok := record.Attributes().Get("event.name"); ok && eventNameAttr.Str() != "" { + document.AddString("event_name", eventNameAttr.Str()) + } + m.encodeAttributesOTelMode(&document, record.Attributes()) m.encodeResourceOTelMode(&document, resource, resourceSchemaURL) m.encodeScopeOTelMode(&document, scope, scopeSchemaURL) // Body - setOTelLogBody(&document, record.Body(), record.Attributes()) + setOTelLogBody(&document, record) return document } -func setOTelLogBody(doc *objmodel.Document, body pcommon.Value, attributes pcommon.Map) { +func setOTelLogBody(doc *objmodel.Document, record plog.LogRecord) { // Determine if this log record is an event, as they are mapped differently // https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/events.md - _, isEvent := attributes.Get("event.name") + _, isEvent := record.Attributes().Get("event.name") + isEvent = isEvent || record.EventName() != "" + body := record.Body() switch body.Type() { case pcommon.ValueTypeMap: if isEvent { @@ -734,6 +742,8 @@ func (m *encodeModel) encodeSpanEvent(resource pcommon.Resource, resourceSchemaU } var document objmodel.Document document.AddTimestamp("@timestamp", spanEvent.Timestamp()) + document.AddString("event_name", spanEvent.Name()) + // todo remove before GA, make sure Kibana uses event_name document.AddString("attributes.event.name", spanEvent.Name()) document.AddSpanID("span_id", span.SpanID()) document.AddTraceID("trace_id", span.TraceID()) diff --git a/exporter/elasticsearchexporter/model_test.go b/exporter/elasticsearchexporter/model_test.go index eda750a540e7..dddf46a14a01 100644 --- a/exporter/elasticsearchexporter/model_test.go +++ b/exporter/elasticsearchexporter/model_test.go @@ -908,6 +908,7 @@ type OTelRecord struct { ObservedTimestamp time.Time `json:"observed_timestamp"` SeverityNumber int32 `json:"severity_number"` SeverityText string `json:"severity_text"` + EventName string `json:"event_name"` Attributes map[string]any `json:"attributes"` DroppedAttributesCount uint32 `json:"dropped_attributes_count"` Scope OTelScope `json:"scope"` @@ -1076,6 +1077,30 @@ func TestEncodeLogOtelMode(t *testing.T) { return assignDatastreamData(or, "", ds, ns) }, }, + { + name: "event_name from attributes.event.name", + rec: buildOTelRecordTestData(t, func(or OTelRecord) OTelRecord { + or.Attributes["event.name"] = "foo" + or.EventName = "" + return or + }), + wantFn: func(or OTelRecord) OTelRecord { + or.EventName = "foo" + return assignDatastreamData(or) + }, + }, + { + name: "event_name takes precedent over attributes.event.name", + rec: buildOTelRecordTestData(t, func(or OTelRecord) OTelRecord { + or.Attributes["event.name"] = "foo" + or.EventName = "bar" + return or + }), + wantFn: func(or OTelRecord) OTelRecord { + or.EventName = "bar" + return assignDatastreamData(or) + }, + }, } m := encodeModel{ @@ -1117,6 +1142,7 @@ func createTestOTelLogRecord(t *testing.T, rec OTelRecord) (plog.LogRecord, pcom record.SetSeverityNumber(plog.SeverityNumber(rec.SeverityNumber)) record.SetSeverityText(rec.SeverityText) record.SetDroppedAttributesCount(rec.DroppedAttributesCount) + record.SetEventName(rec.EventName) err := record.Attributes().FromRaw(rec.Attributes) require.NoError(t, err) @@ -1143,6 +1169,7 @@ func buildOTelRecordTestData(t *testing.T, fn func(OTelRecord) OTelRecord) OTelR "event.name": "user-password-change", "foo.some": "bar" }, + "event_name": "user-password-change", "dropped_attributes_count": 1, "observed_timestamp": "2024-03-12T20:00:41.123456789Z", "resource": {