Skip to content

Conversation

brettlangdon
Copy link
Member

@brettlangdon brettlangdon commented Oct 18, 2025

Description

4.0

This updates all tag and metric methods to explicitly be dict[str, str] and dict[str, int float] only.

This removes the value: Any from set_tag which allows us to remove a lot of checks for things like if it is a numeric type or not.

Testing

Risks

Additional Notes

Copy link
Contributor

github-actions bot commented Oct 18, 2025

CODEOWNERS have been resolved as:

releasenotes/notes/explicit-span-tag-typing-5fabfe5f2c458e7d.yaml       @DataDog/apm-python
ddtrace/_trace/context.py                                               @DataDog/apm-sdk-capabilities-python
ddtrace/_trace/span.py                                                  @DataDog/apm-sdk-capabilities-python
ddtrace/_trace/types.py                                                 @DataDog/apm-sdk-capabilities-python
ddtrace/_trace/utils_botocore/aws_payload_tagging.py                    @DataDog/apm-sdk-capabilities-python
ddtrace/appsec/ai_guard/_api_client.py                                  @DataDog/asm-python
ddtrace/contrib/internal/trace_utils.py                                 @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/contrib/internal/trace_utils_base.py                            @DataDog/apm-core-python @DataDog/apm-idm-python
ddtrace/debugging/_signal/tracing.py                                    @DataDog/debugger-python
ddtrace/internal/ci_visibility/filters.py                               @DataDog/ci-app-libraries
ddtrace/internal/ci_visibility/utils.py                                 @DataDog/ci-app-libraries
ddtrace/internal/opentelemetry/span.py                                  @DataDog/apm-sdk-capabilities-python
ddtrace/internal/opentelemetry/trace.py                                 @DataDog/apm-sdk-capabilities-python
ddtrace/internal/utils/http.py                                          @DataDog/apm-core-python
ddtrace/opentracer/span.py                                              @DataDog/apm-sdk-capabilities-python
ddtrace/propagation/http.py                                             @DataDog/apm-sdk-capabilities-python
ddtrace/settings/_config.py                                             @DataDog/apm-core-python
tests/tracer/test_encoders.py                                           @DataDog/apm-sdk-capabilities-python
tests/tracer/test_env_vars.py                                           @DataDog/apm-sdk-capabilities-python
tests/tracer/test_global_config.py                                      @DataDog/apm-sdk-capabilities-python
tests/tracer/test_http.py                                               @DataDog/apm-sdk-capabilities-python
tests/tracer/test_processors.py                                         @DataDog/apm-sdk-capabilities-python
tests/tracer/test_propagation.py                                        @DataDog/apm-sdk-capabilities-python
tests/tracer/test_span.py                                               @DataDog/apm-sdk-capabilities-python
tests/tracer/test_trace_utils.py                                        @DataDog/apm-sdk-capabilities-python
tests/tracer/test_tracer.py                                             @DataDog/apm-sdk-capabilities-python

@brettlangdon brettlangdon changed the title !chore(tracing): simplify Span tag/metric API typing chore!(tracing): simplify Span tag/metric API typing Oct 18, 2025
@brettlangdon brettlangdon changed the title chore!(tracing): simplify Span tag/metric API typing chore(tracing): simplify Span tag/metric API typing Oct 18, 2025
Copy link
Contributor

github-actions bot commented Oct 18, 2025

Bootstrap import analysis

Comparison of import times between this PR and base.

Summary

The average import time from this PR is: 209 ± 2 ms.

The average import time from base is: 205 ± 2 ms.

The import time difference between this PR and base is: 4.44 ± 0.08 ms.

Import time breakdown

The following import paths have appeared:

ddtrace.auto 0.614 ms (0.29%)
ddtrace 0.444 ms (0.21%)
ddtrace.trace 0.444 ms (0.21%)
ddtrace._trace.filters 0.255 ms (0.12%)
ddtrace._trace.processor 0.255 ms (0.12%)
ddtrace._trace.sampler 0.255 ms (0.12%)
ddtrace._trace.span 0.255 ms (0.12%)
ddtrace._trace.types 0.255 ms (0.12%)
ddtrace._trace.tracer 0.189 ms (0.09%)
ddtrace._trace.processor.resource_renaming 0.189 ms (0.09%)
ddtrace.ext.http 0.189 ms (0.09%)
ddtrace.bootstrap.sitecustomize 0.171 ms (0.08%)
ddtrace._trace.trace_handlers 0.171 ms (0.08%)
ddtrace.contrib.trace_utils 0.171 ms (0.08%)
ddtrace.contrib.internal.redis_utils 0.171 ms (0.08%)
ddtrace.ext.net 0.171 ms (0.08%)

