use futures::prelude::*;
use hyper::{header::CONTENT_LENGTH, Method, Uri, Version};
use log::Level;
use log::{log, log_enabled};
use std::pin::Pin;
use crate::handler::HandlerFuture;
use crate::helpers::timing::Timer;
use crate::middleware::{Middleware, NewMiddleware};
use crate::state::request_id::request_id;
use crate::state::{client_addr, FromState, State};
#[derive(Copy, Clone)]
pub struct RequestLogger {
level: Level,
}
impl RequestLogger {
pub fn new(level: Level) -> Self {
RequestLogger { level }
}
}
impl NewMiddleware for RequestLogger {
type Instance = Self;
fn new_middleware(&self) -> anyhow::Result<Self::Instance> {
Ok(*self)
}
}
impl Middleware for RequestLogger {
fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
where
Chain: FnOnce(State) -> Pin<Box<HandlerFuture>>,
{
if !log_enabled!(self.level) {
return chain(state);
}
let timer = Timer::new();
let f = chain(state).and_then(move |(state, response)| {
let datetime = timer.start_time().format("%d/%b/%Y:%H:%M:%S %z");
let ip = client_addr(&state).unwrap().ip();
{
let path = Uri::borrow_from(&state);
let method = Method::borrow_from(&state);
let version = Version::borrow_from(&state);
let status = response.status().as_u16();
let length = response
.headers()
.get(CONTENT_LENGTH)
.map(|len| len.to_str().unwrap())
.unwrap_or("0");
log!(
self.level,
"{} - - [{}] \"{} {} {:?}\" {} {} - {}",
ip,
datetime,
method,
path,
version,
status,
length,
timer.elapsed()
);
}
future::ok((state, response))
});
f.boxed()
}
}
#[derive(Copy, Clone)]
pub struct SimpleLogger {
level: Level,
}
impl SimpleLogger {
pub fn new(level: Level) -> Self {
SimpleLogger { level }
}
}
impl NewMiddleware for SimpleLogger {
type Instance = Self;
fn new_middleware(&self) -> anyhow::Result<Self::Instance> {
Ok(*self)
}
}
impl Middleware for SimpleLogger {
fn call<Chain>(self, state: State, chain: Chain) -> Pin<Box<HandlerFuture>>
where
Chain: FnOnce(State) -> Pin<Box<HandlerFuture>>,
{
if !log_enabled!(self.level) {
return chain(state);
}
let timer = Timer::new();
let f = chain(state).and_then(move |(state, response)| {
log!(
self.level,
"[RESPONSE][{}][{:?}][{}][{}]",
request_id(&state),
response.version(),
response.status(),
timer.elapsed()
);
future::ok((state, response))
});
f.boxed()
}
}