Skip to content

[receiver/datadogreceiver] Add EnableMultiTagParsing feature gate#44747

Merged
atoulme merged 3 commits intoopen-telemetry:mainfrom
discord:datadogreceiver/multi-tag-parsing
Dec 8, 2025
Merged

[receiver/datadogreceiver] Add EnableMultiTagParsing feature gate#44747
atoulme merged 3 commits intoopen-telemetry:mainfrom
discord:datadogreceiver/multi-tag-parsing

Conversation

@goakley
Copy link
Contributor

@goakley goakley commented Dec 4, 2025

Description

Datadog's metric model treats tags on data points as arbitrary strings. However, the product has special support for tags formatted as a key:value string. key:value tags allow slicing metrics in powerful ways and are core to many of Datadog's offerings.

Since Datadog tags are arbitrary strings, there's nothing to stop users from recording tags with the same key: prefix on the same data point. In fact, Datadog's own integrations can do this: The kube_service tag from the kubelet integration will have one entry per Kubernetes service that fronts a pod. (An example set of tags might look something like [...,"kube_service:datadog-cluster-agent-metrics-api","kube_service:datadog-cluster-agent-admission-controller",...]).

Currently, when the datadogreceiver receives multiple tags for a data point that share the same key, it only retains the last key:value pair as an OpenTelemetry attribe. In the above example, the attribute map would have an entry kube_service = datadog-cluster-agent-admission-controller - "datadog-cluster-agent-metrics-api" would have been discarded.

This change introduces a feature gate that changes the tag parsing behaviour to support tags that share the same key. Instead of overwriting the previous Str entry in the attribute map, it converts the attribute map to a Slice and stores all values for that key. This conversion only happens if more than one value is encountered for the same key, so even with the feature flag enabled, there will not be any behaviour change for users who do not send tags that share the same key.

I chose to implement this as a feature flag instead of a config setting because I believe this is technically correct behaviour, and should be the default in some future release of the OpenTelemetry Collector. Feedback on this decision is appreciated.

Testing

Unit tests have been added against tagsToAttributes to make sure attribute conversion works as expected both with and without the feature gate. The collector has been built and run locally, confirming that exporters receive the expected (slice) attributes when multiple tags with the same key are received.

Documentation

The README for the receiver now describes how tags are processed, both with and without the feature gate.

Datadog's metric model treats tags on data points as arbitrary strings.
However, the product has special support for tags formatted as a `key:value` string.
`key:value` tags allow slicing metrics in powerful ways and are core to many of Datadog's offerings.

Since Datadog tags are arbitrary strings, there's nothing to stop users from recording tags with the same `key:` prefix on the same data point.
In fact, Datadog's own integrations can do this: The `kube_service` tag from [the `kubelet` integration](https://docs.datadoghq.com/containers/kubernetes/tag/) will have one entry per Kubernetes service that fronts a pod.
(An example set of tags might look something like `[...,"kube_service:datadog-cluster-agent-metrics-api","kube_service:datadog-cluster-agent-admission-controller",...]`).

Currently, when the `datadogreceiver` receives multiple tags for a data point that share the same key, it only retains the last `key:value` pair as an OpenTelemetry attribe.
In the above example, the attribute map would have an entry `kube_service = datadog-cluster-agent-admission-controller` - "datadog-cluster-agent-metrics-api" would have been discarded.

This change introduces a feature gate that changes the tag parsing behaviour to support tags that share the same key.
Instead of overwriting the previous `Str` entry in the attribute map, it converts the attribute map to a `Slice` and stores all values for that key.
This conversion only happens if more than one value is encountered for the same key, so even with the feature flag enabled, there will not be any behaviour change for users who do not send tags that share the same key.

I chose to implement this as a feature flag instead of a config setting because I believe this is technically correct behaviour, and should be the default in some future release of the OpenTelemetry Collector.
Feedback on this decision is appreciated.
@MovieStoreGuy
Copy link
Contributor

This makes sense to me, and I don't see any issue with this approach.

Thank you for putting it behind a feature gate to begin with :)

