Add the per-request root span (middleware) and drop per-handler boilerplate #139

Closed
opened 2026-09-29 14:57:09 +00:00 by cameron · 1 comment
Owner

Part of #135

Work

Apply the middleware chosen in "Confirm the per-request root-span middleware works on actix-web 4 + otel 0.31" so every request gets a root span plus incoming traceparent extraction, then remove the per-handler extract_context_from_request + tracer.start_with_context boilerplate.

  • Register the middleware in the actix app setup (main.rs / service.rs).
  • Convert handlers to #[instrument] where they still need a named span; drop the manual context threading into handlers.
  • Verify traces still nest as before (compare against the baseline).

Resolution

How the root span is created, and confirmation the per-handler boilerplate is gone.

Part of #135 ## Work Apply the middleware chosen in "Confirm the per-request root-span middleware works on actix-web 4 + otel 0.31" so every request gets a root span plus incoming `traceparent` extraction, then remove the per-handler `extract_context_from_request` + `tracer.start_with_context` boilerplate. - Register the middleware in the actix app setup (`main.rs` / `service.rs`). - Convert handlers to `#[instrument]` where they still need a named span; drop the manual context threading into handlers. - Verify traces still nest as before (compare against the baseline). ## Resolution How the root span is created, and confirmation the per-handler boilerplate is gone.
cameron added a new dependency 2026-09-29 14:57:54 +00:00
cameron added a new dependency 2026-09-29 15:00:56 +00:00
Author
Owner

Resolved on feature/tracing-migration (commit 350b748).

What was done

  • tracing-actix-web 0.7.25 added with the opentelemetry_0_31 feature; TracingLogger::default() registered as the outermost Actix middleware in main.rs. It creates one OTel server span per request and extracts an incoming W3C traceparent onto it via the global text-map propagator (installed in #138).
  • extract_context_from_request now delegates: it sources the parent from the root span (tracing::Span::current().context()) instead of re-parsing the header. Existing per-handler operation spans therefore nest under the root span (one trace tree) rather than duplicating it as a sibling. Header parsing stays as a fallback for handlers invoked without the middleware (e.g. direct tests).
  • No per-handler refactor in this ticket — that's the deliberate split: the literal span boilerplate becomes #[instrument] in the LLM (#140) and DAO (#141) migrations, where the &Context threading disappears entirely.

Verification

  • tests/otel_traceparent.rs (new, end-to-end): an incoming traceparent's trace_id propagates to the request, the root span is parented on the caller's remote span, and the operation span nests under the root span (not a sibling). Confirmed captured spans: root GET /op has parent=<remote> / remote=true; test.operation has parent=<root span id>, both sharing the caller's trace_id.
  • cargo clippy --all-targets: 0 warnings.
  • Full suite green: 690 lib + 775 integration + both OTel regression tests + 4 doc-tests.

Note: a startup smoke-run of the full binary couldn't bind in this environment because main.rs also binds a fixed localhost:8088 (held by the running production server); the integration test exercises the middleware at runtime and is the authoritative check.

Resolved on `feature/tracing-migration` (commit `350b748`). **What was done** - **`tracing-actix-web` 0.7.25** added with the `opentelemetry_0_31` feature; `TracingLogger::default()` registered as the **outermost** Actix middleware in `main.rs`. It creates one OTel server span per request and extracts an incoming W3C `traceparent` onto it via the global text-map propagator (installed in #138). - **`extract_context_from_request` now delegates**: it sources the parent from the root span (`tracing::Span::current().context()`) instead of re-parsing the header. Existing per-handler operation spans therefore nest *under* the root span (one trace tree) rather than duplicating it as a sibling. Header parsing stays as a fallback for handlers invoked without the middleware (e.g. direct tests). - **No per-handler refactor in this ticket** — that's the deliberate split: the literal span boilerplate becomes `#[instrument]` in the LLM (#140) and DAO (#141) migrations, where the `&Context` threading disappears entirely. **Verification** - `tests/otel_traceparent.rs` (new, end-to-end): an incoming `traceparent`'s `trace_id` propagates to the request, the root span is parented on the caller's *remote* span, and the operation span nests under the root span (not a sibling). Confirmed captured spans: root `GET /op` has `parent=<remote>` / `remote=true`; `test.operation` has `parent=<root span id>`, both sharing the caller's `trace_id`. - `cargo clippy --all-targets`: 0 warnings. - Full suite green: 690 lib + 775 integration + both OTel regression tests + 4 doc-tests. **Note:** a startup smoke-run of the full binary couldn't bind in this environment because `main.rs` also binds a fixed `localhost:8088` (held by the running production server); the integration test exercises the middleware at runtime and is the authoritative check.
cameron added the wayfinder:task label 2026-09-30 00:47:27 +00:00
Sign in to join this conversation.