1use std::borrow::Cow;
2use std::fmt::Display;
3use std::time::Instant;
4
5use tracing::{error, error_span, field, info, info_span, warn, warn_span};
6use trillium::async_trait;
7use trillium::{Conn, Handler, Info};
8
9struct TracerRun;
10
11#[async_trait]
12impl Handler for Tracer {
13 async fn run(&self, conn: Conn) -> Conn {
14 let path = path(&conn);
15 let method = conn.method();
16 let ip = ip(&conn);
17 info_span!("Request", http.method = ?method, ip = ?ip, path = %path).in_scope(|| {
18 info!("received");
19 conn.with_state(TracerRun)
20 })
21 }
22 async fn init(&mut self, info: &mut Info) {
23 info!("Starting server");
24 info!(server = ?info.server_description(), socker_addr = ?info.tcp_socket_addr().unwrap());
25 }
26
27 async fn before_send(&self, mut conn: Conn) -> Conn {
28 let response_len = conn.response_len().unwrap_or_default();
29 let response_time = response_time(&conn).to_string();
30
31 let status = conn.status(); if conn.state::<TracerRun>().is_some() {
36 conn.inner_mut().after_send(move |s| {
37 let response_span = info_span!("Response", http.status_code = field::Empty, http.duration = ?response_time, http.response_len = ?response_len);
38 response_span.in_scope(|| {
39 if let Some(status) = status {
40 response_span.record("http.status_code", &(status as u32));
41 if s.is_success() {
42 if status.is_server_error() {
43 error!("Internal Server error");
44 } else if status.is_client_error() {
45 warn_span!("Client error").in_scope(|| warn!("sent"))
46 } else {
47 info!("sent")
48 }
49 } else {
50 error_span!("Internal error").in_scope(|| warn!("cannot be sent"))
51 }
52 } else {
53 error_span!("Internal error").in_scope(||
56 error!("status not set, set the default status")
57 );
58 }
59
60 });
61 });
62 }
63 conn
64 }
65}
66
67struct ResponseTimeOutput(Instant);
68impl Display for ResponseTimeOutput {
69 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
70 f.write_fmt(format_args!("{:?}", Instant::now() - self.0))
71 }
72}
73
74fn response_time(conn: &Conn) -> ResponseTimeOutput {
75 ResponseTimeOutput(conn.inner().start_time())
76}
77
78fn ip(conn: &Conn) -> Cow<'static, str> {
79 match conn.inner().peer_ip() {
80 Some(peer) => format!("{:?}", peer).into(),
81 None => "-".into(),
82 }
83}
84
85fn path(conn: &Conn) -> Cow<'static, str> {
86 let p = conn.inner().path();
87 format!("{p:?}").into()
88}
89
90#[derive(Debug, Default, Clone)]
91pub struct Tracer;
92
93impl Tracer {
94 #[must_use]
95 pub const fn new() -> Self {
96 Self
97 }
98}