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