1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
// SPDX-License-Identifier: Apache-2.0
// Copyright 2026 Craton Software Company
//! W3C Trace Context propagation helpers.
//!
//! This module is the single point of contact between the inbound HTTP
//! request's `traceparent` / `tracestate` headers and the local `tracing`
//! span tree. It exists so the wiring lives in exactly one place — the
//! tower middleware in [`crate::middleware`] is the only consumer at the
//! moment, but the same helpers are usable from integration tests and from
//! any future surface that wants to participate in distributed tracing
//! (e.g. a CLI shim that calls back into the gateway, a benchmark harness
//! that captures the resulting trace IDs, etc.).
//!
//! The actual span-creation logic still lives in
//! [`crate::middleware::trace_layer_with_propagation`]; this file owns:
//!
//! * the [`HeaderMapExtractor`] adapter that lets the OpenTelemetry
//! `TextMapPropagator` API read from `axum::http::HeaderMap`;
//! * [`install_w3c_propagator`], an idempotent one-shot installer for the
//! global [`opentelemetry_sdk::propagation::TraceContextPropagator`] so
//! middleware extraction is not a silent no-op;
//! * [`extract_parent_context`], a thin helper around the global
//! propagator that the middleware uses to attach a parent to its span;
//! * [`current_trace_id`], a helper for emitting the active trace id on
//! responses (`x-trace-id` header) and in audit records.
//!
//! ## Why install the propagator from the API crate
//!
//! `tensor-wasm-core`'s [`telemetry`](tensor_wasm_core::telemetry) module
//! initialises the global tracer provider but its OTel deps are
//! feature-gated under `otlp`. The gateway crate, by contrast, needs the
//! propagator on every deployment regardless of whether the operator has
//! enabled OTLP export — without it the middleware sees an empty context
//! and `traceparent` from the client is silently dropped. So we install
//! the propagator here, unconditionally, at router build time. It's a
//! single global write guarded by a [`Once`].
use Once;
// referenced indirectly via the global propagator registration
use TextMapPropagator;
use TraceContextPropagator;
/// HTTP header name used by the gateway to surface the request's resolved
/// trace id on every response. Lowercased per HTTP/2 norms; axum
/// normalises outgoing headers regardless, but using the canonical
/// lowercase spelling here matches the rest of the codebase.
///
/// The value is the 32-character lowercase hex string the OpenTelemetry
/// SDK assigns to the request's trace, the same one operators paste into
/// Jaeger / Tempo / Honeycomb to find the trace. Operators correlating
/// logs with a captured trace id should query this header — see
/// `docs/runbooks/trace-id.md`.
pub const HEADER_TRACE_ID: &str = "x-trace-id";
/// Guards [`install_w3c_propagator`]. A separate global from any used in
/// `tensor_wasm_core::telemetry` so the API crate can stand on its own
/// without forcing operators to enable the `otlp` feature.
static PROPAGATOR_INIT: Once = new;
/// Install the W3C Trace Context propagator as the global text-map
/// propagator if no other propagator has been installed yet.
///
/// Idempotent: subsequent calls are no-ops. Safe to call from
/// [`crate::server::build_router`] on every router construction — the
/// `Once` guarantees the install happens exactly once per process.
///
/// Without this call the global propagator defaults to OpenTelemetry's
/// `NoopTextMapPropagator`, which always extracts an empty context and
/// therefore silently drops the inbound `traceparent` header. We make
/// the failure mode "the propagator is installed but the SDK is not"
/// (degraded to a no-op exporter) rather than "the propagator is
/// missing" (which is a much harder bug to spot at runtime).
/// Adapter that lets the OpenTelemetry `TextMapPropagator` read from an
/// `axum`/`http` [`HeaderMap`](axum::http::HeaderMap).
///
/// The propagator API is generic over an
/// [`opentelemetry::propagation::Extractor`], which expects
/// `get(&str) -> Option<&str>` and `keys() -> Vec<&str>` semantics. We
/// provide the smallest possible bridge so we do not need the
/// `opentelemetry-http` crate as an additional dependency.
;
/// Extract a parent [`opentelemetry::Context`] from the supplied headers
/// via whichever global propagator has been installed (typically
/// [`TraceContextPropagator`] after [`install_w3c_propagator`] has run).
///
/// Returns an empty context if no propagator is installed or if the
/// headers do not carry a recognised trace-context entry; the caller can
/// pass that empty context to `set_parent` safely — the resulting span
/// becomes a new local root, which is the documented degradation
/// behaviour.
/// Return the 32-character lowercase hex representation of the active
/// span's trace id, if any.
///
/// The value is read from `tracing::Span::current()` via the
/// [`tracing_opentelemetry::OpenTelemetrySpanExt`] bridge, which only
/// resolves to a non-zero id when a `tracing_opentelemetry` layer is
/// active in the subscriber. Returns `None` when no layer is installed
/// (the common case in unit tests that do not initialise telemetry) or
/// when the active span has no associated OTel context.
///
/// The "invalid" all-zero trace id (`00000000000000000000000000000000`)
/// returned by `SpanContext::INVALID` is also mapped to `None` so callers
/// can branch on `Option` instead of pattern-matching on the sentinel.
/// Middleware that stamps `x-trace-id: <hex>` on every response when the
/// active span has a resolved OTel trace id.
///
/// Called via [`axum::middleware::from_fn`] from
/// [`crate::server::build_router_with_audit`]. Sits inside
/// [`crate::middleware::trace_layer_with_propagation`] so the active
/// span at header-injection time is the one the tracing layer just
/// created (with its parent already wired from `traceparent`).
///
/// Absent trace id => header omitted entirely; we never emit
/// `x-trace-id: ` with an empty value, as that surfaces as a confusing
/// "trace context unavailable" signal to operators reading captures.
pub async