use futures_util::future::{self, FutureExt, TryFutureExt};
use http::header::CONTENT_LENGTH;
use http::{Method, Uri, Version};
use log::{log, log_enabled, Level};
use std::pin::Pin;
use crate::handler::HandlerFuture;
use crate::helpers::timing::Timer;
use crate::middleware::{Middleware, NewMiddleware};
use crate::state::{client_addr, request_id, 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 = {
use time::format_description::FormatItem;
use time::macros::format_description;
const DT_FORMAT: &[FormatItem<'static>]
= format_description!("[day]/[month repr:short]/[year]:[hour repr:24]:[minute]:[second] [offset_hour][offset_minute]");
timer.start_time().format(&DT_FORMAT).expect("Failed to format time")
};
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()
}
}