use std::fmt::Debug;
use axum::http::Request;
use futures::{future::Map, FutureExt};
use tower::Service;
#[derive(Clone)]
pub struct Log;
impl<S> tower::Layer<S> for Log {
type Service = ResponseTimeService<S>;
fn layer(&self, service: S) -> Self::Service {
ResponseTimeService { inner: service }
}
}
#[derive(Clone)]
pub struct ResponseTimeService<S> {
inner: S,
}
impl<S, B> Service<Request<B>> for ResponseTimeService<S>
where
S::Response: Debug,
S::Error: Debug,
S: Service<Request<B>>,
B: Send + Debug,
{
type Response = S::Response;
type Error = S::Error;
type Future =
Map<S::Future, impl FnOnce(Result<S::Response, S::Error>) -> Result<S::Response, S::Error>>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, req: Request<B>) -> Self::Future {
let start_time: u64 = coarsetime::Clock::now_since_epoch().as_millis();
let method = req.method().to_owned();
let url = req.uri().to_owned();
self.inner.call(req).map(move |response| {
{
let latency =
(coarsetime::Clock::now_since_epoch().as_millis() - start_time) as f32 / 1000.0;
tracing::info!("{} {} {}s", method, url, latency)
}
response
})
}
}