Skip to content

Allow embedding a Dag run in an external trace via run conf#69633

Merged
dstandish merged 8 commits into
apache:mainfrom
astronomer:embed-dagrun-external-trace-conf
Jul 13, 2026
Merged

Allow embedding a Dag run in an external trace via run conf#69633
dstandish merged 8 commits into
apache:mainfrom
astronomer:embed-dagrun-external-trace-conf

Conversation

@dstandish

@dstandish dstandish commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Lets a Dag run opt into being embedded inside an external trace instead of always starting its own root trace. A run triggered by an external system that already emits a trace — an upstream orchestrator, event pipeline, CI job, or another Airflow — can now appear as part of that larger workflow end-to-end.

A reserved run conf key, airflow/dagrun_parent_trace_context (a W3C traceparent string, or a {traceparent, tracestate} mapping), is read at run-creation time. When present, the run's trace carrier rides the external trace_id (so the whole run — dag_run span plus all task/worker spans — lives inside the external trace) and parent-based samplers inherit the external sampling decision; the emitted dag_run span nests under the external span. When the key is absent, the run remains a root trace exactly as before. Malformed/missing values are ignored rather than failing run creation.

This joins the existing per-run trace controls airflow/trace_sampled and airflow/task_span_detail_level, and is documented alongside them. Supersedes the auto-nesting approach in #67424 (see the redesigned spec on the issue).

closes: #67210


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@xBis7 xBis7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dstandish I tested it manually with a python script that starts a root span and then triggers a dag with the context passed in the --conf param. It works as expected!

@xBis7 xBis7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I added some small comments but generally it looks good and I tested it manually and I don't see any issues.

Comment thread airflow-core/src/airflow/models/dagrun.py Outdated
Comment thread shared/observability/tests/observability/test_traces.py Outdated
Comment thread shared/observability/src/airflow_shared/observability/traces/__init__.py Outdated

@xBis7 xBis7 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dstandish Thanks for addressing all of my comments. LGTM!

Comment thread airflow-core/docs/administration-and-deployment/logging-monitoring/traces.rst Outdated
Comment thread airflow-core/newsfragments/69633.feature.rst Outdated

@ashb ashb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall looks good, but a number of nits, chiefly the error handling one

Comment thread airflow-core/src/airflow/models/dagrun.py Outdated
Comment thread airflow-core/src/airflow/models/dagrun.py Outdated
Comment thread airflow-core/src/airflow/models/dagrun.py Outdated
Comment thread airflow-core/src/airflow/models/dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
Comment thread airflow-core/tests/unit/models/test_dagrun.py Outdated
dstandish and others added 8 commits July 13, 2026 13:40
A run triggered by an external system that already emits a trace (an upstream
orchestrator, event pipeline, CI job, or another Airflow) currently always
starts its own root trace, so the Airflow run cannot be seen as part of that
larger workflow. A reserved run conf key, airflow/parent_trace_context, lets a
run opt into embedding under a supplied W3C traceparent: the whole run rides the
external trace and parent-based samplers inherit its sampling decision. Absent
the key, the run stays a root trace as before.

closes: apache#67210
Guards the isinstance filter that keeps a malformed tracestate value
from raising inside OpenTelemetry's TraceState.from_header instead of
being ignored, per the function's documented contract.
Reduces duplication across three tests that only varied the sampler,
parent sampled-flag, and force_sampled input.
Distinguishes it from future non-dag-run traces (e.g. scheduler debug
spans) that may need their own external-trace embedding key.
A malformed dagrun_parent_trace_context value must never break Dag run
creation. Guard the propagator extraction so any parsing failure falls
back to a root trace instead of surfacing, and keep only string carrier
members so a non-string tracestate can't discard an otherwise-valid parent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dstandish
dstandish force-pushed the embed-dagrun-external-trace-conf branch from 07c19ac to 4a5ad1a Compare July 13, 2026 20:41
@dstandish
dstandish merged commit ae9dbd7 into apache:main Jul 13, 2026
78 checks passed
@dstandish
dstandish deleted the embed-dagrun-external-trace-conf branch July 13, 2026 21:46
joshuabvarghese pushed a commit to joshuabvarghese/airflow that referenced this pull request Jul 16, 2026
…9633)

Add a new reserved key to dagrun conf, `airflow/parent_trace_context`.  When set with a W3C traceparent, the dag run span will be nested under this trace.


def _carrier_span_context(carrier: dict[str, str]):
ctx = TraceContextTextMapPropagator().extract(carrier)

@tailg8nj tailg8nj Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Should this instead be:

Suggested change
ctx = TraceContextTextMapPropagator().extract(carrier)
ctx = extract(carrier)

or get_global_textmap().extract(carrier)

So that distros and configurations can leverage their own?

https://github.com/open-telemetry/opentelemetry-python/blob/684b19d3748d3f362cbe2d06c3caad9cbde8edef/opentelemetry-api/src/opentelemetry/propagate/__init__.py#L70

https://github.com/open-telemetry/opentelemetry-python/blob/684b19d3748d3f362cbe2d06c3caad9cbde8edef/opentelemetry-api/src/opentelemetry/propagate/__init__.py#L173

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This sounds like a valid comment.

So that distros and configurations can leverage their own?

@tailg8nj Can you explain the need for this? Is there a scenario where the user would need to use a custom propagator in general? For example, not just here but throughout the project, in all other places where we propagate the context. Attach and detach something to every context?

@tailg8nj tailg8nj Jul 24, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I actually meant to comment on dagrun.py above, but it's the same concept. I have outlined rationale in #70388

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.

Allow embedding a Dag run under an external trace via run conf

5 participants