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}