Skip to content

Commit 1eda3bc

Browse files
authored
refactor: remove ExecutionContext._inner_span (#19084)
This change removes the `_inner_span` attribute from `ddtrace.core.ExecutionContext`, replacing it with an `_inner_span` datum stored via existing mechanisms. This eliminates Spans as a special case in the Core API, treating them like any other piece of data stored in the context tree. `span_bus` is introduced containing two of the span-related helper functions from `core`, indicating the separateness of Span management from Core. Note that while this separation does fix the import-time coupling between `_trace` and `core`, it does not address the functional dependence of certain products on `_trace`, specifically those that use Spans as context tracking. Co-authored-by: emmett.butler <emmett.butler@datadoghq.com>
1 parent 100519b commit 1eda3bc

57 files changed

Lines changed: 296 additions & 235 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitlab/benchmarks/bp-runner.microbenchmarks.fail-on-breach.template.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,13 @@ experiments:
123123
# errortrackingflasksqli
124124
- name: errortrackingflasksqli-errortracking-enabled-all
125125
thresholds:
126-
- execution_time < 2.32 ms
126+
- execution_time < 2.34 ms
127127
- name: errortrackingflasksqli-errortracking-enabled-user
128128
thresholds:
129129
- execution_time < 2.85 ms
130130
- name: errortrackingflasksqli-tracer-enabled
131131
thresholds:
132-
- execution_time < 2.32 ms
132+
- execution_time < 2.33 ms
133133

134134
# flask_simple
135135
- name: flasksimple-tracer

benchmarks/events_api/scenario.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ddtrace._trace.trace_handlers import _start_span
1010
from ddtrace.constants import SPAN_KIND
1111
from ddtrace.internal import core
12+
from ddtrace.internal import span_bus
1213
from ddtrace.internal.constants import COMPONENT
1314
from ddtrace.internal.core.events import Event
1415
from ddtrace.internal.core.events import event_field
@@ -50,7 +51,7 @@ def benchmark_core_api(loops):
5051
# Register benchmark specific handlers
5152
def _context_started_handler(ctx: core.ExecutionContext) -> None:
5253
_start_span(ctx, call_trace=True)
53-
span = ctx.span
54+
span = span_bus.span_from_context(ctx)
5455
span._set_attribute("http.url", ctx.get_item("url"))
5556
span._set_attribute("http.method", ctx.get_item("method"))
5657
span._set_attribute("http.status_code", ctx.get_item("status_code"))
@@ -82,7 +83,7 @@ class SpanContextSubscriber(TracingSubscriber):
8283

8384
@classmethod
8485
def on_started(cls, ctx: core.ExecutionContext) -> None:
85-
span = ctx.span
86+
span = span_bus.span_from_context(ctx)
8687
event: BenchmarkTracingEvent = ctx.event
8788
span._set_attribute("http.url", event.url)
8889
span._set_attribute("http.method", event.method)

ddtrace/_trace/subscribers/_base.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from ddtrace.internal import core
1818
from ddtrace.internal.constants import COMPONENT
1919
from ddtrace.internal.core.subscriber import ContextSubscriber
20+
from ddtrace.internal.span_bus import span_from_context
21+
from ddtrace.internal.span_bus import store_span_on_context
2022
from ddtrace.trace import tracer
2123

2224

@@ -34,7 +36,7 @@ def _finish_span(
3436
Once every integration adopts the events API, trace_handlers._finish_span
3537
should be completely removed.
3638
"""
37-
span = ctx.span
39+
span = span_from_context(ctx)
3840
if not span:
3941
return
4042

@@ -98,7 +100,7 @@ def _start_span(ctx: core.ExecutionContext[TracingEventType]) -> Span:
98100
span._set_attribute(_SPAN_MEASURED_KEY, 1)
99101

100102
set_service_and_source(span, ctx.get_item("service"), integration_config or dict())
101-
ctx.span = span
103+
store_span_on_context(ctx, span)
102104

103105
if config._inferred_proxy_services_enabled:
104106
# TODO(IDM): Subscriber should be added for Inferred Proxy span handling
@@ -120,12 +122,12 @@ class MySpanSubscriber(SpanTracingSubscriber):
120122
121123
@classmethod
122124
def on_started(cls, ctx):
123-
ctx.span.set_tag("custom.tag", "value")
125+
span_from_context(ctx).set_tag("custom.tag", "value")
124126
125127
@classmethod
126128
def on_ended(cls, ctx, exc_info):
127129
if exc_info[1]:
128-
ctx.span.set_tag("error", True)
130+
span_from_context(ctx).set_tag("error", True)
129131
"""
130132

131133
# Register here events that just create / finish spans

ddtrace/_trace/subscribers/aws_durable.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from ddtrace.contrib._events.aws_durable import AwsDurableOperationEvent
99
from ddtrace.ext import aws_durable
1010
from ddtrace.internal import core
11+
from ddtrace.internal.span_bus import span_from_context
1112

1213

1314
class AwsDurableExecuteSubscriber(TracingSubscriber):
@@ -17,9 +18,11 @@ class AwsDurableExecuteSubscriber(TracingSubscriber):
1718
def on_started(cls, ctx: core.ExecutionContext) -> None:
1819
event: AwsDurableExecuteEvent = ctx.event
1920
if event.execution_arn:
20-
ctx.span._set_attribute(aws_durable.TAG_EXECUTION_ARN, event.execution_arn)
21+
span_from_context(ctx)._set_attribute(aws_durable.TAG_EXECUTION_ARN, event.execution_arn)
2122
if event.is_replay_execution is not None:
22-
ctx.span._set_attribute(aws_durable.TAG_REPLAYED, "true" if event.is_replay_execution else "false")
23+
span_from_context(ctx)._set_attribute(
24+
aws_durable.TAG_REPLAYED, "true" if event.is_replay_execution else "false"
25+
)
2326

2427
@classmethod
2528
def on_ended(
@@ -34,7 +37,7 @@ def on_ended(
3437
status = "failed"
3538
else:
3639
status = "succeeded"
37-
ctx.span._set_attribute(aws_durable.TAG_INVOCATION_STATUS, status)
40+
span_from_context(ctx)._set_attribute(aws_durable.TAG_INVOCATION_STATUS, status)
3841

3942

4043
class AwsDurableInvokeSubscriber(TracingSubscriber):
@@ -43,9 +46,9 @@ class AwsDurableInvokeSubscriber(TracingSubscriber):
4346
@classmethod
4447
def on_started(cls, ctx: core.ExecutionContext) -> None:
4548
event: AwsDurableInvokeEvent = ctx.event
46-
ctx.span._set_attribute(aws_durable.TAG_INVOKE_FUNCTION_NAME, event.invoke_function_name)
49+
span_from_context(ctx)._set_attribute(aws_durable.TAG_INVOKE_FUNCTION_NAME, event.invoke_function_name)
4750
if event.name is not None:
48-
ctx.span._set_attribute(aws_durable.TAG_NAME, event.name)
51+
span_from_context(ctx)._set_attribute(aws_durable.TAG_NAME, event.name)
4952

5053
@classmethod
5154
def on_ended(
@@ -55,9 +58,9 @@ def on_ended(
5558
) -> None:
5659
event: AwsDurableInvokeEvent = ctx.event
5760
if event.replayed is not None:
58-
ctx.span._set_attribute(aws_durable.TAG_REPLAYED, "true" if event.replayed else "false")
61+
span_from_context(ctx)._set_attribute(aws_durable.TAG_REPLAYED, "true" if event.replayed else "false")
5962
if event.id is not None:
60-
ctx.span._set_attribute(aws_durable.TAG_ID, event.id)
63+
span_from_context(ctx)._set_attribute(aws_durable.TAG_ID, event.id)
6164

6265

6366
class AwsDurableOperationSubscriber(TracingSubscriber):
@@ -67,7 +70,7 @@ class AwsDurableOperationSubscriber(TracingSubscriber):
6770
def on_started(cls, ctx: core.ExecutionContext) -> None:
6871
event: AwsDurableOperationEvent = ctx.event
6972
if event.name is not None:
70-
ctx.span._set_attribute(aws_durable.TAG_NAME, event.name)
73+
span_from_context(ctx)._set_attribute(aws_durable.TAG_NAME, event.name)
7174

7275
@classmethod
7376
def on_ended(
@@ -77,8 +80,8 @@ def on_ended(
7780
) -> None:
7881
event: AwsDurableOperationEvent = ctx.event
7982
if event.replayed is not None:
80-
ctx.span._set_attribute(aws_durable.TAG_REPLAYED, "true" if event.replayed else "false")
83+
span_from_context(ctx)._set_attribute(aws_durable.TAG_REPLAYED, "true" if event.replayed else "false")
8184
if event.id is not None:
82-
ctx.span._set_attribute(aws_durable.TAG_ID, event.id)
85+
span_from_context(ctx)._set_attribute(aws_durable.TAG_ID, event.id)
8386
if event.operation_attempt is not None:
84-
ctx.span._set_attribute(aws_durable.TAG_OPERATION_ATTEMPT, event.operation_attempt)
87+
span_from_context(ctx)._set_attribute(aws_durable.TAG_OPERATION_ATTEMPT, event.operation_attempt)

ddtrace/_trace/subscribers/http_client.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ddtrace.contrib._events.http_client import HttpClientRequestEvent
1010
from ddtrace.internal import core
1111
from ddtrace.internal.logger import get_logger
12+
from ddtrace.internal.span_bus import span_from_context
1213
from ddtrace.propagation.http import HTTPPropagator
1314

1415

@@ -54,7 +55,7 @@ def on_started(cls, ctx: core.ExecutionContext) -> None:
5455
return
5556

5657
if trace_utils.distributed_tracing_enabled(event.integration_config) and event.request_headers is not None:
57-
HTTPPropagator.inject(ctx.span.context, cast(dict[str, str], event.request_headers))
58+
HTTPPropagator.inject(span_from_context(ctx).context, cast(dict[str, str], event.request_headers))
5859

5960
@classmethod
6061
def on_ended(
@@ -66,7 +67,7 @@ def on_ended(
6667

6768
try:
6869
trace_utils.set_http_meta(
69-
ctx.span,
70+
span_from_context(ctx),
7071
event.integration_config,
7172
method=event.request_method,
7273
url=event.request_url,

ddtrace/_trace/subscribers/llm.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from ddtrace.internal import core
88
from ddtrace.internal.constants import COMPONENT
99
from ddtrace.internal.logger import get_logger
10+
from ddtrace.internal.span_bus import span_from_context
1011

1112

1213
# Duplicated from ddtrace.llmobs._constants to avoid importing
@@ -30,7 +31,7 @@ class LlmTracingSubscriber(TracingSubscriber["LlmRequestEvent"]):
3031
@classmethod
3132
def on_started(cls, ctx: core.ExecutionContext["LlmRequestEvent"]) -> None:
3233
event: LlmRequestEvent = ctx.event
33-
span = ctx.span
34+
span = span_from_context(ctx)
3435

3536
# Remove component/span.kind tags set by _start_span — the old
3637
# BaseLLMIntegration.trace() never set these, so existing snapshot
@@ -65,7 +66,7 @@ def on_ended(
6566
"""
6667
event: LlmRequestEvent = ctx.event
6768
event.llmobs_integration.llmobs_set_tags(
68-
ctx.span,
69+
span_from_context(ctx),
6970
args=[],
7071
kwargs=event.request_kwargs,
7172
response=event.response,

ddtrace/_trace/subscribers/ray.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
from ddtrace.internal import core
4848
from ddtrace.internal.core.subscriber import Subscriber
4949
from ddtrace.internal.logger import get_logger
50+
from ddtrace.internal.span_bus import span_from_context
5051
from ddtrace.propagation.http import _TraceContext
5152
from ddtrace.trace import tracer
5253

@@ -64,7 +65,7 @@ def on_started(cls, ctx: core.ExecutionContext) -> None:
6465
event: RayJobEvent = ctx.event
6566
submission_id = event.submission_id
6667

67-
job_span = ctx.span
68+
job_span = span_from_context(ctx)
6869
_set_dist_ai_metrics(job_span)
6970
_set_runtime_context_attributes(job_span, submission_id)
7071
start_long_running_job(job_span)
@@ -92,7 +93,7 @@ def on_ended(
9293
exc_info: tuple[Optional[type], Optional[BaseException], Optional[TracebackType]],
9394
) -> None:
9495
event: RayJobEvent = ctx.event
95-
job_span = ctx.span
96+
job_span = span_from_context(ctx)
9697

9798
# a simple if event.submit_failed does not work on event_field
9899
if event.submit_failed is True:
@@ -113,7 +114,7 @@ class RayExecutionSubscriber(TracingSubscriber):
113114

114115
@classmethod
115116
def on_started(cls, ctx: core.ExecutionContext) -> None:
116-
span = ctx.span
117+
span = span_from_context(ctx)
117118
_set_dist_ai_metrics(span)
118119
_set_runtime_context_attributes(span)
119120

@@ -147,7 +148,7 @@ def on_ended(
147148
ctx: core.ExecutionContext,
148149
exc_info: tuple[Optional[type], Optional[BaseException], Optional[TracebackType]],
149150
) -> None:
150-
span = ctx.span
151+
span = span_from_context(ctx)
151152

152153
exc_type, exc_val, exc_tb = exc_info
153154
if exc_type is not None and exc_val is not None:
@@ -164,7 +165,7 @@ class RayCoreAPITracingSubscriber(TracingSubscriber):
164165
@classmethod
165166
def on_started(cls, ctx: core.ExecutionContext) -> None:
166167
event: RayCoreAPIEvent = ctx.event
167-
span = ctx.span
168+
span = span_from_context(ctx)
168169

169170
_set_dist_ai_metrics(span)
170171
_set_runtime_context_attributes(span)
@@ -179,7 +180,7 @@ def on_ended(
179180
exc_info: tuple[Optional[type], Optional[BaseException], Optional[TracebackType]],
180181
) -> None:
181182
event: RayCoreAPIEvent = ctx.event
182-
span = ctx.span
183+
span = span_from_context(ctx)
183184

184185
exc_type, exc_val, exc_tb = exc_info
185186
if exc_type is not None and exc_val is not None:
@@ -200,7 +201,7 @@ class RaySubmissionSubscriber(TracingSubscriber):
200201
@classmethod
201202
def on_started(cls, ctx: core.ExecutionContext) -> None:
202203
event: RaySubmissionEvent = ctx.event
203-
span = ctx.span
204+
span = span_from_context(ctx)
204205

205206
_set_dist_ai_metrics(span)
206207
_set_runtime_context_attributes(span)
@@ -250,7 +251,7 @@ def on_ended(
250251
exc_info: tuple[Optional[type], Optional[BaseException], Optional[TracebackType]],
251252
) -> None:
252253
event: RaySubmissionEvent = ctx.event
253-
span = ctx.span
254+
span = span_from_context(ctx)
254255

255256
status_tag = RAY_TASK_SUBMIT_STATUS if event.is_task_submission else RAY_ACTOR_METHOD_SUBMIT_STATUS
256257
span._set_attribute(status_tag, RAY_STATUS_ERROR if exc_info[1] is not None else RAY_STATUS_SUCCESS)

ddtrace/_trace/subscribers/web_framework.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from ddtrace.ext import http
1010
from ddtrace.internal import core
1111
from ddtrace.internal.logger import get_logger
12+
from ddtrace.internal.span_bus import span_from_context
1213

1314

1415
log = get_logger(__name__)
@@ -37,7 +38,7 @@ def on_ended(
3738
method = event.request_method
3839
res_headers = event.response_headers
3940

40-
span: Span = ctx.span
41+
span: Span = span_from_context(ctx)
4142

4243
# event.resource can be updated at span finish time
4344
if event.resource:

0 commit comments

Comments
 (0)