Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion distributions/elastic-components/manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ receivers:
- gomod: github.com/open-telemetry/opentelemetry-collector-contrib/receiver/kubeletstatsreceiver v0.120.0
- gomod: go.opentelemetry.io/collector/receiver/otlpreceiver v0.120.0
- gomod: github.com/elastic/opentelemetry-collector-components/receiver/loadgenreceiver v0.0.0
- gomod: github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver v0.0.0

processors:
- gomod: go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.120.0
Expand Down Expand Up @@ -71,4 +72,4 @@ replaces:
- github.com/elastic/opentelemetry-collector-components/processor/ratelimitprocessor => ../processor/ratelimitprocessor
- github.com/elastic/opentelemetry-collector-components/extension/beatsauthextension => ../extension/beatsauthextension
- github.com/elastic/opentelemetry-collector-components/connector/elasticapmconnector => ../connector/elasticapmconnector

- github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver => ../receiver/elasticapmreceiver
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module github.com/gregkalapos/opentelemetry-collector-components

go 1.22.3
4 changes: 4 additions & 0 deletions receiver/elasticapmreceiver/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ func NewFactory() receiver.Factory {
func createDefaultConfig() component.Config {
defaultServerConfig := confighttp.NewDefaultServerConfig()
defaultServerConfig.Endpoint = defaultEndpoint

// TODO: Remove this once we have a proper way to configure TLS
defaultServerConfig.TLSSetting = nil

return &Config{
ServerConfig: defaultServerConfig,
}
Expand Down
7 changes: 6 additions & 1 deletion receiver/elasticapmreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ go 1.23.6

require (
github.com/elastic/apm-data v1.18.0
github.com/elastic/opentelemetry-collector-components/internal/testutil v0.0.0-20250220144628-323275205ce9
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.116.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.116.0
github.com/stretchr/testify v1.10.0
go.opentelemetry.io/collector/component v0.120.0
go.opentelemetry.io/collector/component/componentstatus v0.120.0
Expand All @@ -15,13 +18,15 @@ require (
go.opentelemetry.io/collector/pdata v1.26.0
go.opentelemetry.io/collector/receiver v0.120.0
go.opentelemetry.io/collector/receiver/receivertest v0.120.0
go.opentelemetry.io/collector/semconv v0.120.0
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.27.0
golang.org/x/sync v0.11.0
)

require (
github.com/armon/go-radix v1.0.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/elastic/go-sysinfo v1.15.1 // indirect
github.com/elastic/go-windows v1.0.2 // indirect
Expand All @@ -43,6 +48,7 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
Expand All @@ -64,7 +70,6 @@ require (
go.opentelemetry.io/collector/pdata/pprofile v0.120.0 // indirect
go.opentelemetry.io/collector/pipeline v0.120.0 // indirect
go.opentelemetry.io/collector/receiver/xreceiver v0.120.0 // indirect
go.opentelemetry.io/collector/semconv v0.120.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions receiver/elasticapmreceiver/go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
Expand All @@ -10,6 +12,8 @@ github.com/elastic/go-sysinfo v1.15.1 h1:zBmTnFEXxIQ3iwcQuk7MzaUotmKRp3OabbbWM8T
github.com/elastic/go-sysinfo v1.15.1/go.mod h1:jPSuTgXG+dhhh0GKIyI2Cso+w5lPJ5PvVqKlL8LV/Hk=
github.com/elastic/go-windows v1.0.2 h1:yoLLsAsV5cfg9FLhZ9EXZ2n2sQFKeDYrHenkcivY4vI=
github.com/elastic/go-windows v1.0.2/go.mod h1:bGcDpBzXgYSqM0Gx3DM4+UxFj300SZLixie9u9ixLM8=
github.com/elastic/opentelemetry-collector-components/internal/testutil v0.0.0-20250220144628-323275205ce9 h1:/Ju7knos+GwQymH3l8aizpSgKRJVRdknWnK5kJ5sEsE=
github.com/elastic/opentelemetry-collector-components/internal/testutil v0.0.0-20250220144628-323275205ce9/go.mod h1:kyvhWUk2Cdca9s2psYT7YoyQhMPSGpIt/nd05cs69rg=
github.com/elastic/opentelemetry-lib v0.14.0 h1:4P5q3RzwZTbAclHBmQp2dXxSsOMBQXZgkDStIR2iZnM=
github.com/elastic/opentelemetry-lib v0.14.0/go.mod h1:/FfOjBoi8gaKQrkhFxzxQzP5g7soH/tShRWDxfeIUq8=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
Expand Down Expand Up @@ -60,6 +64,12 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.116.0 h1:YENvOsl67sj8Ovvl5R8hKMnpPvdW3q5B7+CYYgy/GvQ=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.116.0/go.mod h1:D56LJWVbMc1Kdy7qa6HCrHH6ZOr4yr7YuVfp1rJn0es=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.116.0 h1:RlEK9MbxWyBHbLel8EJ1L7DbYVLai9dZL6Ljl2cBgyA=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.116.0/go.mod h1:AVUEyIjPb+0ARr7mhIkZkdNg3fd0ZcRhzAi53oZhl1Q=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0 h1:jwnZYRBuPJnsKXE5H6ZvTEm91bXW5VP8+tLewzl54eg=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.116.0/go.mod h1:NT3Ag+DdnIAZQfD7l7OHwlYqnaAJ19SoPZ0nhD9yx4s=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
Expand Down
33 changes: 33 additions & 0 deletions receiver/elasticapmreceiver/internal/attributes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package attr // import "github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver/internal"

// These constants hold attribute names that are defined by the Elastic APM data model and do not match
// any SemConv attribute. These fields are not used by the UI, and store information related to a specific span type
const (
SpanDBLink = "span.db.link"
SpanDBRowsAffected = "span.db.rows_affected"
SpanDBUserName = "span.db.user_name"
HTTPRequestBody = "http.request.body"
HTTPRequestID = "http.request.id"
HTTPRequestReferrer = "http.request.referrer"
HTTPResponseDecodedBodySize = "http.response.decoded_body_size"
HTTPResponseEncodedBodySize = "http.response.encoded_body_size"
HTTPResponseTransferSize = "http.response.transfer_size"
SpanMessageBody = "span.message.body"
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// This file contains mappings where we move intakeV2 fields into Attributes and Resource attributes on OTel events
// These fields are not covered by SemConv and are specific to Elastic

// TODO: attribute names should be pulled in from https://github.com/elastic/opentelemetry-lib/blob/main/enrichments/trace/internal/elastic/attributes.go
// `opentelemetry-lib` already has a PR to do so, after the next release of that repo, we can update this file to use those constants

package mappers // import "github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver/internal/mappers"

import (
"strings"

"github.com/elastic/apm-data/model/modelpb"
"go.opentelemetry.io/collector/pdata/pcommon"
)

// Sets fields that are NOT part of OTel for transactions. These fields are derived by the Enrichment lib in case of OTLP input
func SetDerivedFieldsForTransaction(event *modelpb.APMEvent, attributes pcommon.Map) {

attributes.PutStr("processor.event", "transaction")
attributes.PutStr("transaction.id", event.Transaction.Id)
attributes.PutStr("transaction.name", event.Transaction.Name)
attributes.PutBool("transaction.sampled", event.Transaction.Sampled)
// from whatever reason Transaction.Root is always false. That seems to be a derived field already - I don't see that fields directly on IntakeV2 - there is only ParentId
attributes.PutBool("transaction.root", event.ParentId == "")
attributes.PutStr("transaction.type", event.Transaction.Type)
attributes.PutStr("transaction.result", event.Transaction.Result)
attributes.PutInt("transaction.duration.us", int64(event.Event.Duration))
}

// Sets fields that are NOT part of OTel for spans. These fields are derived by the Enrichment lib in case of OTLP input
func SetDerivedFieldsForSpan(event *modelpb.APMEvent, attributes pcommon.Map) {

attributes.PutStr("processor.event", "span")
attributes.PutInt("span.duration.us", int64(event.Event.Duration))
attributes.PutStr("span.id", event.Span.Id)
attributes.PutStr("span.name", event.Span.Name)
attributes.PutStr("span.type", event.Span.Type)
attributes.PutStr("span.subtype", event.Span.Subtype)
attributes.PutStr("span.action", event.Span.Action)

if event.Span.Sync != nil {
attributes.PutBool("span.sync", *event.Span.Sync)
}

if event.Span.DestinationService != nil {
attributes.PutStr("service.target.name", event.Span.DestinationService.Name)
attributes.PutStr("service.target.type", event.Span.DestinationService.Type)
attributes.PutStr("span.destination.service.resource", event.Span.DestinationService.Resource)
}
}

// Sets resource fields that are NOT part of OTel. These fields are derived by the Enrichment lib in case of OTLP input
func SetDerivedResourceAttributes(event *modelpb.APMEvent, attributes pcommon.Map) {
attributes.PutStr("agent.name", event.Agent.Name)
attributes.PutStr("agent.version", event.Agent.Version)
}

// Shared across spans and transactions
func SetDerivedFieldsCommon(event *modelpb.APMEvent, attributes pcommon.Map) {
attributes.PutInt("timestamp.us", int64(event.Timestamp))

if strings.EqualFold(event.Event.Outcome, "success") {
attributes.PutStr("event.outcome", "success")
} else if strings.EqualFold(event.Event.Outcome, "failure") {
attributes.PutStr("event.outcome", "failure")
} else {
attributes.PutStr("event.outcome", "unknown")
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// This file contains mappings where we move intakeV2 fields into Attributes and Resource attributes on OTel events
// These fields are not covered by SemConv and are specific to Elastic

package mappers // import "github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver/internal/mappers"

import (
"github.com/elastic/apm-data/model/modelpb"
attr "github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver/internal"
"go.opentelemetry.io/collector/pdata/pcommon"
)

// Sets fields on spans that are not defined by OTel.
// Unlike fields from IntakeV2ToDerivedFields.go, these fields are not used by the UI
// and store information about a specific span type
func SetElasticSpecificFieldsForSpan(event *modelpb.APMEvent, attributesMap pcommon.Map) {
if event.Span.Db != nil {
attributesMap.PutStr(attr.SpanDBLink, event.Span.Db.Link)
// SemConv db.response.returned_rows is similar, but not the same
attributesMap.PutInt(attr.SpanDBRowsAffected, int64(*event.Span.Db.RowsAffected))
attributesMap.PutStr(attr.SpanDBUserName, event.Span.Db.UserName)
}

if event.Http.Request != nil {
attributesMap.PutStr(attr.HTTPRequestBody, event.Http.Request.Body.GetStringValue())
attributesMap.PutStr(attr.HTTPRequestID, event.Http.Request.Id)
attributesMap.PutStr(attr.HTTPRequestReferrer, event.Http.Request.Referrer)
}

if event.Http.Response != nil {
// SemConv http.response.body.size may match one of these.
attributesMap.PutInt(attr.HTTPResponseDecodedBodySize, int64(*event.Http.Response.DecodedBodySize))
attributesMap.PutInt(attr.HTTPResponseEncodedBodySize, int64(*event.Http.Response.EncodedBodySize))
attributesMap.PutInt(attr.HTTPResponseTransferSize, int64(*event.Http.Response.TransferSize))
}

if event.Span.Message != nil {
attributesMap.PutStr(attr.SpanMessageBody, event.Span.Message.Body)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

// This file contains all the mapping from IntakeV2 fields to OTel Semantic Convention

package mappers // import "github.com/elastic/opentelemetry-collector-components/receiver/elasticapmreceiver/internal/mappers"

import (
"github.com/elastic/apm-data/model/modelpb"
"go.opentelemetry.io/collector/pdata/pcommon"
semconv "go.opentelemetry.io/collector/semconv/v1.27.0"
)

// Translates resource attributes from the Elastic APM model to SemConv resource attributes
func TranslateToOtelResourceAttributes(event *modelpb.APMEvent, attributes pcommon.Map) {
attributes.PutStr(semconv.AttributeServiceName, event.Service.Name)
attributes.PutStr(semconv.AttributeServiceVersion, event.Service.Version)
if event.Service.Language != nil {
attributes.PutStr(semconv.AttributeTelemetrySDKLanguage, event.Service.Language.Name)
}
attributes.PutStr(semconv.AttributeTelemetrySDKName, "ElasticAPM")
if event.Service.Environment != "" {
attributes.PutStr(semconv.AttributeDeploymentEnvironmentName, event.Service.Environment)
}
}

// Translates transaction attributes from the Elastic APM model to SemConv attributes
func TranslateIntakeV2TransactionToOTelAttributes(event *modelpb.APMEvent, attributes pcommon.Map) {
setHttpAttributes(event, attributes)

if event.Span.Message != nil {
attributes.PutStr(semconv.AttributeMessagingDestinationName, event.Transaction.Message.QueueName)
attributes.PutStr(semconv.AttributeMessagingRabbitmqDestinationRoutingKey, event.Transaction.Message.RoutingKey)

// This may need to be unified, see AttributeMessagingSystem for spans
attributes.PutStr(semconv.AttributeMessagingSystem, event.Service.Framework.Name)
}
}

// Translates span attributes from the Elastic APM model to SemConv attributes
func TranslateIntakeV2SpanToOTelAttributes(event *modelpb.APMEvent, attributes pcommon.Map) {
if event.Http != nil {

setHttpAttributes(event, attributes)

if event.Url != nil && event.Url.Full != "" {
attributes.PutStr(semconv.AttributeURLFull, event.Url.Full)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if any of this inner fields can be nil, if so we might need to add a guard function for all the setters.

Copy link
Contributor Author

@gregkalapos gregkalapos Feb 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually a good point - we need to guard these. For now I just added another check. This was already setting empty strings.

In the spec the required fields are listed: https://github.com/elastic/apm-server/tree/main/docs/spec/v2

If any of those are missing, apm server rejects those documents. But url.full isn't required.

This whole question makes me think... we should probably discuss how to handle invalid documents - like when a required field is missing.

In any case - this specific one is handled.

Updated: opened https://github.com/elastic/opentelemetry-dev/issues/553?issue=elastic%7Copentelemetry-dev%7C752

}
}
if event.Span.Db != nil {
attributes.PutStr(semconv.AttributeDBSystem, event.Span.Db.Type)
attributes.PutStr(semconv.AttributeDBNamespace, event.Span.Db.Instance)
attributes.PutStr(semconv.AttributeDBQueryText, event.Span.Db.Statement)
}
if event.Span.Message != nil {
// Elastic APM span.subtype does not 100% overlap with https://opentelemetry.io/docs/specs/semconv/attributes-registry/messaging/#messaging-system
// E.g. azureservicebus in Elastic APM vs servicebus in SemConv
attributes.PutStr(semconv.AttributeMessagingSystem, event.Span.Subtype)
// No 100% overlap either
attributes.PutStr(semconv.AttributeMessagingOperationName, event.Span.Action)

if event.Span.Message.QueueName != "" {
attributes.PutStr(semconv.AttributeMessagingDestinationName, event.Span.Message.QueueName)
}
if event.Span.Message.RoutingKey != "" {
attributes.PutStr(semconv.AttributeMessagingRabbitmqDestinationRoutingKey, event.Span.Message.RoutingKey)
}
}
}

func setHttpAttributes(event *modelpb.APMEvent, attributes pcommon.Map) {
if event.Http != nil {
if event.Http.Request != nil {
attributes.PutStr(semconv.AttributeHTTPRequestMethod, event.Http.Request.Method)
if event.Url != nil && event.Url.Full != "" {
attributes.PutStr(semconv.AttributeURLFull, event.Url.Full)
}
}
if event.Http.Response != nil {
attributes.PutInt(semconv.AttributeHTTPResponseStatusCode, int64(event.Http.Response.StatusCode))
}
}
}
Loading
Loading