Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(opentelemetry): implement unified service tagging #26118

Merged
merged 2 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
34 changes: 34 additions & 0 deletions comp/core/tagger/taggerimpl/collectors/workloadmeta_extract.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,12 @@ const (
envVarVersion = "DD_VERSION"
envVarService = "DD_SERVICE"

// OpenTelemetry SDK - Environment variables
wdhif marked this conversation as resolved.
Show resolved Hide resolved
// https://opentelemetry.io/docs/languages/sdk-configuration/general
// https://opentelemetry.io/docs/specs/semconv/resource/
envVarOtelService = "OTEL_SERVICE_NAME"
envVarOtelResourceAttributes = "OTEL_RESOURCE_ATTRIBUTES"

// Docker label keys
dockerLabelEnv = "com.datadoghq.tags.env"
dockerLabelVersion = "com.datadoghq.tags.version"
Expand All @@ -66,6 +72,16 @@ var (
envVarService: tagKeyService,
}

otelStandardEnvKeys = map[string]string{
envVarOtelService: tagKeyService,
}

otelResourceAttributesMapping = map[string]string{
"service.name": tagKeyService,
"service.version": tagKeyVersion,
"deployment.environment": tagKeyEnv,
}

lowCardOrchestratorEnvKeys = map[string]string{
"MARATHON_APP_ID": "marathon_app",

Expand Down Expand Up @@ -212,6 +228,9 @@ func (c *WorkloadMetaCollector) handleContainer(ev workloadmeta.Event) []*types.
// standard tags from environment
c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard)

// standard tags in OpenTelemetry SDK format from environment
c.addOpenTelemetryStandardTags(container, tags)

// orchestrator tags from environment
c.extractFromMapWithFn(container.EnvVars, lowCardOrchestratorEnvKeys, tags.AddLow)
c.extractFromMapWithFn(container.EnvVars, orchCardOrchestratorEnvKeys, tags.AddOrchestrator)
Expand Down Expand Up @@ -650,6 +669,9 @@ func (c *WorkloadMetaCollector) extractTagsFromPodContainer(pod *workloadmeta.Ku
// enrich with standard tags from environment variables
c.extractFromMapWithFn(container.EnvVars, standardEnvKeys, tags.AddStandard)

// standard tags in OpenTelemetry SDK format from environment
wdhif marked this conversation as resolved.
Show resolved Hide resolved
c.addOpenTelemetryStandardTags(container, tags)

// container-specific tags provided through pod annotation
annotation := fmt.Sprintf(podContainerTagsAnnotationFormat, containerName)
c.extractTagsFromJSONInMap(annotation, pod.Annotations, tags)
Expand Down Expand Up @@ -743,6 +765,18 @@ func (c *WorkloadMetaCollector) extractTagsFromJSONInMap(key string, input map[s
}
}

func (c *WorkloadMetaCollector) addOpenTelemetryStandardTags(container *workloadmeta.Container, tags *taglist.TagList) {
if otelResourceAttributes, ok := container.EnvVars[envVarOtelResourceAttributes]; ok {
for _, pair := range strings.Split(otelResourceAttributes, ",") {
fields := strings.SplitN(pair, "=", 2)
if tag, ok := otelResourceAttributesMapping[fields[0]]; ok {
tags.AddStandard(tag, fields[1])
}
}
}
c.extractFromMapWithFn(container.EnvVars, otelStandardEnvKeys, tags.AddStandard)
}

func buildTaggerEntityID(entityID workloadmeta.EntityID) string {
switch entityID.Kind {
case workloadmeta.KindContainer:
Expand Down
102 changes: 102 additions & 0 deletions comp/core/tagger/taggerimpl/collectors/workloadmeta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
func TestHandleKubePod(t *testing.T) {
const (
fullyFleshedContainerID = "foobarquux"
otelEnvContainerID = "otelcontainer"
noEnvContainerID = "foobarbaz"
containerName = "agent"
runtimeContainerName = "k8s_datadog-agent_agent"
Expand Down Expand Up @@ -84,6 +85,20 @@ func TestHandleKubePod(t *testing.T) {
"DD_VERSION": version,
},
})
store.Set(&workloadmeta.Container{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindContainer,
ID: otelEnvContainerID,
},
EntityMeta: workloadmeta.EntityMeta{
Name: runtimeContainerName,
},
Image: image,
EnvVars: map[string]string{
"OTEL_SERVICE_NAME": svc,
"OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env),
},
})
store.Set(&workloadmeta.Container{
EntityID: workloadmeta.EntityID{
Kind: workloadmeta.KindContainer,
Expand Down Expand Up @@ -279,6 +294,57 @@ func TestHandleKubePod(t *testing.T) {
},
},
},
{
name: "pod with fully formed container, standard tags from env with opentelemetry sdk",
pod: workloadmeta.KubernetesPod{
EntityID: podEntityID,
EntityMeta: workloadmeta.EntityMeta{
Name: podName,
Namespace: podNamespace,
},
Containers: []workloadmeta.OrchestratorContainer{
{
ID: otelEnvContainerID,
Name: containerName,
Image: image,
},
},
},
expected: []*types.TagInfo{
{
Source: podSource,
Entity: podTaggerEntityID,
HighCardTags: []string{},
OrchestratorCardTags: []string{
fmt.Sprintf("pod_name:%s", podName),
},
LowCardTags: []string{
fmt.Sprintf("kube_namespace:%s", podNamespace),
},
StandardTags: []string{},
},
{
Source: podSource,
Entity: fmt.Sprintf("container_id://%s", otelEnvContainerID),
HighCardTags: []string{
fmt.Sprintf("container_id:%s", otelEnvContainerID),
fmt.Sprintf("display_container_name:%s_%s", runtimeContainerName, podName),
},
OrchestratorCardTags: []string{
fmt.Sprintf("pod_name:%s", podName),
},
LowCardTags: append([]string{
fmt.Sprintf("kube_namespace:%s", podNamespace),
fmt.Sprintf("kube_container_name:%s", containerName),
"image_id:datadog/agent@sha256:a63d3f66fb2f69d955d4f2ca0b229385537a77872ffc04290acae65aed5317d2",
"image_name:datadog/agent",
"image_tag:latest",
"short_image:agent",
}, standardTags...),
StandardTags: standardTags,
},
},
},
{
name: "pod with container, standard tags from labels",
pod: workloadmeta.KubernetesPod{
Expand Down Expand Up @@ -990,6 +1056,42 @@ func TestHandleContainer(t *testing.T) {
},
},
},
{
name: "tags from environment with opentelemetry sdk",
wdhif marked this conversation as resolved.
Show resolved Hide resolved
container: workloadmeta.Container{
EntityID: entityID,
EntityMeta: workloadmeta.EntityMeta{
Name: containerName,
},
EnvVars: map[string]string{
// env as tags
"TEAM": "container-integrations",
"TIER": "node",

// otel standard tags
"OTEL_SERVICE_NAME": svc,
"OTEL_RESOURCE_ATTRIBUTES": fmt.Sprintf("service.name=%s,service.version=%s,deployment.environment=%s", svc, version, env),
},
},
envAsTags: map[string]string{
"team": "owner_team",
},
expected: []*types.TagInfo{
{
Source: containerSource,
Entity: taggerEntityID,
HighCardTags: []string{
fmt.Sprintf("container_name:%s", containerName),
fmt.Sprintf("container_id:%s", entityID.ID),
},
OrchestratorCardTags: []string{},
LowCardTags: append([]string{
"owner_team:container-integrations",
}, standardTags...),
StandardTags: standardTags,
},
},
},
{
name: "tags from labels",
container: workloadmeta.Container{
Expand Down
25 changes: 13 additions & 12 deletions pkg/util/containers/env_vars_filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,22 +14,23 @@ import (

var (
defaultEnvVarsIncludeList = []string{
"DD_ENV",
"DD_VERSION",
"DD_SERVICE",
"CHRONOS_JOB_NAME",
"CHRONOS_JOB_OWNER",
"NOMAD_TASK_NAME",
"NOMAD_JOB_NAME",
"NOMAD_GROUP_NAME",
"NOMAD_NAMESPACE",
"NOMAD_DC",
"MESOS_TASK_ID",
"DD_ENV",
"DD_SERVICE",
"DD_VERSION",
"DOCKER_DD_AGENT", // included to be able to detect agent containers
"ECS_CONTAINER_METADATA_URI",
"ECS_CONTAINER_METADATA_URI_V4",
"DOCKER_DD_AGENT", // included to be able to detect agent containers
// Included to ease unit tests without requiring a mock
"TEST_ENV",
"MESOS_TASK_ID",
"NOMAD_DC",
"NOMAD_GROUP_NAME",
"NOMAD_JOB_NAME",
"NOMAD_NAMESPACE",
"NOMAD_TASK_NAME",
"OTEL_RESOURCE_ATTRIBUTES",
"OTEL_SERVICE_NAME",
"TEST_ENV", // Included to ease unit tests without requiring a mock
}

envFilterOnce sync.Once
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Each section from every release note are combined when the
# CHANGELOG.rst is rendered. So the text needs to be worded so that
# it does not depend on any information only available in another
# section. This may mean repeating some details, but each section
# must be readable independently of the other.
#
# Each section note must be formatted as reStructuredText.
---
features:
- |
Implement OpenTelemetry SDK Resource Attributes as Unified Service Tags.
wdhif marked this conversation as resolved.
Show resolved Hide resolved
Loading