Skip to content

Commit 83b1d8f

Browse files
[processor/elasticapm] Update processor to handle new supported fields (#867)
* Add missing supported otel sem conv attributes to ecs translation * Add missing supported APM/ECS sem conv attributes to ecs translation * Updates `ecs/elastic_span_db` test cases to include all supported OTEL semconv attributes * Update `ecs_translation.go` to use constants for ecs resource attributes and update test case
1 parent bac15f8 commit 83b1d8f

File tree

4 files changed

+765
-36
lines changed

4 files changed

+765
-36
lines changed

processor/elasticapmprocessor/internal/ecs/ecs_translation.go

Lines changed: 133 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,48 @@ import (
2525
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"
2626
)
2727

28+
// Supported ECS resource attributes
29+
const (
30+
ecsAttrServiceLanguageName = "service.language.name"
31+
ecsAttrServiceLanguageVersion = "service.language.version"
32+
ecsAttrServiceFrameworkName = "service.framework.name"
33+
ecsAttrServiceFrameworkVersion = "service.framework.version"
34+
ecsAttrServiceRuntimeName = "service.runtime.name"
35+
ecsAttrServiceRuntimeVersion = "service.runtime.version"
36+
ecsAttrServiceOriginID = "service.origin.id"
37+
ecsAttrServiceOriginName = "service.origin.name"
38+
ecsAttrServiceOriginVersion = "service.origin.version"
39+
ecsAttrServiceTargetName = "service.target.name"
40+
ecsAttrServiceTargetType = "service.target.type"
41+
ecsAttrCloudOriginAccountID = "cloud.origin.account.id"
42+
ecsAttrCloudOriginProvider = "cloud.origin.provider"
43+
ecsAttrCloudOriginRegion = "cloud.origin.region"
44+
ecsAttrCloudOriginServiceName = "cloud.origin.service.name"
45+
ecsAttrCloudAccountName = "cloud.account.name"
46+
ecsAttrCloudInstanceID = "cloud.instance.id"
47+
ecsAttrCloudInstanceName = "cloud.instance.name"
48+
ecsAttrCloudMachineType = "cloud.machine.type"
49+
ecsAttrCloudProjectID = "cloud.project.id"
50+
ecsAttrCloudProjectName = "cloud.project.name"
51+
ecsAttrContainerImageTag = "container.image.tag"
52+
ecsAttrHostOSPlatform = "host.os.platform"
53+
ecsAttrProcessRuntimeName = "process.runtime.name"
54+
ecsAttrProcessRuntimeVersion = "process.runtime.version"
55+
ecsAttrDeviceManufacturer = "device.manufacturer"
56+
ecsAttrDataStreamDataset = "data_stream.dataset"
57+
ecsAttrDataStreamNamespace = "data_stream.namespace"
58+
ecsAttrUserDomain = "user.domain"
59+
ecsAttrSourceNATIP = "source.nat.ip"
60+
ecsAttrDestinationIP = "destination.ip"
61+
ecsAttrFaaSTriggerRequestID = "faas.trigger.request.id"
62+
ecsAttrFaaSExecution = "faas.execution"
63+
ecsAttrOpenCensusExporterVersion = "opencensus.exporterversion"
64+
ecsAttrAgentName = "agent.name"
65+
ecsAttrAgentVersion = "agent.version"
66+
ecsAttrAgentEphemeralID = "agent.ephemeral_id"
67+
ecsAttrAgentActivationMethod = "agent.activation_method"
68+
)
69+
2870
func TranslateResourceMetadata(resource pcommon.Resource) {
2971
attributes := resource.Attributes()
3072

@@ -51,18 +93,30 @@ func replaceDots(key string) string {
5193
return strings.ReplaceAll(key, ".", "_")
5294
}
5395

54-
// This is based to what found in the apm-data repo
55-
// (e.g https://github.com/elastic/apm-data/blob/main/input/otlp/metadata.go)
56-
// plus other extra fields
96+
// isSupportedAttribute returns true if the resource attribute is
97+
// supported by ECS and can be mapped directly.
98+
// Supported fields can include OTEL SemConv attributes or ECS specific attributes.
99+
// Fields are based on those found in the below areas:
100+
// 1. apm-data: https://github.com/elastic/apm-data/blob/main/input/otlp/metadata.go
101+
// 2. elasticapmintake receiver: https://github.com/elastic/opentelemetry-collector-components/tree/main/receiver/elasticapmintakereceiver/internal/mappers
57102
func isSupportedAttribute(attr string) bool {
58103
switch attr {
59104
// service.*
60105
case string(semconv.ServiceNameKey),
61106
string(semconv.ServiceVersionKey),
62107
string(semconv.ServiceInstanceIDKey),
63108
string(semconv.ServiceNamespaceKey),
64-
"service.language.name",
65-
"service.language.version":
109+
ecsAttrServiceLanguageName,
110+
ecsAttrServiceLanguageVersion,
111+
ecsAttrServiceFrameworkName,
112+
ecsAttrServiceFrameworkVersion,
113+
ecsAttrServiceRuntimeName,
114+
ecsAttrServiceRuntimeVersion,
115+
ecsAttrServiceOriginID,
116+
ecsAttrServiceOriginName,
117+
ecsAttrServiceOriginVersion,
118+
ecsAttrServiceTargetName,
119+
ecsAttrServiceTargetType:
66120
return true
67121

68122
// deployment.*
@@ -80,15 +134,26 @@ func isSupportedAttribute(attr string) bool {
80134
string(semconv.CloudAccountIDKey),
81135
string(semconv.CloudRegionKey),
82136
string(semconv.CloudAvailabilityZoneKey),
83-
string(semconv.CloudPlatformKey):
137+
string(semconv.CloudPlatformKey),
138+
ecsAttrCloudOriginAccountID,
139+
ecsAttrCloudOriginProvider,
140+
ecsAttrCloudOriginRegion,
141+
ecsAttrCloudOriginServiceName,
142+
ecsAttrCloudAccountName,
143+
ecsAttrCloudInstanceID,
144+
ecsAttrCloudInstanceName,
145+
ecsAttrCloudMachineType,
146+
ecsAttrCloudProjectID,
147+
ecsAttrCloudProjectName:
84148
return true
85149

86150
// container.*
87151
case string(semconv.ContainerNameKey),
88152
string(semconv.ContainerIDKey),
89153
string(semconv.ContainerImageNameKey),
90-
"container.image.tag",
91-
"container.runtime":
154+
ecsAttrContainerImageTag,
155+
string(semconv.ContainerImageTagsKey),
156+
string(semconv.ContainerRuntimeKey):
92157
return true
93158

94159
// k8s.*
@@ -102,16 +167,19 @@ func isSupportedAttribute(attr string) bool {
102167
case string(semconv.HostNameKey),
103168
string(semconv.HostIDKey),
104169
string(semconv.HostTypeKey),
105-
"host.arch",
106-
string(semconv.HostIPKey):
170+
string(semconv.HostArchKey),
171+
string(semconv.HostIPKey),
172+
ecsAttrHostOSPlatform:
107173
return true
108174

109175
// process.*
110176
case string(semconv.ProcessPIDKey),
177+
string(semconv.ProcessParentPIDKey),
178+
string(semconv.ProcessExecutableNameKey),
111179
string(semconv.ProcessCommandLineKey),
112180
string(semconv.ProcessExecutablePathKey),
113-
"process.runtime.name",
114-
"process.runtime.version",
181+
ecsAttrProcessRuntimeName,
182+
ecsAttrProcessRuntimeVersion,
115183
string(semconv.ProcessOwnerKey):
116184
return true
117185

@@ -126,21 +194,68 @@ func isSupportedAttribute(attr string) bool {
126194
case string(semconv.DeviceIDKey),
127195
string(semconv.DeviceModelIdentifierKey),
128196
string(semconv.DeviceModelNameKey),
129-
"device.manufacturer":
197+
ecsAttrDeviceManufacturer:
130198
return true
131199

132200
// data_stream.*
133-
case "data_stream.dataset",
134-
"data_stream.namespace":
201+
case ecsAttrDataStreamDataset,
202+
ecsAttrDataStreamNamespace:
203+
return true
204+
205+
// user.*
206+
case string(semconv.UserIDKey),
207+
string(semconv.UserEmailKey),
208+
string(semconv.UserNameKey),
209+
ecsAttrUserDomain:
210+
return true
211+
212+
// user_agent.*
213+
case string(semconv.UserAgentOriginalKey):
214+
return true
215+
216+
// network.*
217+
case string(semconv.NetworkConnectionTypeKey),
218+
string(semconv.NetworkConnectionSubtypeKey),
219+
string(semconv.NetworkCarrierNameKey),
220+
string(semconv.NetworkCarrierMccKey),
221+
string(semconv.NetworkCarrierMncKey),
222+
string(semconv.NetworkCarrierIccKey):
223+
return true
224+
225+
// client.*
226+
case string(semconv.ClientAddressKey),
227+
string(semconv.ClientPortKey):
228+
return true
229+
230+
// source.*
231+
case string(semconv.SourceAddressKey),
232+
string(semconv.SourcePortKey),
233+
ecsAttrSourceNATIP:
234+
return true
235+
236+
// destination.*
237+
case ecsAttrDestinationIP:
238+
return true
239+
240+
// faas.*
241+
case string(semconv.FaaSInstanceKey),
242+
string(semconv.FaaSNameKey),
243+
string(semconv.FaaSVersionKey),
244+
string(semconv.FaaSTriggerKey),
245+
string(semconv.FaaSColdstartKey),
246+
ecsAttrFaaSTriggerRequestID,
247+
ecsAttrFaaSExecution:
135248
return true
136249

137250
// Legacy OpenCensus attributes
138-
case "opencensus.exporterversion":
251+
case ecsAttrOpenCensusExporterVersion:
139252
return true
140253

141254
// APM Agent enrichment
142-
case "agent.name",
143-
"agent.version":
255+
case ecsAttrAgentName,
256+
ecsAttrAgentVersion,
257+
ecsAttrAgentEphemeralID,
258+
ecsAttrAgentActivationMethod:
144259
return true
145260
}
146261

processor/elasticapmprocessor/processor_test.go

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,25 @@ package elasticapmprocessor // import "github.com/elastic/opentelemetry-collecto
1919

2020
import (
2121
"context"
22+
"flag"
2223
"path/filepath"
2324
"testing"
2425

25-
"go.opentelemetry.io/collector/client"
26-
27-
"github.com/elastic/opentelemetry-collector-components/processor/elasticapmprocessor/internal/metadata"
2826
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden"
2927
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest/ptracetest"
3028
"github.com/stretchr/testify/assert"
3129
"github.com/stretchr/testify/require"
30+
"go.opentelemetry.io/collector/client"
3231
"go.opentelemetry.io/collector/consumer/consumertest"
3332
"go.opentelemetry.io/collector/processor/processortest"
3433
"go.uber.org/zap/zapcore"
3534
"go.uber.org/zap/zaptest"
35+
36+
"github.com/elastic/opentelemetry-collector-components/processor/elasticapmprocessor/internal/metadata"
3637
)
3738

39+
var update = flag.Bool("update", false, "Flag to generate/updated the expected yaml files")
40+
3841
// TestProcessor does some basic tests to check if enrichment is happening.
3942
// More exhaustive test for the logic are left to the library.
4043
func TestProcessor(t *testing.T) {
@@ -66,11 +69,18 @@ func TestProcessor(t *testing.T) {
6669
dir := filepath.Join("testdata", tc)
6770
inputTraces, err := golden.ReadTraces(filepath.Join(dir, "input.yaml"))
6871
require.NoError(t, err)
69-
expectedTraces, err := golden.ReadTraces(filepath.Join(dir, "output.yaml"))
72+
73+
outputFile := filepath.Join(dir, "output.yaml")
74+
expectedTraces, err := golden.ReadTraces(outputFile)
7075
require.NoError(t, err)
7176

7277
require.NoError(t, tp.ConsumeTraces(ctx, inputTraces))
73-
assert.NoError(t, ptracetest.CompareTraces(expectedTraces, next.AllTraces()[0]))
78+
actual := next.AllTraces()[0]
79+
if *update {
80+
err := golden.WriteTraces(t, outputFile, actual)
81+
assert.NoError(t, err)
82+
}
83+
assert.NoError(t, ptracetest.CompareTraces(expectedTraces, actual))
7484
})
7585
}
7686
}
@@ -95,9 +105,16 @@ func TestProcessorECS(t *testing.T) {
95105

96106
inputTraces, err := golden.ReadTraces("testdata/ecs/elastic_span_db/input.yaml")
97107
require.NoError(t, err)
98-
expectedTraces, err := golden.ReadTraces("testdata/ecs/elastic_span_db/output.yaml")
108+
109+
outputFile := "testdata/ecs/elastic_span_db/output.yaml"
110+
expectedTraces, err := golden.ReadTraces(outputFile)
99111
require.NoError(t, err)
100112

101113
require.NoError(t, tp.ConsumeTraces(ctx, inputTraces))
102-
assert.NoError(t, ptracetest.CompareTraces(expectedTraces, next.AllTraces()[0]))
114+
actual := next.AllTraces()[0]
115+
if *update {
116+
err := golden.WriteTraces(t, outputFile, actual)
117+
assert.NoError(t, err)
118+
}
119+
assert.NoError(t, ptracetest.CompareTraces(expectedTraces, actual))
103120
}

0 commit comments

Comments
 (0)