Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
35 changes: 33 additions & 2 deletions elasticattr/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,39 @@ package elasticattr

const (
// resource s
AgentName = "agent.name"
AgentVersion = "agent.version"
AgentName = "agent.name"
AgentVersion = "agent.version"
AgentEphemeralID = "agent.ephemeral_id"
AgentActivationMethod = "agent.activation_method"
CloudOriginAccountID = "cloud.origin.account.id"
CloudOriginProvider = "cloud.origin.provider"
CloudOriginRegion = "cloud.origin.region"
CloudOriginServiceName = "cloud.origin.service.name"
CloudAccountName = "cloud.account.name"
CloudInstanceID = "cloud.instance.id"
CloudInstanceName = "cloud.instance.name"
CloudMachineType = "cloud.machine.type"
CloudProjectID = "cloud.project.id"
CloudProjectName = "cloud.project.name"
ContainerImageTag = "container.image.tag"
DeviceManufacturer = "device.manufacturer"
DataStreamDataset = "data_stream.dataset"
DataStreamNamespace = "data_stream.namespace"
DestinationIP = "destination.ip"
SourceNATIP = "source.nat.ip"
FaaSExecution = "faas.execution"
FaaSTriggerRequestID = "faas.trigger.request.id"
HostOSPlatform = "host.os.platform"
ProcessRuntimeName = "process.runtime.name"
ProcessRuntimeVersion = "process.runtime.version"
ServiceLanguageName = "service.language.name"
ServiceLanguageVersion = "service.language.version"
ServiceRuntimeName = "service.runtime.name"
ServiceRuntimeVersion = "service.runtime.version"
ServiceOriginID = "service.origin.id"
ServiceOriginName = "service.origin.name"
ServiceOriginVersion = "service.origin.version"
UserDomain = "user.domain"

// scope s
ServiceFrameworkName = "service.framework.name"
Expand Down
2 changes: 2 additions & 0 deletions enrichments/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type ResourceConfig struct {
AgentVersion AttributeConfig `mapstructure:"agent_version"`
OverrideHostName AttributeConfig `mapstructure:"override_host_name"`
DeploymentEnvironment AttributeConfig `mapstructure:"deployment_environment"`
ServiceInstanceID AttributeConfig `mapstructure:"service_instance_id"`
}

// ScopeConfig configures the enrichment of scope attributes.
Expand Down Expand Up @@ -122,6 +123,7 @@ func Enabled() Config {
AgentVersion: AttributeConfig{Enabled: true},
OverrideHostName: AttributeConfig{Enabled: true},
DeploymentEnvironment: AttributeConfig{Enabled: true},
ServiceInstanceID: AttributeConfig{Enabled: true},
},
Scope: ScopeConfig{
ServiceFrameworkName: AttributeConfig{Enabled: true},
Expand Down
36 changes: 34 additions & 2 deletions enrichments/internal/elastic/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ package elastic
import (
"fmt"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
"go.opentelemetry.io/collector/pdata/pcommon"
semconv25 "go.opentelemetry.io/otel/semconv/v1.25.0"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
)

// EnrichResource derives and adds Elastic specific resource attributes.
Expand All @@ -45,6 +46,9 @@ type resourceEnrichmentContext struct {

deploymentEnvironment string
deploymentEnvironmentName string

serviceInstanceID string
containerID string
}

func (s *resourceEnrichmentContext) Enrich(resource pcommon.Resource, cfg config.ResourceConfig) {
Expand All @@ -68,6 +72,10 @@ func (s *resourceEnrichmentContext) Enrich(resource pcommon.Resource, cfg config
s.deploymentEnvironment = v.Str()
case string(semconv.DeploymentEnvironmentNameKey):
s.deploymentEnvironmentName = v.Str()
case string(semconv25.ServiceInstanceIDKey):
s.serviceInstanceID = v.Str()
case string(semconv.ContainerIDKey):
s.containerID = v.Str()
}
return true
})
Expand All @@ -91,6 +99,10 @@ func (s *resourceEnrichmentContext) Enrich(resource pcommon.Resource, cfg config
if cfg.DeploymentEnvironment.Enabled {
s.setDeploymentEnvironment(resource)
}

if cfg.ServiceInstanceID.Enabled {
s.setServiceInstanceID(resource)
}
}

// SemConv v1.27.0 deprecated `deployment.environment` and added `deployment.environment.name` in favor of it.
Expand Down Expand Up @@ -161,3 +173,23 @@ func (s *resourceEnrichmentContext) overrideHostNameWithK8sNodeName(resource pco
s.k8sNodeName,
)
}

