1use crate::LogFormatter;
2use colored::{ColoredString, Colorize};
3use size::{Base, Size};
4use std::{borrow::Cow, fmt::Display, sync::Arc, time::Instant};
5use trillium::{Conn, HeaderName, KnownHeaderName, Method, Status, Version};
6
7pub fn apache_combined(
43 request_id: impl LogFormatter,
44 user_id: impl LogFormatter,
45) -> impl LogFormatter {
46 (
47 apache_common(request_id, user_id),
48 " ",
49 request_header(KnownHeaderName::Referer),
50 " ",
51 request_header(KnownHeaderName::UserAgent),
52 )
53}
54
55pub fn method(conn: &Conn, _color: bool) -> Method {
60 conn.method()
61}
62
63pub fn dev_formatter(conn: &Conn, color: bool) -> impl Display + Send + 'static {
71 (method, " ", url, " ", response_time, " ", status).format(conn, color)
72}
73
74pub fn ip(conn: &Conn, _color: bool) -> Cow<'static, str> {
85 match conn.inner().peer_ip() {
86 Some(peer) => format!("{peer:?}").into(),
87 None => "-".into(),
88 }
89}
90
91mod status_mod {
92 use super::*;
93 #[derive(Copy, Clone)]
97 pub struct StatusOutput(Status, bool);
98 impl Display for StatusOutput {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 let StatusOutput(status, color) = *self;
101 let status_string = (status as u16).to_string();
102 if color {
103 f.write_fmt(format_args!(
104 "{}",
105 status_string.color(match status as u16 {
106 200..=299 => "green",
107 300..=399 => "cyan",
108 400..=499 => "yellow",
109 500..=599 => "red",
110 _ => "white",
111 })
112 ))
113 } else {
114 f.write_str(&status_string)
115 }
116 }
117 }
118
119 pub fn status(conn: &Conn, color: bool) -> StatusOutput {
134 StatusOutput(conn.status().unwrap_or(Status::NotFound), color)
135 }
136}
137
138pub use status_mod::status;
139
140pub fn request_header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
160 let header_name = header_name.into();
161 move |conn: &Conn, _color: bool| {
162 format!(
163 "{:?}",
164 conn.request_headers()
165 .get_str(header_name.clone())
166 .unwrap_or("")
167 )
168 }
169}
170
171#[deprecated = "use trillium_logger::formatters::request_header"]
176pub fn header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
177 request_header(header_name)
178}
179
180pub fn response_header(header_name: impl Into<HeaderName<'static>>) -> impl LogFormatter {
197 let header_name = header_name.into();
198 move |conn: &Conn, _color: bool| {
199 format!(
200 "{:?}",
201 conn.inner()
202 .response_headers()
203 .get_str(header_name.clone())
204 .unwrap_or("")
205 )
206 }
207}
208
209mod timestamp_mod {
210 use time::{macros::format_description, OffsetDateTime};
211
212 use super::*;
213 pub struct Now;
217
218 pub fn timestamp(_conn: &Conn, _color: bool) -> Now {
223 Now
224 }
225
226 impl Display for Now {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 let now = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc())
230 .format(format_description!(version = 2, "[day]/[month repr:short]/[year repr:full]:[hour repr:24]:[minute]:[second] [offset_hour sign:mandatory][offset_minute]")).unwrap();
231 f.write_str(&now)
232 }
233 }
234}
235pub use timestamp_mod::timestamp;
236
237pub fn body_len_human(conn: &Conn, _color: bool) -> Cow<'static, str> {
243 conn.response_len()
244 .map(|l| {
245 Size::from_bytes(l)
246 .format()
247 .with_base(Base::Base10)
248 .to_string()
249 .into()
250 })
251 .unwrap_or_else(|| Cow::from("-"))
252}
253
254pub fn apache_common(
288 request_id: impl LogFormatter,
289 user_id: impl LogFormatter,
290) -> impl LogFormatter {
291 (
292 ip, " ", request_id, " ", user_id, " [", timestamp, "] \"", method, " ", url, " ", version,
293 "\" ", status, " ", bytes,
294 )
295}
296
297pub fn bytes(conn: &Conn, _color: bool) -> u64 {
303 conn.response_len().unwrap_or_default()
304}
305
306pub fn secure(conn: &Conn, _: bool) -> &'static str {
311 if conn.is_secure() {
312 "🔒"
313 } else {
314 " "
315 }
316}
317
318pub fn url(conn: &Conn, _color: bool) -> String {
322 match conn.querystring() {
323 "" => conn.inner().path().into(),
324 query => format!("{}?{}", conn.inner().path(), query),
325 }
326}
327
328mod response_time_mod {
329 use super::*;
330 pub struct ResponseTimeOutput(Instant);
334 impl Display for ResponseTimeOutput {
335 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
336 f.write_fmt(format_args!("{:?}", Instant::now() - self.0))
337 }
338 }
339
340 pub fn response_time(conn: &Conn, _color: bool) -> ResponseTimeOutput {
346 ResponseTimeOutput(conn.inner().start_time())
347 }
348}
349
350pub use response_time_mod::response_time;
351
352pub fn version(conn: &Conn, _color: bool) -> Version {
357 conn.inner().http_version()
358}
359
360impl LogFormatter for &'static str {
361 type Output = Self;
362 fn format(&self, _conn: &Conn, _color: bool) -> Self::Output {
363 self
364 }
365}
366
367impl LogFormatter for Arc<str> {
368 type Output = Self;
369 fn format(&self, _conn: &Conn, _color: bool) -> Self::Output {
370 Arc::clone(self)
371 }
372}
373
374impl LogFormatter for ColoredString {
375 type Output = String;
376 fn format(&self, _conn: &Conn, color: bool) -> Self::Output {
377 if color {
378 self.to_string()
379 } else {
380 (**self).to_string()
381 }
382 }
383}
384
385impl<F, O> LogFormatter for F
386where
387 F: Fn(&Conn, bool) -> O + Send + Sync + 'static,
388 O: Display + Send + Sync + 'static,
389{
390 type Output = O;
391 fn format(&self, conn: &Conn, color: bool) -> Self::Output {
392 self(conn, color)
393 }
394}
395
396mod tuples {
397 use super::*;
398 pub struct TupleOutput<O>(O);
408 macro_rules! impl_formatter_tuple {
409 ($($name:ident)+) => (
410 #[allow(non_snake_case)]
411 impl<$($name,)*> Display for TupleOutput<($($name,)*)> where $($name: Display + Send + Sync + 'static,)* {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 let ($(ref $name,)*) = self.0;
414 f.write_fmt(format_args!(
415 concat!($(
416 concat!("{",stringify!($name) ,":}")
417 ),*),
418 $($name = ($name)),*
419 ))
420 }
421 }
422
423 #[allow(non_snake_case)]
424 impl<$($name),*> LogFormatter for ($($name,)*) where $($name: LogFormatter),* {
425 type Output = TupleOutput<($($name::Output,)*)>;
426 fn format(&self, conn: &Conn, color: bool) -> Self::Output {
427 let ($(ref $name,)*) = *self;
428 TupleOutput(($(($name).format(conn, color),)*))
429 }
430 }
431 )
432 }
433
434 impl_formatter_tuple! { A B }
435 impl_formatter_tuple! { A B C }
436 impl_formatter_tuple! { A B C D }
437 impl_formatter_tuple! { A B C D E }
438 impl_formatter_tuple! { A B C D E F }
439 impl_formatter_tuple! { A B C D E F G }
440 impl_formatter_tuple! { A B C D E F G H }
441 impl_formatter_tuple! { A B C D E F G H I }
442 impl_formatter_tuple! { A B C D E F G H I J }
443 impl_formatter_tuple! { A B C D E F G H I J K }
444 impl_formatter_tuple! { A B C D E F G H I J K L }
445 impl_formatter_tuple! { A B C D E F G H I J K L M }
446 impl_formatter_tuple! { A B C D E F G H I J K L M N }
447 impl_formatter_tuple! { A B C D E F G H I J K L M N O }
448 impl_formatter_tuple! { A B C D E F G H I J K L M N O P }
449 impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q }
450 impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R }
451 impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S }
452 impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T }
453 impl_formatter_tuple! { A B C D E F G H I J K L M N O P Q R S T U }
454 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 }
455 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 }
456 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 }
457 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 }
458 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 }
459}