1use std::{
2 fmt::{self, Arguments, Debug},
3 str::FromStr,
4 sync::{
5 OnceLock,
6 atomic::{AtomicU8, Ordering},
7 },
8};
9
10mod cli_logger;
11pub use cli_logger::*;
12
13static LOGGER: OnceLock<&'static dyn Log> = OnceLock::new();
14
15pub fn set_logger(logger: &'static dyn Log) -> Result<(), &'static dyn Log> {
21 LOGGER.set(logger)
22}
23
24pub fn get_logger() -> Option<&'static dyn Log> {
26 LOGGER.get().copied()
27}
28
29#[derive(Debug, thiserror::Error)]
30pub enum LogLevelParseError {
31 #[error("Invalid log level: {0}")]
32 InvalidInput(String),
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
37pub enum LevelFilter {
38 Off,
39 Error,
40 Warn,
41 Info,
42 Debug,
43 Trace,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
50pub enum LogLevel {
51 Error = 1,
52 Warn,
53 Info,
54 Debug,
55 Trace,
56}
57
58impl fmt::Display for LogLevel {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 LogLevel::Error => f.write_str("Error"),
62 LogLevel::Warn => f.write_str("Warn"),
63 LogLevel::Info => f.write_str("Info"),
64 LogLevel::Debug => f.write_str("Debug"),
65 LogLevel::Trace => f.write_str("Trace"),
66 }
67 }
68}
69
70impl FromStr for LevelFilter {
71 type Err = LogLevelParseError;
72
73 fn from_str(s: &str) -> Result<Self, Self::Err> {
74 match s.to_ascii_lowercase().as_str() {
75 "off" | "quiet" => Ok(LevelFilter::Off),
76 "error" => Ok(LevelFilter::Error),
77 "warn" => Ok(LevelFilter::Warn),
78 "info" => Ok(LevelFilter::Info),
79 "debug" => Ok(LevelFilter::Debug),
80 "trace" => Ok(LevelFilter::Trace),
81 _ => Err(LogLevelParseError::InvalidInput(s.to_owned())),
82 }
83 }
84}
85
86static MAX_LOG_LEVEL: AtomicU8 = AtomicU8::new(0);
87pub fn set_max_log_level(level: LevelFilter) {
91 MAX_LOG_LEVEL.store(level as u8, Ordering::Relaxed);
92}
93
94pub fn can_log(level: LogLevel) -> bool {
98 MAX_LOG_LEVEL.load(Ordering::Relaxed) >= level as u8
99}
100
101pub trait Log: Sync + Debug {
102 fn enabled(&self, level: LogLevel) -> bool {
104 can_log(level)
105 }
106
107 fn log_unchecked(&self, level: LogLevel, msg: Arguments);
111
112 fn log(&self, level: LogLevel, msg: Arguments) {
114 if self.enabled(level) {
115 self.log_unchecked(level, msg);
116 }
117 }
118}
119
120#[macro_export]
124macro_rules! log {
125 ($level:expr, $($arg:tt)*) => {{
126 if let Some(logger) = $crate::log::get_logger()
127 && logger.enabled($level) {
128 logger.log_unchecked($level, format_args!($($arg)*))
129 }
130 }};
131}
132
133#[macro_export]
137macro_rules! error {
138 ($($arg:tt)*) => { $crate::log!($crate::log::LogLevel::Error, $($arg)*) };
139}
140
141#[macro_export]
145macro_rules! warn {
146 ($($arg:tt)*) => { $crate::log!($crate::log::LogLevel::Warn, $($arg)*) };
147}
148
149#[macro_export]
153macro_rules! info {
154 ($($arg:tt)*) => { $crate::log!($crate::log::LogLevel::Info, $($arg)*) };
155}
156
157#[macro_export]
161macro_rules! debug {
162 ($($arg:tt)*) => { $crate::log!($crate::log::LogLevel::Debug, $($arg)*) };
163}
164
165#[macro_export]
169macro_rules! trace {
170 ($($arg:tt)*) => { $crate::log!($crate::log::LogLevel::Trace, $($arg)*) };
171}