@atoulme atoulme changed the title [receiver/datadogreciever] Add EnableMultiTagParsing feature gate [receiver/datadogreceiver] Add EnableMultiTagParsing feature gate Dec 8, 2025
@atoulme atoulme merged commit 3176aef into open-telemetry:main Dec 8, 2025
189 checks passed
@github-actions github-actions bot added this to the next release milestone Dec 8, 2025
@otelbot
Copy link
Contributor

otelbot bot commented Dec 8, 2025

Thank you for your contribution @goakley! 🎉 We would like to hear from you about your experience contributing to OpenTelemetry by taking a few minutes to fill out this survey. If you are getting started contributing, you can also join the CNCF Slack channel #opentelemetry-new-contributors to ask for guidance and get help.

@goakley goakley deleted the datadogreceiver/multi-tag-parsing branch December 9, 2025 19:08
goakley added a commit to discord/opentelemetry-collector-contrib that referenced this pull request Dec 9, 2025
…` feature gate

The pair to open-telemetry#44747, this gets us on the way to converting OpenTelemetry attribute slices to individual Datadog tags.

Currently when exporting to Datadog, attribute slices are converted to a "json-style" string, which is then turned into a valid Datadog tag.
For example, the attribute `{kube_service = ["datadog-cluster-agent-admission-controller", "datadog-cluster-agent-metrics-api"]}`
appears in Datadog as the tag `"kube_service:_datadog-cluster-agent-admission-controller_datadog-cluster-agent-metrics-api"`
(the underscores are the result of converting the `[,]` json-style characters to [safe tag characters](https://docs.datadoghq.com/getting_started/tagging/#define-tags)).

Following the same semantic convention as in open-telemetry#44747, slice attributes should instead be converted to individual tags.
The above example would instead render in Datadog as the tags `"kube_service:datadog-cluster-agent-admission-controller","kube_service:datadog-cluster-agent-metrics-api"`.
Datadog supports this sort of tag-key overlap on data points, and in fact uses it on purpose in [some of](https://docs.datadoghq.com/containers/kubernetes/tag/) its own integrations.

This new behaviour is assumed to be the "correct" conversion behaviour, so it is implemented as a feature gate that will eventually become the default exporter behaviour (instead of being implemented as a config setting).

Unfortuantely, there is an annoying circular dependency to deal with while implementing this.
The `serializerexporter` package used by the exporter is part of [the `datadog-agent` repository](github.com/DataDog/datadog-agent/),
but that package [depends on logic](https://github.com/DataDog/datadog-agent/blob/7.72.4/comp/otelcol/otlp/components/exporter/serializerexporter/factory.go#L22) in the `pkg/datadog` package of _this_ repository.
To handle the cycle, the full change will be implemented as follows:

1. Add the feature gate for use in the `serializerexporter` package (this PR)
2. Update the logic in the `serializerexporter` package in the other repository
3. Update the logic in the `datadogexporter` package in this repository

This creates a weird situation where the feature gate will do nothing for a period of time.
I'm not sure if there's a good way to deal with this situation, other than to not document the feature gate until it's implemented.
goakley added a commit to discord/opentelemetry-collector-contrib that referenced this pull request Dec 9, 2025
…pen-telemetry#44747)

Datadog's metric model treats tags on data points as arbitrary strings.
However, the product has special support for tags formatted as a
`key:value` string. `key:value` tags allow slicing metrics in powerful
ways and are core to many of Datadog's offerings.

Since Datadog tags are arbitrary strings, there's nothing to stop users
from recording tags with the same `key:` prefix on the same data point.
In fact, Datadog's own integrations can do this: The `kube_service` tag
from [the `kubelet`
integration](https://docs.datadoghq.com/containers/kubernetes/tag/) will
have one entry per Kubernetes service that fronts a pod. (An example set
of tags might look something like
`[...,"kube_service:datadog-cluster-agent-metrics-api","kube_service:datadog-cluster-agent-admission-controller",...]`).

Currently, when the `datadogreceiver` receives multiple tags for a data
point that share the same key, it only retains the last `key:value` pair
as an OpenTelemetry attribe. In the above example, the attribute map
would have an entry `kube_service =
datadog-cluster-agent-admission-controller` -
"datadog-cluster-agent-metrics-api" would have been discarded.

This change introduces a feature gate that changes the tag parsing
behaviour to support tags that share the same key. Instead of
overwriting the previous `Str` entry in the attribute map, it converts
the attribute map to a `Slice` and stores all values for that key. This
conversion only happens if more than one value is encountered for the
same key, so even with the feature flag enabled, there will not be any
behaviour change for users who do not send tags that share the same key.

I chose to implement this as a feature flag instead of a config setting
because I believe this is technically correct behaviour, and should be
the default in some future release of the OpenTelemetry Collector.
Feedback on this decision is appreciated.

Unit tests have been added against `tagsToAttributes` to make sure
attribute conversion works as expected both with and without the feature
gate. The collector has been built and run locally, confirming that
exporters receive the expected (slice) attributes when multiple tags
with the same key are received.

The README for the receiver now describes how tags are processed, both
with and without the feature gate.
goakley added a commit to discord/opentelemetry-collector-contrib that referenced this pull request Dec 9, 2025
…` feature gate

