1use 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}