flatbox_core/
logger.rs

1/* 
2 *
3 * Heavily inspired by `pretty_env_logger` https://crates.io/crates/pretty_env_logger/
4 * 
5 */
6
7use std::fmt;
8use std::sync::atomic::{AtomicUsize, Ordering};
9use colored::*;
10use log::{Metadata, Record, Log, LevelFilter, SetLoggerError};
11
12pub use log::{info, error, warn, debug, trace, Level};
13
14#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
15pub enum LoggerLevel {
16    Error,
17    Warning,
18    Info,
19    Debug,
20    Trace,
21    None,
22}
23
24impl From<LoggerLevel> for Option<Level> {
25    fn from(value: LoggerLevel) -> Self {
26        match value {
27            LoggerLevel::Debug => Some(Level::Debug),
28            LoggerLevel::Error => Some(Level::Error),
29            LoggerLevel::Info => Some(Level::Info),
30            LoggerLevel::Trace => Some(Level::Trace),
31            LoggerLevel::Warning => Some(Level::Warn),
32            LoggerLevel::None => None,
33        }
34    }
35}
36
37pub struct FlatboxLogger {
38    log_level: Level,
39}
40
41impl FlatboxLogger {
42    pub fn init(){
43        FlatboxLogger::try_init().expect("Failed to set logger");
44    }
45
46    pub fn try_init() -> Result<(), SetLoggerError> {
47        log::set_boxed_logger(Box::<FlatboxLogger>::default())?;
48        #[cfg(not(debug_assertions))]
49        log::set_max_level(LevelFilter::Info);
50        #[cfg(debug_assertions)]
51        log::set_max_level(LevelFilter::Debug);
52
53        Ok(())
54    }
55
56    pub fn init_with_level(logger_level: LoggerLevel){
57        FlatboxLogger::try_init_with_level(logger_level).expect("Failed to set logger with level");
58    }
59
60    pub fn try_init_with_level(logger_level: LoggerLevel) -> Result<(), SetLoggerError> {
61        if let Some(log_level) = logger_level.into() {
62            log::set_boxed_logger(Box::new(FlatboxLogger { log_level }))?;
63            log::set_max_level(log_level.to_level_filter());
64        }
65
66        Ok(())
67    }
68}
69
70impl Default for FlatboxLogger {
71    fn default() -> Self {
72        FlatboxLogger  {
73            #[cfg(not(debug_assertions))]
74            log_level: Level::Info,
75            #[cfg(debug_assertions)]
76            log_level: Level::Debug,
77        }
78    }
79}
80
81impl Log for FlatboxLogger {
82    fn enabled(&self, metadata: &Metadata) -> bool {
83        metadata.level() <= self.log_level
84    }
85
86    fn log(&self, record: &Record) {
87        if self.enabled(record.metadata()) {
88            let target = split_target(record.target());
89            let max_width = max_target_width(target);
90
91            let level = colored_level(record.level());
92
93            let target = Padded {
94                value: target.bold(),
95                width: max_width,
96            };
97
98            println!("{} {} > {}", level, target, record.args());
99        }
100    }
101
102    fn flush(&self) {}
103}
104
105struct Padded<T> {
106    value: T,
107    width: usize,
108}
109
110impl<T: fmt::Display> fmt::Display for Padded<T> {
111    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112        write!(f, "{: <width$}", self.value, width = self.width)
113    }
114}
115
116static MAX_MODULE_WIDTH: AtomicUsize = AtomicUsize::new(0);
117
118fn split_target(target: &str) -> &str {
119    match target.split_once("::") {
120        Some((module, _)) => module,
121        None => target,
122    }
123}
124
125fn max_target_width(target: &str) -> usize {
126    let max_width = MAX_MODULE_WIDTH.load(Ordering::Relaxed);
127    if max_width < target.len() {
128        MAX_MODULE_WIDTH.store(target.len(), Ordering::Relaxed);
129        target.len()
130    } else {
131        max_width
132    }
133}
134
135fn colored_level(level: Level) -> ColoredString {
136    match level {
137        Level::Trace => "TRACE".magenta(),
138        Level::Debug => "DEBUG".blue(),
139        Level::Info =>  "INFO ".green(),
140        Level::Warn =>  "WARN ".yellow(),
141        Level::Error => "ERROR".red(),
142    }
143}