feat(server): expose OTel trace_id as x-request-id and traceparent (#1975)#1990
Merged
feat(server): expose OTel trace_id as x-request-id and traceparent (#1975)#1990
Conversation
…1975) Add a global tower middleware in `rara_server::http::inject_trace_headers` that reads the OTel trace_id off the active `http_request` span and writes two response headers: `x-request-id` (32-hex) and `traceparent` (W3C v00). Layered AFTER `TraceLayer::new_for_http()` so the span is active. When the span has no valid trace_id (rejected before TraceLayer ran), the headers are simply omitted — no panic, no empty values. `rara_backend_admin::state::build_cors_layer` now calls `.expose_headers([x-request-id, traceparent])` so the browser fetch API can read both headers across the `localhost:5173 -> 10.0.0.183:25555` proxy boundary. No other CORS knob changes. `web/src/api/client.ts` extends `ApiError` with an optional `requestId` field populated from `res.headers.get('x-request-id')` at the existing throw sites. A small `raiseApiError` helper logs the id alongside the message via `console.error` so a developer triaging a user bug report can paste a 32-hex id straight into Langfuse and Loki. Closes the gap where a user reporting "rara didn't reply at 3:42pm" left the developer with no anchor to correlate web, telegram, and kernel spans across `Library/Logs/rara/job.*` — see `specs/issue-1975-trace-id-response-header.spec.md` for the full reproducer and intent. Advances `goal.md` signal 4 ("every action is inspectable") by extending inspectability from the backend (where the trace_id already lived end-to-end) to the client surface. Verification: - 3 Rust tests in `crates/server/tests/trace_headers.rs` exercise the middleware end-to-end via an in-process `SdkTracerProvider`. - 1 Rust test in `crates/extensions/backend-admin/src/state.rs` asserts the CORS expose-headers list contains exactly the two new headers via the live `access-control-expose-headers` response. - 2 vitest specs in `web/src/api/__tests__/client.requestId.test.ts` cover the `ApiError.requestId` propagation path with and without the header present. - `agent-spec lifecycle` reports 5/5 scenarios passing. Spec correction note: the spec author's draft used `Package: web` for the ApiError scenario, but `agent-spec` only dispatches `cargo test`. The implementer rebound that scenario to a Rust test that pins the wire header literal (`x-request-id`) on both ends of the cross-language contract; the vitest test still runs as supplementary verification of the TS-side propagation. Two other typos were corrected: the CORS scenario package name (`rara-extensions-backend-admin` -> `rara-backend-admin`, the actual cargo crate name) and the boundary glob for `crates/server/tests/**` (the spec body authorized the path in prose but the Boundaries glob list did not include it). Closes #1975
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Every HTTP response from
rara servernow carriesx-request-id(32-hex OTel trace_id) andtraceparent(W3C format) headers. CORSexpose_headerspermits the browser to read both. The web client surfacesrequestIdonApiErrorfor triage.Implementation: a new
inject_trace_headersmiddleware layered afterTraceLayer::new_for_http()reads the active span's OTel context and writes the headers onto the outgoing response.Closes #1975
Test plan
just spec-lifecycle specs/issue-1975-trace-id-response-header.spec.mdcrates/server/tests/trace_headers.rs(header presence, format, propagation)expose-headerstest inrara-backend-adminclient.requestId.test.tscovers TS-sideApiError.requestIdpropagationprek run --all-filescleanReviewer notes
P2 process finding (acknowledged): the spec was edited mid-implementation rather than escalated to spec-author. Edits accepted as in-scope clarifications:
rara-extensions-backend-admin→rara-backend-adminPackage: webbecauseagent-speconly dispatchescargo testfor the BDD lifecycle gate🤖 Generated with Claude Code