The following import paths have disappeared:

ddtrace.auto 0.664 ms (0.32%)
ddtrace 0.664 ms (0.32%)
ddtrace.trace 0.664 ms (0.32%)
ddtrace._trace.filters 0.360 ms (0.17%)
ddtrace._trace.processor 0.360 ms (0.17%)
ddtrace._trace.sampler 0.360 ms (0.17%)
ddtrace._trace.span 0.360 ms (0.17%)
ddtrace.ext.http 0.194 ms (0.09%)
ddtrace.ext.net 0.166 ms (0.08%)
ddtrace._trace.context 0.304 ms (0.15%)
ddtrace._trace.types 0.304 ms (0.15%)

The following import paths have grown:

ddtrace.auto 4.954 ms (2.37%)
ddtrace 4.038 ms (1.93%)
ddtrace.trace 1.647 ms (0.79%)
ddtrace._trace.filters 1.399 ms (0.67%)
ddtrace._trace.processor 1.399 ms (0.67%)
ddtrace.internal.writer 1.042 ms (0.50%)
ddtrace.internal.writer.writer 1.042 ms (0.50%)
ddtrace.internal.agent 0.830 ms (0.40%)
ddtrace.internal.dogstatsd 0.149 ms (0.07%)
ddtrace.vendor.dogstatsd 0.127 ms (0.06%)
ddtrace.vendor.dogstatsd.base 0.127 ms (0.06%)
ddtrace.vendor.dogstatsd.container 0.092 ms (0.04%)
gzip 0.023 ms (0.01%)
ddtrace._trace.sampler 0.325 ms (0.16%)
ddtrace._trace.span 0.211 ms (0.10%)
ddtrace._trace._span_pointer 0.189 ms (0.09%)
hashlib 0.047 ms (0.02%)
_hashlib 0.047 ms (0.02%)
ddtrace.internal._rand 0.023 ms (0.01%)
ddtrace.internal.sampling 0.114 ms (0.05%)
ddtrace._trace.sampling_rule 0.114 ms (0.05%)
ddtrace._trace.tracer 0.071 ms (0.03%)
ddtrace.internal.processor.endpoint_call_counter 0.024 ms (0.01%)
ddtrace._trace.context 0.031 ms (0.01%)
ddtrace._monkey 1.218 ms (0.58%)
ddtrace.appsec._listeners 1.197 ms (0.57%)
ddtrace.internal.core 1.197 ms (0.57%)
ddtrace.internal.core.event_hub 1.197 ms (0.57%)
ddtrace.settings._config 1.151 ms (0.55%)
ddtrace.internal.gitmetadata 0.028 ms (0.01%)
ddtrace.ext.ci 0.028 ms (0.01%)
ddtrace.ext.git 0.028 ms (0.01%)
ddtrace._logger 1.172 ms (0.56%)
ddtrace.internal.telemetry 1.083 ms (0.52%)
ddtrace.internal.telemetry.writer 0.515 ms (0.25%)
ddtrace.settings._telemetry 0.139 ms (0.07%)
ddtrace.settings._inferred_base_service 0.014 ms (0.01%)
http.client 0.108 ms (0.05%)
ssl 0.088 ms (0.04%)
_ssl 0.042 ms (0.02%)
email.parser 0.020 ms (0.01%)
email.feedparser 0.020 ms (0.01%)
ddtrace.internal.packages 0.065 ms (0.03%)
_sysconfigdata__linux_x86_64-linux-gnu 0.043 ms (0.02%)
sysconfig 0.023 ms (0.01%)
ddtrace.internal.utils.http 0.044 ms (0.02%)
ddtrace.internal.endpoints 0.044 ms (0.02%)
ddtrace.internal.encoding 0.028 ms (0.01%)
ddtrace.internal._encoding 0.028 ms (0.01%)
ddtrace.internal.telemetry.metrics_namespaces 0.025 ms (0.01%)
ddtrace.internal.runtime 0.024 ms (0.01%)
uuid 0.024 ms (0.01%)
platform 0.024 ms (0.01%)
ddtrace.settings._agent 0.404 ms (0.19%)
ddtrace.settings 0.312 ms (0.15%)
ddtrace.vendor.debtcollector 0.312 ms (0.15%)
ddtrace.vendor 0.312 ms (0.15%)
ddtrace.internal.module 0.312 ms (0.15%)
ddtrace.internal.wrapping.context 0.286 ms (0.14%)
ddtrace.internal.utils.inspection 0.268 ms (0.13%)
ddtrace.internal.safety 0.268 ms (0.13%)
wrapt 0.268 ms (0.13%)
wrapt.importer 0.236 ms (0.11%)
importlib.metadata 0.236 ms (0.11%)
importlib.metadata._adapters 0.127 ms (0.06%)
email.message 0.127 ms (0.06%)
email.utils 0.127 ms (0.06%)
email.charset 0.083 ms (0.04%)
email.encoders 0.083 ms (0.04%)
socket 0.027 ms (0.01%)
_socket 0.027 ms (0.01%)
random 0.017 ms (0.01%)
zipfile 0.070 ms (0.03%)
shutil 0.031 ms (0.02%)
wrapt.__wrapt__ 0.032 ms (0.02%)
wrapt.wrappers 0.017 ms (0.01%)
wrapt._wrappers 0.015 ms (0.01%)
ddtrace.internal.wrapping 0.018 ms (0.01%)
bytecode 0.018 ms (0.01%)
bytecode.cfg 0.018 ms (0.01%)
bytecode.concrete 0.018 ms (0.01%)
struct 0.018 ms (0.01%)
_struct 0.018 ms (0.01%)
ddtrace.settings._core 0.092 ms (0.04%)
ddtrace.internal.native 0.061 ms (0.03%)
ddtrace.internal.native._native 0.061 ms (0.03%)
ddtrace.internal.utils.formats 0.117 ms (0.06%)
ddtrace.internal.compat 0.117 ms (0.06%)
pathlib 0.117 ms (0.06%)
ddtrace.settings._otel_remapper 0.048 ms (0.02%)
ddtrace.internal.logger 0.051 ms (0.02%)
dataclasses 0.051 ms (0.02%)
inspect 0.051 ms (0.02%)
logging 0.038 ms (0.02%)
ddtrace.bootstrap.sitecustomize 0.916 ms (0.44%)
ddtrace.bootstrap.preload 0.675 ms (0.32%)
multiprocessing 0.138 ms (0.07%)
multiprocessing.context 0.138 ms (0.07%)
multiprocessing.reduction 0.138 ms (0.07%)
pickle 0.138 ms (0.07%)
_pickle 0.101 ms (0.05%)
ddtrace.internal.symbol_db.remoteconfig 0.116 ms (0.06%)
ddtrace.internal.symbol_db.symbols 0.077 ms (0.04%)
ddtrace.settings.crashtracker 0.108 ms (0.05%)
ddtrace.internal.core.crashtracking 0.098 ms (0.05%)
ddtrace.settings.profiling 0.094 ms (0.05%)
ddtrace.vendor.psutil 0.067 ms (0.03%)
ddtrace.vendor.psutil._pslinux 0.022 ms (0.01%)
ddtrace.vendor.psutil._psutil_linux 0.022 ms (0.01%)
ddtrace.internal.datadog.profiling.ddup 0.027 ms (0.01%)
ddtrace.internal.datadog.profiling.ddup._ddup 0.027 ms (0.01%)
ddtrace.settings.dynamic_instrumentation 0.030 ms (0.01%)
ddtrace.debugging._import 0.028 ms (0.01%)
ddtrace.debugging._function.discovery 0.028 ms (0.01%)
ddtrace.internal.remoteconfig._connectors 0.024 ms (0.01%)
multiprocessing.sharedctypes 0.021 ms (0.01%)
ctypes 0.021 ms (0.01%)
_ctypes 0.021 ms (0.01%)
ddtrace.internal.remoteconfig.worker 0.017 ms (0.01%)
ddtrace._trace.trace_handlers 0.241 ms (0.12%)
ddtrace.contrib.trace_utils 0.118 ms (0.06%)
ddtrace.contrib.internal.trace_utils 0.118 ms (0.06%)
ddtrace.internal.utils.wrappers 0.094 ms (0.05%)
ddtrace._trace._inferred_proxy 0.080 ms (0.04%)
ddtrace.propagation.http 0.080 ms (0.04%)
ddtrace.internal._tagset 0.035 ms (0.02%)

