at_jet/middleware/
logging.rs1use {axum::http::Request,
4 std::{future::Future,
5 pin::Pin,
6 task::{Context,
7 Poll},
8 time::Instant},
9 tower::{Layer,
10 Service},
11 tracing::{info,
12 warn}};
13
14#[derive(Clone)]
16pub struct RequestLoggingLayer;
17
18impl<S> Layer<S> for RequestLoggingLayer {
19 type Service = RequestLoggingMiddleware<S>;
20
21 fn layer(&self, inner: S) -> Self::Service {
22 RequestLoggingMiddleware { inner }
23 }
24}
25
26#[derive(Clone)]
28pub struct RequestLoggingMiddleware<S> {
29 inner: S,
30}
31
32impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for RequestLoggingMiddleware<S>
33where
34 S: Service<Request<ReqBody>, Response = axum::response::Response<ResBody>> + Clone + Send + 'static,
35 S::Future: Send,
36 ReqBody: Send + 'static,
37 ResBody: Send + 'static,
38{
39 type Error = S::Error;
40 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
41 type Response = S::Response;
42
43 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
44 self.inner.poll_ready(cx)
45 }
46
47 fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
48 let method = req.method().clone();
49 let uri = req.uri().clone();
50 let start = Instant::now();
51
52 let mut inner = self.inner.clone();
53
54 Box::pin(async move {
55 let response = inner.call(req).await?;
56 let duration = start.elapsed();
57 let status = response.status();
58
59 if status.is_success() {
60 info!(
61 method = %method,
62 uri = %uri,
63 status = %status.as_u16(),
64 duration_ms = %duration.as_millis(),
65 "Request completed"
66 );
67 } else {
68 warn!(
69 method = %method,
70 uri = %uri,
71 status = %status.as_u16(),
72 duration_ms = %duration.as_millis(),
73 "Request failed"
74 );
75 }
76
77 Ok(response)
78 })
79 }
80}