1818import random
1919import time
2020from contextlib import contextmanager
21+ from contextvars import ContextVar
2122from datetime import datetime
2223from typing import TYPE_CHECKING , Any
2324
@@ -85,6 +86,10 @@ class _StatusCodeStub:
8586
8687# The instrumentation scope name used when creating spans.
8788_TRACER_NAME = "durabletask"
89+ _SPAN_EMISSION_SUPPRESSED : ContextVar [bool ] = ContextVar (
90+ "durabletask_span_emission_suppressed" ,
91+ default = False ,
92+ )
8893
8994
9095# ---------------------------------------------------------------------------
@@ -226,6 +231,35 @@ def extract_trace_context(proto_ctx: pb.TraceContext | None) -> Any | None:
226231 return ctx
227232
228233
234+ @contextmanager
235+ def use_trace_context (trace_context : pb .TraceContext | None ):
236+ """Make a protobuf trace context current without creating a span."""
237+ parent_ctx = extract_trace_context (trace_context )
238+ if parent_ctx is None :
239+ yield
240+ return
241+
242+ token = otel_context .attach (parent_ctx )
243+ try :
244+ yield
245+ finally :
246+ otel_context .detach (token )
247+
248+
249+ @contextmanager
250+ def suppress_span_emission (suppress : bool = True ):
251+ """Temporarily suppress spans emitted by this module."""
252+ if not suppress :
253+ yield
254+ return
255+
256+ token = _SPAN_EMISSION_SUPPRESSED .set (True )
257+ try :
258+ yield
259+ finally :
260+ _SPAN_EMISSION_SUPPRESSED .reset (token )
261+
262+
229263@contextmanager
230264def start_span (
231265 name : str ,
@@ -254,6 +288,11 @@ def start_span(
254288 yield None
255289 return
256290
291+ if _SPAN_EMISSION_SUPPRESSED .get ():
292+ with use_trace_context (trace_context ):
293+ yield None
294+ return
295+
257296 parent_ctx = extract_trace_context (trace_context )
258297
259298 if kind is None :
@@ -313,7 +352,7 @@ def emit_orchestration_span(
313352 Falls back to ``tracer.start_span()`` (which generates its own
314353 span ID) when *orchestration_trace_context* is ``None``.
315354 """
316- if not _OTEL_AVAILABLE :
355+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
317356 return
318357
319358 span_name = create_span_name (TASK_TYPE_ORCHESTRATION , name , version )
@@ -529,7 +568,7 @@ def generate_client_trace_context(
529568 returned the caller should fall back to the orchestration's own
530569 trace context as the parent for downstream spans.
531570 """
532- if not _OTEL_AVAILABLE :
571+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
533572 return None
534573 if parent_trace_context is None :
535574 return None
@@ -588,7 +627,7 @@ def emit_client_span(
588627 SERVER span remains connected to the orchestration span via the
589628 fallback parenting established in :func:`generate_client_trace_context`.
590629 """
591- if not _OTEL_AVAILABLE :
630+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
592631 return
593632
594633 # SDK-internal imports — see _is_deferred_span_capable() for details.
@@ -698,7 +737,7 @@ def emit_timer_span(
698737 When *parent_trace_context* is provided the span is created as a
699738 child of that context; otherwise it inherits the ambient context.
700739 """
701- if not _OTEL_AVAILABLE :
740+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
702741 return
703742
704743 span_name = create_timer_span_name (orchestration_name )
@@ -741,7 +780,7 @@ def emit_event_raised_span(
741780 When *parent_trace_context* is provided the span is created as a
742781 child of that context; otherwise it inherits the ambient context.
743782 """
744- if not _OTEL_AVAILABLE :
783+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
745784 return
746785
747786 span_name = create_span_name (SPAN_TYPE_ORCHESTRATION_EVENT , event_name )
@@ -787,7 +826,7 @@ def start_create_orchestration_span(
787826 Yields the span; caller should capture the trace context after entering
788827 the span context so it can be injected into the gRPC request.
789828 """
790- if not _OTEL_AVAILABLE :
829+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
791830 yield None
792831 return
793832
@@ -815,7 +854,7 @@ def start_raise_event_span(
815854 target_instance_id : str ,
816855):
817856 """Context manager for a Producer span when raising an event from the client."""
818- if not _OTEL_AVAILABLE :
857+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
819858 yield None
820859 return
821860
@@ -845,7 +884,7 @@ def reconstruct_trace_context(
845884 the span ID with *span_id*. This is used to reuse a pre-determined
846885 orchestration span ID across replays.
847886 """
848- if not _OTEL_AVAILABLE :
887+ if not _OTEL_AVAILABLE or _SPAN_EMISSION_SUPPRESSED . get () :
849888 return None
850889
851890 parsed = _parse_traceparent (parent_trace_context .traceParent )
@@ -869,7 +908,7 @@ def build_orchestration_trace_context(
869908 replays so that all dispatches produce a consistent orchestration
870909 SERVER span.
871910 """
872- if start_time_ns is None :
911+ if start_time_ns is None or _SPAN_EMISSION_SUPPRESSED . get () :
873912 return None
874913
875914 ctx = pb .OrchestrationTraceContext ()
0 commit comments