Skip to main content

http_srv/
log.rs

1use std::{
2    io::{Write, stderr},
3    sync::{LazyLock, Mutex},
4};
5
6use crate::HttpError;
7
8#[derive(Clone, Copy, PartialEq, PartialOrd)]
9pub enum LogLevel {
10    None = 0,
11    Error = 1,
12    Warn = 2,
13    Info = 3,
14}
15
16impl TryFrom<u8> for LogLevel {
17    type Error = HttpError;
18
19    fn try_from(value: u8) -> crate::Result<Self> {
20        Ok(match value {
21            0 => LogLevel::None,
22            1 => LogLevel::Error,
23            2 => LogLevel::Warn,
24            3 => LogLevel::Info,
25            _ => return Err(format!("Invalid log level: {value}").into()),
26        })
27    }
28}
29
30#[allow(clippy::missing_panics_doc)]
31pub fn get_level() -> LogLevel {
32    #[allow(clippy::unwrap_used)]
33    LOGGER.lock().unwrap().get_level()
34}
35
36#[allow(clippy::missing_panics_doc)]
37pub fn set_level(level: LogLevel) {
38    #[allow(clippy::unwrap_used)]
39    LOGGER.lock().unwrap().set_level(level);
40}
41
42pub trait Logger: Send + Sync {
43    fn log(&mut self, level: LogLevel, args: core::fmt::Arguments);
44    fn set_level(&mut self, level: LogLevel);
45    fn get_level(&self) -> LogLevel;
46}
47
48struct StdErrLogger(LogLevel);
49
50impl Logger for StdErrLogger {
51    fn log(&mut self, level: LogLevel, args: core::fmt::Arguments) {
52        if level <= self.0 {
53            #[allow(clippy::unwrap_used)]
54            stderr().write_fmt(args).unwrap();
55        }
56    }
57    fn set_level(&mut self, level: LogLevel) {
58        self.0 = level;
59    }
60    fn get_level(&self) -> LogLevel {
61        self.0
62    }
63}
64
65#[cfg(not(debug_assertions))]
66const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Warn;
67
68#[cfg(debug_assertions)]
69const DEFAULT_LOG_LEVEL: LogLevel = LogLevel::Info;
70
71static LOGGER: LazyLock<Mutex<Box<dyn Logger>>> =
72    LazyLock::new(|| Mutex::new(Box::new(StdErrLogger(DEFAULT_LOG_LEVEL))));
73
74#[macro_export]
75#[doc(hidden)]
76macro_rules! __log {
77    ($lev:expr , $($arg:tt)*) => {
78        {
79            #[allow(clippy::used_underscore_items)]
80            $crate::log::_log($lev, format_args!($($arg)*))
81        }
82    };
83}
84
85#[macro_export]
86#[doc(hidden)]
87macro_rules! __log_info {
88    () => ($crate::log::__log!("\n"));
89    ($($arg:tt)*) => ($crate::__log!($crate::log::LogLevel::Info, "INFO: {}\n", format_args!($($arg)*)));
90}
91
92#[macro_export]
93#[doc(hidden)]
94macro_rules! __log_warn {
95    () => ($crate::log!("\n"));
96    ($($arg:tt)*) => ($crate::__log!($crate::log::LogLevel::Warn, "WARNING: {}\n", format_args!($($arg)*)));
97}
98
99#[macro_export]
100#[doc(hidden)]
101macro_rules! __log_error {
102    () => ($crate::log!("\n"));
103    ($($arg:tt)*) => ($crate::__log!($crate::log::LogLevel::Error, "ERROR: {}\n", format_args!($($arg)*)));
104}
105
106pub mod prelude {
107    pub use __log_error as log_error;
108    pub use __log_info as log_info;
109    pub use __log_warn as log_warn;
110}
111
112#[doc(hidden)]
113pub fn _log(level: LogLevel, args: core::fmt::Arguments) {
114    #[allow(clippy::unwrap_used)]
115    LOGGER.lock().unwrap().log(level, args);
116}