Skip to content

Commit

Permalink
[exporter/datadog]: Add container tags to attributes package (#6086)
Browse files Browse the repository at this point in the history
* add container tags to attributes package

* Make changes based on feedback

* Change comment based on Feedback

* Fix lint issues
  • Loading branch information
mackjmr authored Nov 5, 2021
1 parent eb3b3c4 commit e066c9c
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 25 deletions.
65 changes: 61 additions & 4 deletions exporter/datadogexporter/internal/attributes/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package attributes

import (
"fmt"
"strings"

"go.opentelemetry.io/collector/model/pdata"
conventions "go.opentelemetry.io/collector/model/semconv/v1.5.0"
Expand All @@ -31,6 +32,12 @@ var (
conventions.AttributeServiceName: "service",
conventions.AttributeServiceVersion: "version",

// Containers
conventions.AttributeContainerID: "container_id",
conventions.AttributeContainerName: "container_name",
conventions.AttributeContainerImageName: "image_name",
conventions.AttributeContainerImageTag: "image_tag",

// Cloud conventions
// https://www.datadoghq.com/blog/tagging-best-practices/
conventions.AttributeCloudProvider: "cloud_provider",
Expand All @@ -39,19 +46,50 @@ var (

// ECS conventions
// https://github.com/DataDog/datadog-agent/blob/e081bed/pkg/tagger/collectors/ecs_extract.go
conventions.AttributeAWSECSTaskFamily: "task_family",
conventions.AttributeAWSECSClusterARN: "ecs_cluster_name",
"aws.ecs.task.revision": "task_version",
conventions.AttributeAWSECSTaskFamily: "task_family",
conventions.AttributeAWSECSTaskARN: "task_arn",
conventions.AttributeAWSECSClusterARN: "ecs_cluster_name",
conventions.AttributeAWSECSTaskRevision: "task_version",
conventions.AttributeAWSECSContainerARN: "ecs_container_name",

// Kubernetes resource name (via semantic conventions)
// https://github.com/DataDog/datadog-agent/blob/e081bed/pkg/util/kubernetes/const.go
conventions.AttributeK8SPodName: "pod_name",
conventions.AttributeK8SContainerName: "kube_container_name",
conventions.AttributeK8SClusterName: "kube_cluster_name",
conventions.AttributeK8SDeploymentName: "kube_deployment",
conventions.AttributeK8SReplicaSetName: "kube_replica_set",
conventions.AttributeK8SStatefulSetName: "kube_stateful_set",
conventions.AttributeK8SDaemonSetName: "kube_daemon_set",
conventions.AttributeK8SJobName: "kube_job",
conventions.AttributeK8SCronJobName: "kube_cronjob",
conventions.AttributeK8SNamespaceName: "kube_namespace",
conventions.AttributeK8SPodName: "pod_name",
}

// containerTagsAttributes contains a set of attributes that will be extracted as Datadog container tags.
containerTagsAttributes = []string{
conventions.AttributeContainerID,
conventions.AttributeContainerName,
conventions.AttributeContainerImageName,
conventions.AttributeContainerImageTag,
conventions.AttributeK8SContainerName,
conventions.AttributeK8SClusterName,
conventions.AttributeK8SDeploymentName,
conventions.AttributeK8SReplicaSetName,
conventions.AttributeK8SStatefulSetName,
conventions.AttributeK8SDaemonSetName,
conventions.AttributeK8SJobName,
conventions.AttributeK8SCronJobName,
conventions.AttributeK8SNamespaceName,
conventions.AttributeK8SPodName,
conventions.AttributeCloudProvider,
conventions.AttributeCloudRegion,
conventions.AttributeCloudAvailabilityZone,
conventions.AttributeAWSECSTaskFamily,
conventions.AttributeAWSECSTaskARN,
conventions.AttributeAWSECSClusterARN,
conventions.AttributeAWSECSTaskRevision,
conventions.AttributeAWSECSContainerARN,
}

// Kubernetes mappings defines the mapping between Kubernetes conventions (both general and Datadog specific)
Expand Down Expand Up @@ -120,3 +158,22 @@ func TagsFromAttributes(attrs pdata.AttributeMap) []string {

return tags
}

// ContainerTagFromAttributes extracts the value of _dd.tags.container from the given
// set of attributes.
func ContainerTagFromAttributes(attr map[string]string) string {
var str strings.Builder
for _, key := range containerTagsAttributes {
val, ok := attr[key]
if !ok {
continue
}
if str.Len() > 0 {
str.WriteByte(',')
}
str.WriteString(conventionsMapping[key])
str.WriteByte(':')
str.WriteString(val)
}
return str.String()
}
29 changes: 29 additions & 0 deletions exporter/datadogexporter/internal/attributes/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,32 @@ func TestTagsFromAttributesEmpty(t *testing.T) {

assert.Equal(t, []string{}, TagsFromAttributes(attrs))
}

func TestContainerTagFromAttributes(t *testing.T) {
attributeMap := map[string]string{
conventions.AttributeContainerName: "sample_app",
conventions.AttributeContainerImageTag: "sample_app_image_tag",
conventions.AttributeK8SContainerName: "kube_sample_app",
conventions.AttributeK8SReplicaSetName: "sample_replica_set",
conventions.AttributeK8SDaemonSetName: "sample_daemonset_name",
conventions.AttributeK8SPodName: "sample_pod_name",
conventions.AttributeCloudProvider: "sample_cloud_provider",
conventions.AttributeCloudRegion: "sample_region",
conventions.AttributeCloudAvailabilityZone: "sample_zone",
conventions.AttributeAWSECSTaskFamily: "sample_task_family",
conventions.AttributeAWSECSClusterARN: "sample_ecs_cluster_name",
conventions.AttributeAWSECSContainerARN: "sample_ecs_container_name",
"custom_tag": "example_custom_tag",
"": "empty_string_key",
"empty_string_val": "",
}

assert.Equal(t, "container_name:sample_app,image_tag:sample_app_image_tag,kube_container_name:kube_sample_app,kube_replica_set:sample_replica_set,kube_daemon_set:sample_daemonset_name,pod_name:sample_pod_name,cloud_provider:sample_cloud_provider,region:sample_region,zone:sample_zone,task_family:sample_task_family,ecs_cluster_name:sample_ecs_cluster_name,ecs_container_name:sample_ecs_container_name", ContainerTagFromAttributes(attributeMap))
}

func TestContainerTagFromAttributesEmpty(t *testing.T) {
var empty string
attributeMap := map[string]string{}

assert.Equal(t, empty, ContainerTagFromAttributes(attributeMap))
}
22 changes: 1 addition & 21 deletions exporter/datadogexporter/translate_traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"encoding/json"
"fmt"
"strconv"
"strings"
"time"

"github.com/DataDog/datadog-agent/pkg/trace/exportable/pb"
Expand Down Expand Up @@ -390,29 +389,10 @@ func aggregateSpanTags(span pdata.Span, datadogTags map[string]string) map[strin
})

// we don't want to normalize these tags since `_dd` is a special case
spanTags[tagContainersTags] = buildDatadogContainerTags(spanTags)
spanTags[tagContainersTags] = attributes.ContainerTagFromAttributes(spanTags)
return spanTags
}

// buildDatadogContainerTags returns container and orchestrator tags belonging to containerID
// as a comma delimeted list for datadog's special container tag key
func buildDatadogContainerTags(spanTags map[string]string) string {
var b strings.Builder

if val, ok := spanTags[conventions.AttributeContainerID]; ok {
b.WriteString(fmt.Sprintf("%s:%s,", "container_id", val))
}
if val, ok := spanTags[conventions.AttributeK8SPodName]; ok {
b.WriteString(fmt.Sprintf("%s:%s,", "pod_name", val))
}

if val, ok := spanTags[conventions.AttributeAWSECSTaskARN]; ok {
b.WriteString(fmt.Sprintf("%s:%s,", "task_arn", val))
}

return strings.TrimSuffix(b.String(), ",")
}

// inferDatadogTypes returns a string for the datadog type based on metadata
// in the otel span. DB semantic conventions state that what datadog
// would mark as a db or cache span type, otel marks as a CLIENT span kind, but
Expand Down

0 comments on commit e066c9c

Please sign in to comment.