cja/server/
trace.rs

1use tower_http::trace::{MakeSpan, OnResponse};
2use tracing::Level;
3
4#[derive(Debug, Clone, Copy)]
5pub(crate) struct Tracer;
6
7impl<Body> MakeSpan<Body> for Tracer {
8    fn make_span(&mut self, request: &http::Request<Body>) -> tracing::Span {
9        let route = http_route(request);
10        let span_name = format!("{} {}", request.method(), route);
11
12        tracing::span!(
13            Level::INFO,
14            "server.request",
15            otel.name = span_name,
16            kind = "server",
17            uri = %request.uri(),
18            url.path = %request.uri().path(),
19            url.query = request.uri().query(),
20            url.scheme = request.uri().scheme_str(),
21            server.address = request.uri().host(),
22            server.port = request.uri().port_u16(),
23            http_version = ?request.version(),
24            user_agent.original = request.headers().get("user-agent").and_then(|h| h.to_str().ok()),
25            http.route = route,
26            http.request.method = %request.method(),
27            http.request.header.host = request.headers().get("host").and_then(|h| h.to_str().ok()),
28            http.request.header.forwarded_for = request.headers().get("x-forwarded-for").and_then(|h| h.to_str().ok()),
29            http.request.header.forwarded_proto = request.headers().get("x-forwarded-proto").and_then(|h| h.to_str().ok()),
30            http.request.header.host = request.headers().get("x-forwarded-ssl").and_then(|h| h.to_str().ok()),
31            http.request.header.referer = request.headers().get("referer").and_then(|h| h.to_str().ok()),
32            http.request.header.fly_forwarded_port = request.headers().get("fly-forwarded-port").and_then(|h| h.to_str().ok()),
33            http.request.header.fly_region = request.headers().get("fly-region").and_then(|h| h.to_str().ok()),
34            http.request.header.via = request.headers().get("via").and_then(|h| h.to_str().ok()),
35
36            http.response.status_code = tracing::field::Empty,
37            http.response.header.content_type = tracing::field::Empty,
38        )
39    }
40}
41
42impl<Body> OnResponse<Body> for Tracer {
43    fn on_response(
44        self,
45        response: &http::Response<Body>,
46        latency: std::time::Duration,
47        span: &tracing::Span,
48    ) {
49        let status_code = response.status().as_u16();
50        tracing::event!(
51            Level::INFO,
52            status = status_code,
53            latency = format_args!("{} ms", latency.as_millis()),
54            "finished processing request"
55        );
56
57        span.record("http.response.status_code", status_code);
58        span.record(
59            "http.response.header.content_type",
60            response
61                .headers()
62                .get("content-type")
63                .and_then(|h| h.to_str().ok()),
64        );
65    }
66}
67
68#[inline]
69fn http_route<B>(req: &http::Request<B>) -> &str {
70    req.extensions()
71        .get::<axum::extract::MatchedPath>()
72        .map_or_else(|| "", |mp| mp.as_str())
73}