Skip to content

feat(telemetry): correlate logs with an x-request-id span - #1208

Open
bilhokista wants to merge 2 commits into
Fracverse:masterfrom
bilhokista:feat/1127-request-correlation-id
Open

feat(telemetry): correlate logs with an x-request-id span#1208
bilhokista wants to merge 2 commits into
Fracverse:masterfrom
bilhokista:feat/1127-request-correlation-id

Conversation

@bilhokista

Copy link
Copy Markdown

Closes #1127.

Requirement 1 was already done

telemetry.rs already configures tracing-subscriber with .json() — in fact JSON is the default, with pretty and compact as opt-ins via LOG_FORMAT. I did not touch it.

Requirement 2 was the whole gap

There was no request-id handling anywhere in the backend. Every log line stood alone, so two concurrent requests interleaved in the output with no way to tell which line belonged to which — the specific thing that makes an aggregator useful.

request_id_middleware now wraps each request in a span carrying request_id, method and path. Because it is a span rather than a field on individual events, every nested info!/warn!/error! inside a handler inherits it without naming it, so existing log statements gain correlation with no edits.

The id is also written back onto the request (so anything downstream that forwards headers continues the same trace instead of starting a new one) and echoed on the response, so a client can quote it in a bug report.

Two decisions worth review

An inbound id is reused, not overwritten. That is the point of the header: it is what lets one trace be followed across the proxy, this service, and whatever it calls. Generating a fresh id unconditionally would produce ids that correlate with nothing upstream.

But an inbound id is untrusted input, so it is validated. This is the part I would most like checked. The id is interpolated into log output, so accepting arbitrary header content would let a caller embed a newline and write something that reads like a separate log entry — log forging, in a system whose whole purpose here is to be indexed and trusted by Cloudwatch or Datadog. Accepted values are ASCII alphanumerics, - and _, capped at 128 characters so a hostile client cannot bloat the logs one request at a time.

A rejected value is replaced with a generated id rather than failing the request — a malformed header is not a reason to reject otherwise valid traffic.

Layer placement

The middleware is registered outermost of the existing layers, so a request rejected by rate limiting or the geo guard is still logged under a correlation id. Those are exactly the requests worth tracing, and placing it inside those layers would have left them uncorrelated.

Tests

Seven cases in telemetry.rs:

  • well-formed ids accepted, including a UUID and one with -/_
  • ids that could forge log lines rejected\n, \r, tab, space, NUL
  • empty rejected; exactly 128 accepted; 129 rejected (the boundary, not just "too long")
  • non-ASCII and punctuation rejected (café, id/with/slashes, id;drop)
  • a valid inbound id reused verbatim
  • an unusable inbound id replaced, and the replacement asserted to itself be valid — so the generator can never emit something the validator would reject
  • two generated ids differ

Honest note on verification: I could not run cargo test for the crate — it needs the full dependency graph and sqlx's database or offline metadata. I lifted the two pure functions and all seven tests into a standalone file, stubbed Uuid::v4, and compiled with rustc --test --edition 2021: compiles clean, all 7 pass.

The middleware itself and the router wiring need CI. I have not been able to observe the span actually attaching to nested events at runtime, so that is the specific thing worth a reviewer's eye.

🤖 Generated with Claude Code

https://claude.ai/code/session_01CrfEY1tvXrbeMDAUzxfuk7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Backend: Implement Structured JSON Logging & Correlation ID Tracing

1 participant