The pair to open-telemetry#44747, this gets us on the way to converting OpenTelemetry attribute slices to individual Datadog tags.

Currently when exporting to Datadog, attribute slices are converted to a "json-style" string, which is then turned into a valid Datadog tag.
For example, the attribute `{kube_service = ["datadog-cluster-agent-admission-controller", "datadog-cluster-agent-metrics-api"]}`
appears in Datadog as the tag `"kube_service:_datadog-cluster-agent-admission-controller_datadog-cluster-agent-metrics-api"`
(the underscores are the result of converting the `[,]` json-style characters to [safe tag characters](https://docs.datadoghq.com/getting_started/tagging/#define-tags)).

Following the same semantic convention as in open-telemetry#44747, slice attributes should instead be converted to individual tags.
The above example would instead render in Datadog as the tags `"kube_service:datadog-cluster-agent-admission-controller","kube_service:datadog-cluster-agent-metrics-api"`.
Datadog supports this sort of tag-key overlap on data points, and in fact uses it on purpose in [some of](https://docs.datadoghq.com/containers/kubernetes/tag/) its own integrations.

This new behaviour is assumed to be the "correct" conversion behaviour, so it is implemented as a feature gate that will eventually become the default exporter behaviour (instead of being implemented as a config setting).

Unfortuantely, there is an annoying circular dependency to deal with while implementing this.
The `serializerexporter` package used by the exporter is part of [the `datadog-agent` repository](github.com/DataDog/datadog-agent/),
but that package [depends on logic](https://github.com/DataDog/datadog-agent/blob/7.72.4/comp/otelcol/otlp/components/exporter/serializerexporter/factory.go#L22) in the `pkg/datadog` package of _this_ repository.
To handle the cycle, the full change will be implemented as follows:

1. Add the feature gate for use in the `serializerexporter` package (this PR)
2. Update the logic in the `serializerexporter` package in the other repository
3. Update the logic in the `datadogexporter` package in this repository

This creates a weird situation where the feature gate will do nothing for a period of time.
I'm not sure if there's a good way to deal with this situation, other than to not document the feature gate until it's implemented.
goakley added a commit to discord/opentelemetry-collector-contrib that referenced this pull request Dec 9, 2025
…` feature gate

The pair to open-telemetry#44747, this gets us on the way to converting OpenTelemetry attribute slices to individual Datadog tags.

Currently when exporting to Datadog, attribute slices are converted to a "json-style" string, which is then turned into a valid Datadog tag.
For example, the attribute `{kube_service = ["datadog-cluster-agent-admission-controller", "datadog-cluster-agent-metrics-api"]}`
appears in Datadog as the tag `"kube_service:_datadog-cluster-agent-admission-controller_datadog-cluster-agent-metrics-api"`
(the underscores are the result of converting the `[,]` json-style characters to [safe tag characters](https://docs.datadoghq.com/getting_started/tagging/#define-tags)).

Following the same semantic convention as in open-telemetry#44747, slice attributes should instead be converted to individual tags.
The above example would instead render in Datadog as the tags `"kube_service:datadog-cluster-agent-admission-controller","kube_service:datadog-cluster-agent-metrics-api"`.
Datadog supports this sort of tag-key overlap on data points, and in fact uses it on purpose in [some of](https://docs.datadoghq.com/containers/kubernetes/tag/) its own integrations.

This new behaviour is assumed to be the "correct" conversion behaviour, so it is implemented as a feature gate that will eventually become the default exporter behaviour (instead of being implemented as a config setting).

Unfortuantely, there is an annoying circular dependency to deal with while implementing this.
The `serializerexporter` package used by the exporter is part of [the `datadog-agent` repository](github.com/DataDog/datadog-agent/),
but that package [depends on logic](https://github.com/DataDog/datadog-agent/blob/7.72.4/comp/otelcol/otlp/components/exporter/serializerexporter/factory.go#L22) in the `pkg/datadog` package of _this_ repository.
To handle the cycle, the full change will be implemented as follows:

1. Add the feature gate for use in the `serializerexporter` package (this PR)
2. Update the logic in the `serializerexporter` package in the other repository
3. Update the logic in the `datadogexporter` package in this repository

This creates a weird situation where the feature gate will do nothing for a period of time.
I'm not sure if there's a good way to deal with this situation, other than to not document the feature gate until it's implemented.
songy23 pushed a commit that referenced this pull request Dec 11, 2025
…` feature gate (#44859)

#### Description

The pair to #44747, this gets us on the way to converting OpenTelemetry
attribute slices to individual Datadog tags.

Currently when exporting to Datadog, attribute slices are converted to a
"json-style" string, which is then turned into a valid Datadog tag. For
example, the attribute `{kube_service =
["datadog-cluster-agent-admission-controller",
"datadog-cluster-agent-metrics-api"]}` appears in Datadog as the tag
`"kube_service:_datadog-cluster-agent-admission-controller_datadog-cluster-agent-metrics-api"`
(the underscores are the result of converting the `[,]` json-style
characters to [safe tag
characters](https://docs.datadoghq.com/getting_started/tagging/#define-tags)).

Following the same semantic convention as in #44747, slice attributes
should instead be converted to individual tags. The above example would
instead render in Datadog as the tags
`"kube_service:datadog-cluster-agent-admission-controller","kube_service:datadog-cluster-agent-metrics-api"`.
Datadog supports this sort of tag-key overlap on data points, and in
fact uses it on purpose in [some
of](https://docs.datadoghq.com/containers/kubernetes/tag/) its own
integrations.

This new behaviour is assumed to be the "correct" conversion behaviour,
so it is implemented as a feature gate that will eventually become the
default exporter behaviour (instead of being implemented as a config
setting).

Unfortunately, there is an annoying circular dependency to deal with
while implementing this. The `serializerexporter` package used by the
exporter is part of [the `datadog-agent`
repository](https://github.com/DataDog/datadog-agent/), but that package
[depends on
logic](https://github.com/DataDog/datadog-agent/blob/7.72.4/comp/otelcol/otlp/components/exporter/serializerexporter/factory.go#L22)
in the `pkg/datadog` package of _this_ repository. To handle the cycle,
the full change will be implemented as follows:

1. Add the feature gate for use in the `serializerexporter` package
(this PR)
2. Update the logic in the `serializerexporter` package in the other
repository
3. Update the logic in the `datadogexporter` package in this repository

This creates a weird situation where the feature gate will do nothing
for a period of time. I'm not sure if there's a good way to deal with
this situation, other than to not document the feature gate until it's
implemented.

#### Testing

This PR contains none of the exporter logic (see above) so there is
nothing to test.

#### Documentation

The feature flag is currently unimplemented (see above) so there is no
new functionality to document.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants