use {axum::http::Request,
std::{future::Future,
pin::Pin,
task::{Context,
Poll},
time::Instant},
tower::{Layer,
Service},
tracing::{info,
warn}};
#[derive(Clone)]
pub struct RequestLoggingLayer;
impl<S> Layer<S> for RequestLoggingLayer {
type Service = RequestLoggingMiddleware<S>;
fn layer(&self, inner: S) -> Self::Service {
RequestLoggingMiddleware { inner }
}
}
#[derive(Clone)]
pub struct RequestLoggingMiddleware<S> {
inner: S,
}
impl<S, ReqBody, ResBody> Service<Request<ReqBody>> for RequestLoggingMiddleware<S>
where
S: Service<Request<ReqBody>, Response = axum::response::Response<ResBody>> + Clone + Send + 'static,
S::Future: Send,
ReqBody: Send + 'static,
ResBody: Send + 'static,
{
type Error = S::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
type Response = S::Response;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<ReqBody>) -> Self::Future {
let method = req.method().clone();
let uri = req.uri().clone();
let start = Instant::now();
let mut inner = self.inner.clone();
Box::pin(async move {
let response = inner.call(req).await?;
let duration = start.elapsed();
let status = response.status();
if status.is_success() {
info!(
method = %method,
uri = %uri,
status = %status.as_u16(),
duration_ms = %duration.as_millis(),
"Request completed"
);
} else {
warn!(
method = %method,
uri = %uri,
status = %status.as_u16(),
duration_ms = %duration.as_millis(),
"Request failed"
);
}
Ok(response)
})
}
}