// setServiceInstanceID sets service.instance.id from container.id or host.name
// if service.instance.id is not already set. This follows the existing APM logic for
// `service.node.name`.
func (s *resourceEnrichmentContext) setServiceInstanceID(resource pcommon.Resource) {
if s.serviceInstanceID != "" {
return
}

switch {
case s.containerID != "":
s.serviceInstanceID = s.containerID
case s.hostName != "":
s.serviceInstanceID = s.hostName
default:
// no instance id could be derived
return
}
resource.Attributes().PutStr(string(semconv25.ServiceInstanceIDKey), s.serviceInstanceID)
}
66 changes: 60 additions & 6 deletions enrichments/internal/elastic/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,14 @@ package elastic
import (
"testing"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"go.opentelemetry.io/collector/pdata/pcommon"
semconv25 "go.opentelemetry.io/otel/semconv/v1.25.0"
semconv "go.opentelemetry.io/otel/semconv/v1.27.0"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
)

func TestResourceEnrich(t *testing.T) {
Expand Down Expand Up @@ -174,10 +175,11 @@ func TestResourceEnrich(t *testing.T) {
}(),
config: config.Enabled().Resource,
enrichedAttrs: map[string]any{
string(semconv.HostNameKey): "k8s-node",
string(semconv.K8SNodeNameKey): "k8s-node",
elasticattr.AgentName: "otlp",
elasticattr.AgentVersion: "unknown",
string(semconv.HostNameKey): "k8s-node",
string(semconv.K8SNodeNameKey): "k8s-node",
elasticattr.AgentName: "otlp",
elasticattr.AgentVersion: "unknown",
string(semconv25.ServiceInstanceIDKey): string("test-host"),
},
},
{
Expand Down Expand Up @@ -245,6 +247,58 @@ func TestResourceEnrich(t *testing.T) {
elasticattr.AgentVersion: "unknown",
},
},
{
name: "service_instance_id_derived_from_container_id",
input: func() pcommon.Resource {
res := pcommon.NewResource()
res.Attributes().PutStr(string(semconv.ServiceInstanceIDKey), "")
res.Attributes().PutStr(string(semconv25.ContainerIDKey), "container-id")
res.Attributes().PutStr(string(semconv25.HostNameKey), "k8s-node")
return res
}(),
config: config.Enabled().Resource,
enrichedAttrs: map[string]any{
string(semconv25.ServiceInstanceIDKey): "container-id",
string(semconv.ContainerIDKey): "container-id",
string(semconv.HostNameKey): "k8s-node",
elasticattr.AgentName: "otlp",
elasticattr.AgentVersion: "unknown",
},
},
{
name: "service_instance_id_derived_from_host_name",
input: func() pcommon.Resource {
res := pcommon.NewResource()
res.Attributes().PutStr(string(semconv.ServiceInstanceIDKey), "")
res.Attributes().PutStr(string(semconv25.HostNameKey), "k8s-node")
return res
}(),
config: config.Enabled().Resource,
enrichedAttrs: map[string]any{
string(semconv25.ServiceInstanceIDKey): "k8s-node",
string(semconv.HostNameKey): "k8s-node",
elasticattr.AgentName: "otlp",
elasticattr.AgentVersion: "unknown",
},
},
{
name: "service_instance_id_already_set",
input: func() pcommon.Resource {
res := pcommon.NewResource()
res.Attributes().PutStr(string(semconv.ServiceInstanceIDKey), "node-name")
res.Attributes().PutStr(string(semconv25.ContainerIDKey), "container-id")
res.Attributes().PutStr(string(semconv25.HostNameKey), "k8s-node")
return res
}(),
config: config.Enabled().Resource,
enrichedAttrs: map[string]any{
string(semconv25.ServiceInstanceIDKey): "node-name",
string(semconv.ContainerIDKey): "container-id",
string(semconv.HostNameKey): "k8s-node",
elasticattr.AgentName: "otlp",
elasticattr.AgentVersion: "unknown",
},
},
} {
t.Run(tc.name, func(t *testing.T) {
// Merge existing resource attrs with the attrs added
Expand Down
29 changes: 22 additions & 7 deletions enrichments/internal/elastic/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ import (
"net/url"
"strconv"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
"github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling"
"github.com/ua-parser/uap-go/uaparser"
"go.opentelemetry.io/collector/pdata/pcommon"
Expand All @@ -40,6 +38,9 @@ import (
semconv37 "go.opentelemetry.io/otel/semconv/v1.37.0"
tracepb "go.opentelemetry.io/proto/otlp/trace/v1"
"google.golang.org/grpc/codes"

"github.com/elastic/opentelemetry-lib/elasticattr"
"github.com/elastic/opentelemetry-lib/enrichments/config"
)

// defaultRepresentativeCount is the representative count to use for adjusting
Expand Down Expand Up @@ -444,9 +445,14 @@ func (s *spanEnrichmentContext) setSpanTypeSubtype(span ptrace.Span) (spanType s
}
}

span.Attributes().PutStr(elasticattr.SpanType, spanType)
// do not overwrite existing span.type and span.subtype attributes
if existingSpanType, _ := span.Attributes().Get(elasticattr.SpanType); existingSpanType.Str() == "" {
span.Attributes().PutStr(elasticattr.SpanType, spanType)
}
if spanSubtype != "" {
span.Attributes().PutStr(elasticattr.SpanSubtype, spanSubtype)
if existingSpanSubtype, _ := span.Attributes().Get(elasticattr.SpanSubtype); existingSpanSubtype.Str() == "" {
span.Attributes().PutStr(elasticattr.SpanSubtype, spanSubtype)
}
}

return spanType, spanSubtype
Expand Down Expand Up @@ -494,9 +500,15 @@ func (s *spanEnrichmentContext) setServiceTarget(span ptrace.Span) {
}
}

// set either target.type or target.name if at least one is available
if targetType != "" || targetName != "" {
span.Attributes().PutStr(elasticattr.ServiceTargetType, targetType)
span.Attributes().PutStr(elasticattr.ServiceTargetName, targetName)
// do not overwrite existing target.type and target.name attributes
if existingTargetType, _ := span.Attributes().Get(elasticattr.ServiceTargetType); existingTargetType.Str() == "" {
span.Attributes().PutStr(elasticattr.ServiceTargetType, targetType)
}
if existingTargetName, _ := span.Attributes().Get(elasticattr.ServiceTargetName); existingTargetName.Str() == "" {
span.Attributes().PutStr(elasticattr.ServiceTargetName, targetName)
}
}
}

Expand Down Expand Up @@ -536,7 +548,10 @@ func (s *spanEnrichmentContext) setDestinationService(span ptrace.Span) {
}

if destnResource != "" {
span.Attributes().PutStr(elasticattr.SpanDestinationServiceResource, destnResource)
// do not overwrite existing span.destination.service.resource attribute
if existingDestnResource, _ := span.Attributes().Get(elasticattr.SpanDestinationServiceResource); existingDestnResource.Str() == "" {
span.Attributes().PutStr(elasticattr.SpanDestinationServiceResource, destnResource)
}
}
}

Expand Down
Loading