The following import paths have shrunk:

ddtrace.auto 2.578 ms (1.23%)
ddtrace 1.698 ms (0.81%)
ddtrace.trace 1.221 ms (0.58%)
ddtrace._trace.filters 1.101 ms (0.53%)
ddtrace._trace.processor 1.101 ms (0.53%)
ddtrace.internal.writer 0.840 ms (0.40%)
ddtrace.internal.writer.writer 0.840 ms (0.40%)
ddtrace.internal.dist_computing.utils 0.763 ms (0.37%)
ddtrace.internal.dist_computing 0.712 ms (0.34%)
ddtrace.internal.dogstatsd 0.076 ms (0.04%)
ddtrace.vendor.dogstatsd 0.076 ms (0.04%)
ddtrace.vendor.dogstatsd.base 0.076 ms (0.04%)
ddtrace.vendor.dogstatsd.context 0.076 ms (0.04%)
ddtrace.vendor.dogstatsd.context_async 0.076 ms (0.04%)
ddtrace._trace.sampler 0.262 ms (0.13%)
ddtrace._trace.span 0.167 ms (0.08%)
ddtrace.internal.rate_limiter 0.095 ms (0.05%)
ddtrace._trace.pin 0.120 ms (0.06%)
ddtrace.internal._unpatched 0.031 ms (0.01%)
json 0.031 ms (0.01%)
json.decoder 0.031 ms (0.01%)
re 0.031 ms (0.01%)
enum 0.031 ms (0.01%)
types 0.031 ms (0.01%)
ddtrace.bootstrap.sitecustomize 0.880 ms (0.42%)
ddtrace.bootstrap.preload 0.793 ms (0.38%)
ddtrace.internal.remoteconfig.client 0.401 ms (0.19%)
ddtrace.internal.runtime.runtime_metrics 0.096 ms (0.05%)
ddtrace._trace.trace_handlers 0.087 ms (0.04%)
ddtrace.contrib.trace_utils 0.087 ms (0.04%)
ddtrace.contrib.internal.trace_utils_async 0.087 ms (0.04%)

