Recipe
Instrument a request handler
Use when: a service handles many requests at once and its log lines can't be told apart or searched
Goal: every line a request writes carries that request's facts as fields, at the right level, with nothing private in it
Taught in Structured logging and tracing
Quick recipe
-
1
Call
tracing_subscriber::fmt()with anEnvFilter, and.json()when the output goes to a log store.Why: the macros record nothing until a subscriber exists, and one filter controls every crate's output.
-
2
Add
#[instrument(skip_all, fields(...))]orskip(...)for anything private or large.Example
#[instrument(skip(state, req), fields(sailing = %req.sailing))]onasync fn book(...).Why: the span stamps every event inside the call, including those from functions it calls, with the request's facts.
-
3
info!(seats, booking_id, "booking confirmed"), with%for Display and?for Debug.Why: fields are what a log store can filter and count; values inside the message are only text.
-
4
error!for a failure someone must look at,warn!for something odd the service survived,info!for the main steps,debug!for detail.Why: production runs at
info, so the level decides whether a line is seen at all.
Common mistakes
- Holding a
.enter()guard across an.await. - Recording a password, token or whole request body as a field.
- Logging the same error at every level of the call stack instead of once, where it's handled.