Tracing with Tokio's tracing crate
Roadster provides support for tracing as defined by the
init_tracing
method, which is used in the
default implementation of
App#init_tracing
. Some of the
initialization logic can be configured via the app's config files. The app can also provide its own custom tracing
initialization logic.
If the otel
feature is enabled, this method will also initialize some OpenTelemetry resources. See
the OpenTelemetry chapter for more details.
Configuration
[tracing]
level = "debug"
format = "pretty"
See the Tracing config struct for the full list of available fields.
Custom initialization logic
If the app has custom requirements for tracing / metrics, custom logic can be provided. Note that if a custom
implementation is provided, none of the default tracing setup
from init_tracing
will be applied.
fn build_app() -> RoadsterApp<AppContext> {
RoadsterApp::builder()
.tracing_initializer(|config| {
// Implement custom logic here. See Roadster's implementation
// for an example: https://docs.rs/roadster/latest/roadster/tracing/fn.init_tracing.html
todo!("Custom tracing initialization logic")
})
// Use the default `AppContext` for this example
.state_provider(|context| Ok(context))
.add_service_provider(move |_registry, _state| {
Box::pin(async move { todo!("Add services here.") })
})
.build()
}
Provided trace events/logic for HTTP requests
When the http
feature is enabled, Roadster provides some additional tracing features via Axum/Tower middleware.
HTTP Request ID
Roadster can generate an ID for each HTTP request. This is done with the
SetRequestIdMiddleware
,
which is a wrapper around tower-http
's
SetRequestIdLayer
. This layer will
generate a new request ID (as a UUID) for each HTTP request if one wasn't provided in the request.
Additionally, Roadster allows your app to propagate the request ID to any service it calls. This is done with the
PropagateRequestIdMiddleware
,
which is a wrapper around tower-http
's
PropagateRequestIdLayer
.
The request ID is fetched and propagated via an HTTP request header, which is configurable via the header-name
field
of each middleware's config.
[service.http.middleware.set-request-id]
header-name = "request-id"
[service.http.middleware.propagate-request-id]
header-name = "request-id"
Each of these middlewares can also be disabled is desired.
HTTP request/response events
In addition to generating an ID for each request, Roadster can also create a tracing span for each request. This is done
with the
TracingMiddleware
,
which is a wrapper around tower-http
's
TraceLayer
, with some custom logic for how
to create spans and emit events on request and response.
The span includes the request ID as an attribute. The ID is retrieved from the header name configured for the
SetRequestIdMiddleware
. Below is a sample trace (as logs):
2025-03-20T08:22:01.257662Z INFO roadster::service::http::middleware::tracing: started processing request, version: HTTP/1.1, url.path: /api/_health, request_headers: {"host": "localhost:3000", "user-agent": "<redacted>", "accept": "application/json, text/plain, */*", "accept-language": "en-US,en;q=0.5", "accept-encoding": "gzip, deflate, br, zstd", "connection": "keep-alive", "referer": "http://localhost:3000/api/_docs", "sec-fetch-dest": "empty", "sec-fetch-mode": "cors", "sec-fetch-site": "same-origin", "dnt": "1", "sec-gpc": "1", "priority": "u=0", "request-id": "9727c5c8-a982-42ef-8d7e-c3e388d378ae"}
at src/service/http/middleware/tracing/mod.rs:141
in roadster::service::http::middleware::tracing::http_request with http.request.method: GET, http.route: /api/_health, request_id: 9727c5c8-a982-42ef-8d7e-c3e388d378ae
2025-03-20T08:22:01.269580Z INFO tower_http::trace::on_response: finished processing request, latency: 12 ms, status: 200, response_headers: {"content-type": "application/json", "request-id": "9727c5c8-a982-42ef-8d7e-c3e388d378ae", "vary": "origin, access-control-request-method, access-control-request-headers"}
at /Users/<redacted>/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tower-http-0.6.2/src/trace/on_response.rs:114
in roadster::service::http::middleware::tracing::http_request with http.request.method: GET, http.route: /api/_health, request_id: 9727c5c8-a982-42ef-8d7e-c3e388d378ae, http.response.status_code: 200
HTTP request/response payload logging for debugging
In non-prod environments, it can be very helpful to be able to inspect request and response payloads. Roadster allows
this via the custom
RequestResponseLoggingMiddleware
.
Note that this middleware does not work for requests/responses that are long-running streams.
Technically it's possible to enable this middleware in prod via app configs; however,
this is strongly discouraged as this can leak sensitive data into your trace events and/or logs. If logging
request/response payloads in prod is desired, the payload should be encrypted before it's logged. Alternatively,
something like the secrecy
crate could be used for sensitive struct
fields, then the rest of the struct can be safely logged from your API handler.