use crate::LogFormatter;
use colored::{ColoredString, Colorize};
use size::{Base, Size};
use std::{borrow::Cow, fmt::Display, sync::Arc, time::Instant};
use trillium::{Conn, HeaderName, KnownHeaderName, Method, Status, Version};
pub fn apache_combined(
request_id: impl LogFormatter,
user_id: impl LogFormatter,
) -> impl LogFormatter {
(
apache_common(request_id, user_id),
" ",
request_header(KnownHeaderName::Referer),
" ",
request_header(KnownHeaderName::UserAgent),
)
}
pub fn method(conn: &Conn, _color: bool) -> Method {
conn.method()
}
pub fn dev_formatter(conn: &Conn, color: bool) -> impl Display + Send + 'static {
(method, " ", url, " ", response_time, " ", status).format(conn, color)
}
pub fn ip(conn: &Conn, _color: bool) -> Cow<'static, str> {
match conn.inner().peer_ip() {
Some(peer) => format!("{peer:?}").into(),
None => "-".into(),
}
}
mod status_mod {
use super::*;
#[derive(Copy, Clone)]
pub struct StatusOutput(Status, bool);
impl Display for StatusOutput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let StatusOutput(status, color) = *self;
let status_string = (status as u16).to_string();
if color {
f.write_fmt(format_args!(
"{}",
status_string.color(match status as u16 {
200..=299 => "green",
300..=399 => "cyan",
400..=499 => "yellow",
500..=599 => "red",
_ => "white",
})
))
} else {
f.write_str(&status_string)
}
}
}
pub fn status(conn: &Conn, color: bool) -> StatusOutput {
StatusOutput(conn.status().unwrap_or(Status::NotFound), color)
}
}
pub use status_mod::status;
pub fn request_header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
let header_name = header_name.into();
move |conn: &Conn, _color: bool| {
format!(
"{:?}",
conn.request_headers()
.get_str(header_name.clone())
.unwrap_or("")
)
}
}
#[deprecated = "use trillium_logger::formatters::request_header"]
pub fn header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
request_header(header_name)
}
pub fn response_header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
let header_name = header_name.into();
move |conn: &Conn, _color: bool| {
format!(
"{:?}",
conn.inner()
.response_headers()
.get_str(header_name.clone())
.unwrap_or("")
)
}
}
mod timestamp_mod {
use time::{macros::format_description, OffsetDateTime};
use super::*;
pub struct Now;
pub fn timestamp(_conn: &Conn, _color: bool) -> Now {
Now
}
impl Display for Now {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc())
.format(format_description!(version = 2, "[day]/[month repr:short]/[year repr:full]:[hour repr:24]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]")).unwrap();
f.write_str(&now)
}
}
}
pub use timestamp_mod::timestamp;
pub fn body_len_human(conn: &Conn, _color: bool) -> Cow<'static, str> {
conn.response_len()
.map(|l| {
Size::from_bytes(l)
.format()
.with_base(Base::Base10)
.to_string()
.into()
})
.unwrap_or_else(|| Cow::from("-"))
}
pub fn apache_common(
request_id: impl LogFormatter,
user_id: impl LogFormatter,
) -> impl LogFormatter {
(
ip, " ", request_id, " ", user_id, " [", timestamp, "] \"", method, " ", url, " ", version,
"\" ", status, " ", bytes,
)
}
pub fn bytes(conn: &Conn, _color: bool) -> u64 {
conn.response_len().unwrap_or_default()
}
pub fn secure(conn: &Conn, _: bool) -> &'static str {
if conn.is_secure() {
"🔒"
} else {
" "
}
}
pub fn url(conn: &Conn, _color: bool) -> String {
match conn.querystring() {
"" => conn.inner().path().into(),
query => format!("{}?{}", conn.inner().path(), query),
}
}
mod response_time_mod {
use super::*;
pub struct ResponseTimeOutput(Instant);
impl Display for ResponseTimeOutput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{:?}", Instant::now() - self.0))
}
}
pub fn response_time(conn: &Conn, _color: bool) -> ResponseTimeOutput {
ResponseTimeOutput(conn.inner().start_time())
}
}
pub use response_time_mod::response_time;
pub fn version(conn: &Conn, _color: bool) -> Version {
conn.inner().http_version()
}
impl LogFormatter for &'static str {
type Output = Self;
fn format(&self, _conn: &Conn, _color: bool) -> Self::Output {
self
}
}
impl LogFormatter for Arc<str> {
type Output = Self;
fn format(&self, _conn: &Conn, _color: bool) -> Self::Output {
Arc::clone(self)
}
}
impl LogFormatter for ColoredString {
type Output = String;
fn format(&self, _conn: &Conn, color: bool) -> Self::Output {
if color {
self.to_string()
} else {
(**self).to_string()
}
}
}
impl<F, O> LogFormatter for F
where
F: Fn(&Conn, bool) -> O + Send + Sync + 'static,
O: Display + Send + Sync + 'static,
{
type Output = O;
fn format(&self, conn: &Conn, color: bool) -> Self::Output {
self(conn, color)
}
}
mod tuples {
use super::*;
pub struct TupleOutput<O>(O);
macro_rules! impl_formatter_tuple {
($($name:ident)+) => (
#[allow(non_snake_case)]
impl<$($name,)*> Display for TupleOutput<($($name,)*)> where $($name: Display + Send + Sync + 'static,)* {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let ($(ref $name,)*) = self.0;
f.write_fmt(format_args!(
concat!($(
concat!("{",stringify!($name) ,":}")
),*),
$($name = ($name)),*
))
}
}
#[allow(non_snake_case)]
impl<$($name),*> LogFormatter for ($($name,)*) where $($name: LogFormatter),* {
type Output = TupleOutput<($($name::Output,)*)>;
fn format(&self, conn: &Conn, color: bool) -> Self::Output {
let ($(ref $name,)*) = *self;
TupleOutput(($(($name).format(conn, color),)*))
}
}
)
}
impl_formatter_tuple! { A B }
impl_formatter_tuple! { A B C }
impl_formatter_tuple! { A B C D }
impl_formatter_tuple! { A B C D E }
impl_formatter_tuple! { A B C D E F }
impl_formatter_tuple! { A B C D E F G }
impl_formatter_tuple! { A B C D E F G H }
impl_formatter_tuple! { A B C D E F G H I }
impl_formatter_tuple! { A B C D E F G H I J }
impl_formatter_tuple! { A B C D E F G H I J K }
impl_formatter_tuple! { A B C D E F G H I J K L }
impl_formatter_tuple! { A B C D E F G H I J K L M }
impl_formatter_tuple! { A B C D E F G H I J K L M N }
impl_formatter_tuple! { A B C D E F G H I J K L M N O }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U V }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X Y }
impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U V W X Y Z }
}