Traces
Traces come from tracing spans. Because OpenTelemetry::init links
tracing-opentelemetry to
the OTel SDK at boot, every tracing span automatically gets a trace_id
and exports through OTLP when an endpoint is configured.
Span targets
Section titled “Span targets”Targets are dotted, lowercase, framework-prefixed. One target per concern per crate.
| Layer | Target |
|---|---|
| HTTP request span | nest_rs::http |
| HTTP access event | nest_rs::access |
| ORM queries | nest_rs::orm |
| Authentication | nest_rs::authn |
| Authorization | nest_rs::authz |
| WebSocket events | nest_rs::ws |
| Queue jobs | nest_rs::queue |
| Scheduled jobs | nest_rs::schedule |
| Module lifecycle | nest_rs::module |
| Application spans | <app>::<feature> (e.g. api::users) |
Custom spans
Section titled “Custom spans”#[get("/{id}")]async fn read(&self, id: Path<Uuid>) -> Result<Json<User>> { let _span = tracing::info_span!("users.read", user_id = %id.0).entered(); let user = self.svc.find(id.0).await?; Ok(Json(user))}Use info_span! for the span itself, tracing::info! for an event inside it.
Always prefer field = %value to format!("{}", value) — structured fields
serialize cleanly to OTLP and stay filterable in the backend.
W3C propagation
Section titled “W3C propagation”traceparent propagation is automatic on incoming HTTP requests: if a client
sends a W3C traceparent header, the request span is parented onto the
incoming context, and the outgoing response carries X-Trace-Id so a caller
can match a span in their backend to a span in yours.
The W3C propagator is installed even when no OTLP endpoint is set — useful when a sibling service in your monorepo does export and you want the connection preserved across the hop.
Sampling
Section titled “Sampling”ParentBased(TraceIdRatioBased(ratio)) — children inherit the parent’s
sampling decision, so a request either samples end-to-end or not at all.
NESTRS_OPENTELEMETRY__SAMPLE_RATIO=0.1Or pinned:
OpenTelemetryConfig::new("api").with_trace_sample_ratio(0.1)The sampler comes from
opentelemetry_sdk::trace::Sampler.
Ratios are clamped to [0.0, 1.0].
HTTP interceptor
Section titled “HTTP interceptor”Importing OpenTelemetryModule activates the OpenTelemetryHttp interceptor
automatically — there’s nothing else to mount. Per request it:
- Opens a span on
nest_rs::httpnamedhttp.request, with the OTel HTTP semantic-convention attributes —http.request.method,http.route,http.response.status_code,http.response.body.size,client.address,user_agent.original— viaopentelemetry-semantic-conventions. - Extracts the W3C
traceparentheader (viaopentelemetry-http) and parents the span on the incoming context. - Adds
X-Trace-Idto every response, so a client can quote the id when filing an issue. - Wraps the response body in a byte-counter —
bytesandduration_msare measured at end-of-body, not at handler return. A client disconnect emits the access event too (via Drop), so a half-streamed response still produces one access line. - Records
http.response.status_codeandhttp.response.body.sizeon the span when the body finishes, so the span and the access event line up in the backend.
The access event itself is covered in Logs — toggle
it with NESTRS_HTTP__ACCESS_LOG=false; the OTel span still opens, so
propagation and OTLP export keep working.