use std::io::Write;
use std::panic;
use std::time::Duration;
use std::time::Instant;
use chrono;
use Request;
use Response;
pub fn log<W, F>(rq: &Request, mut output: W, f: F) -> Response
where
W: Write,
F: FnOnce() -> Response,
{
let start_instant = Instant::now();
let rq_line = format!(
"{} UTC - {} {}",
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S%.6f"),
rq.method(),
rq.raw_url()
);
let response = panic::catch_unwind(panic::AssertUnwindSafe(f));
let elapsed_time = format_time(start_instant.elapsed());
match response {
Ok(response) => {
let _ = writeln!(
output,
"{} - {} - {}",
rq_line, elapsed_time, response.status_code
);
response
}
Err(payload) => {
let _ = writeln!(output, "{} - {} - PANIC!", rq_line, elapsed_time);
panic::resume_unwind(payload);
}
}
}
pub fn log_custom<L, E, F>(req: &Request, log_ok_f: L, log_err_f: E, handler: F) -> Response
where
L: Fn(&Request, &Response, Duration),
E: Fn(&Request, Duration),
F: FnOnce() -> Response,
{
let start_instant = Instant::now();
let response = panic::catch_unwind(panic::AssertUnwindSafe(handler));
let elapsed = start_instant.elapsed();
match response {
Ok(response) => {
log_ok_f(req, &response, elapsed);
response
}
Err(payload) => {
log_err_f(req, elapsed);
panic::resume_unwind(payload);
}
}
}
fn format_time(duration: Duration) -> String {
let secs_part = match duration.as_secs().checked_mul(1_000_000_000) {
Some(v) => v,
None => return format!("{}s", duration.as_secs() as f64),
};
let duration_in_ns = secs_part + u64::from(duration.subsec_nanos());
if duration_in_ns < 1_000 {
format!("{}ns", duration_in_ns)
} else if duration_in_ns < 1_000_000 {
format!("{:.1}us", duration_in_ns as f64 / 1_000.0)
} else if duration_in_ns < 1_000_000_000 {
format!("{:.1}ms", duration_in_ns as f64 / 1_000_000.0)
} else {
format!("{:.1}s", duration_in_ns as f64 / 1_000_000_000.0)
}
}