1use colored::Colorize;
2use log::Level;
3use std::{env, str::FromStr, sync::OnceLock};
4
5#[derive(Clone)]
6pub struct Logger {
7 level: log::Level,
8}
9
10impl Logger {
11 pub fn new(level: log::Level) -> Self {
12 Self { level }
13 }
14}
15
16impl log::Log for Logger {
17 fn enabled(&self, metadata: &log::Metadata) -> bool {
18 metadata.level() <= self.level
19 }
20
21 fn log(&self, record: &log::Record) {
22 if !self.enabled(record.metadata()) {
23 return;
24 }
25
26 let time = chrono::Local::now()
27 .format("%Y-%m-%d %H:%M:%S")
28 .to_string()
29 .magenta();
30
31 let level = match record.level() {
32 Level::Error => "ERROR".red(),
33 Level::Warn => "WARN".yellow(),
34 Level::Info => "INFO".green(),
35 Level::Debug => "DEBUG".green(),
36 Level::Trace => "TRACE".green(),
37 };
38
39 let prefix = match (record.module_path(), record.line()) {
40 (Some(module_path), Some(line)) => format!("{}:{}", module_path, line).cyan(),
41 (Some(module_path), None) => module_path.cyan(),
42 _ => "".cyan(),
43 };
44
45 let log = format!("{} {} {} {}", time, prefix, level, record.args());
46 println!("{}", log);
47 }
48
49 fn flush(&self) {}
50}
51
52static LOGGER: OnceLock<()> = OnceLock::new();
53
54pub fn init_logger() {
55 if LOGGER.get().is_some() {
56 return;
57 }
58
59 LOGGER.get_or_init(|| {
60 let level = env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string());
61 let level = log::Level::from_str(&level).unwrap_or(log::Level::Info);
62
63 let logger = Logger::new(level.clone());
64 log::set_logger(Box::leak(Box::new(logger))).unwrap();
65 log::set_max_level(level.to_level_filter());
66 });
67}
68
69pub fn init_logger_with_level(level: log::Level) {
70 if LOGGER.get().is_some() {
71 return;
72 }
73
74 LOGGER.get_or_init(|| {
75 let logger = Logger::new(level.clone());
76 log::set_logger(Box::leak(Box::new(logger))).unwrap();
77 log::set_max_level(level.to_level_filter());
78 });
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn test() {
87 init_logger();
88 log::info!("Hello, world!");
89 log::warn!("Hello, world!");
90 log::error!("Hello, world!");
91 log::debug!("Hello, world!");
92 log::trace!("Hello, world!");
93 }
94
95 #[test]
96 fn test_with_env() {
97 env::set_var("RUST_LOG", "debug");
98 init_logger();
99 log::info!("Hello, world!");
100 log::warn!("Hello, world!");
101 log::error!("Hello, world!");
102 log::debug!("Hello, world!");
103 log::trace!("Hello, world!");
104 env::remove_var("RUST_LOG");
105 }
106
107 #[test]
108 fn test_with_level_debug() {
109 init_logger_with_level(log::Level::Trace);
110 log::info!("Hello, world!");
111 log::warn!("Hello, world!");
112 log::error!("Hello, world!");
113 log::debug!("Hello, world!");
114 log::trace!("Hello, world!");
115 }
116}