use core::task::{Context, Poll};
use std::fmt;
use std::net::SocketAddr;
use std::time::Instant;
use futures::future::{BoxFuture, FutureExt};
use humantime::format_duration;
use hyper::{service::Service, Body, Request, Response};
pub struct Logger<S> {
remote_addr: SocketAddr,
service: S,
}
impl<S> Logger<S> {
pub fn new(remote_addr: SocketAddr, service: S) -> Self {
Logger {
remote_addr,
service,
}
}
}
impl<S> Service<Request<Body>> for Logger<S>
where
S: Service<Request<Body>, Response = Response<Body>>,
S::Future: Send + 'static,
S::Error: fmt::Display + Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
cx: &mut Context,
) -> Poll<Result<(), Self::Error>> {
self.service.poll_ready(cx)
}
fn call(&mut self, req: Request<Body>) -> Self::Future {
let method = req.method().clone();
let uri = req.uri().clone();
let remote_addr = self.remote_addr;
let start = Instant::now();
Box::pin(self.service.call(req).inspect(
move |response| match response {
Ok(response) => tracing::info!(
"[{}] {} {} - {} ({})",
remote_addr.ip(),
method,
uri,
response.status(),
format_duration(start.elapsed()),
),
Err(err) => tracing::error!(
"[{}] {} {} - {} ({})",
remote_addr.ip(),
method,
uri,
err,
format_duration(start.elapsed()),
),
},
))
}
}