Copy link
Contributor

@mabdinur mabdinur left a comment

Choose a reason for hiding this comment

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

With this change encoding spans might fail if set_tag is called on an unsupported type. Should we update span encoding to make sure that we don't drop the full span when encoding fails for a particular tag?

return self._meta_struct.get(key, None)

def set_tag_str(self, key: _TagNameType, value: Text) -> None:
def set_tag_str(self, key: str, value: str) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we deprecate/internalize this method? Ideally we should expose one public API for setting tags. If the overhead of handling sampling and service naming logic is significant we can move this logic out of set tags in v5.0

Copy link
Member Author

Choose a reason for hiding this comment

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

I have another PR to deprecate and internalize it. With the changes to set_tag we might be able to just get rid of it entirely.

# Ensure we do not have the same key in both meta and metrics
self._metrics.pop(key, None)

def set_struct_tag(self, key: str, value: Dict[str, Any]) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this method be public or is this functionality internal to the ddtrace library

Copy link
Member Author

Choose a reason for hiding this comment

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

there is another PR to deprecate and internalize this

return
elif key == SERVICE_KEY:
self.service = value
elif key == SERVICE_VERSION_KEY:
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we handle these tags in a trace processor or in Logs backend? It seems unnecessary to do this check every time set_tag is called.

Copy link
Member Author

Choose a reason for hiding this comment

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

good question... probably, but this change is started to get bloated a bit anyways, so maybe better to handle as a follow up.

it is an internal implementation detail, so... 🤷🏻

@brettlangdon brettlangdon requested review from a team as code owners October 20, 2025 17:59
@brettlangdon brettlangdon requested a review from wantsui October 20, 2025 17:59
@brettlangdon
Copy link
Member Author

With this change encoding spans might fail if set_tag is called on an unsupported type. Should we update span encoding to make sure that we don't drop the full span when encoding fails for a particular tag?

@mabdinur yeah, this is one of the bigger hidden issues with this change, since if someone doesn't notice the type changes and was doing set_tag("numeric", 1), then things are going to start to fail to encode. we could update encoding to not fail entirely, but to just skip that specific tag (there are some challenges with it, like we don't have a way to propagate that multiple handled issues happened during encoding, and not all span properties support dynamic sizing, e.g. pack map of size 5, but then we only actually pack 4 items because one was a numeric).

the other approach we could take is to get rid of set_metric and just have set_tag(key: str, value: str | int | float | None) -> None: and get_tag(key: str) -> str | int | float | None:

then splitting into meta/metrics for v0.4/v0.5 is an internal implementation detail only.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants