1use std::collections::HashSet;
2use std::sync::{Arc, RwLock};
3
4use tracing::Level;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
7pub enum LogLevel {
8 Trace = 0,
9 Debug = 1,
10 #[default]
11 Info = 2,
12 Warn = 3,
13 Error = 4,
14 Silent = 5,
15}
16
17pub trait ILogger: Send + Sync {
18 fn log(&self, level: Level, module: &str, message: &str);
19
20 fn trace(&self, module: &str, message: &str) {
21 self.log(Level::TRACE, module, message);
22 }
23
24 fn debug(&self, module: &str, message: &str) {
25 self.log(Level::DEBUG, module, message);
26 }
27
28 fn info(&self, module: &str, message: &str) {
29 self.log(Level::INFO, module, message);
30 }
31
32 fn warn(&self, module: &str, message: &str) {
33 self.log(Level::WARN, module, message);
34 }
35
36 fn error(&self, module: &str, message: &str) {
37 self.log(Level::ERROR, module, message);
38 }
39}
40
41#[derive(Default)]
42pub struct ConsoleLogger;
43
44impl ConsoleLogger {
45 pub fn new() -> Self {
46 Self
47 }
48}
49
50impl ILogger for ConsoleLogger {
51 fn log(&self, level: Level, module: &str, message: &str) {
52 match level {
53 Level::TRACE => tracing::trace!("[{module}] {message}"),
54 Level::DEBUG => tracing::debug!("[{module}] {message}"),
55 Level::INFO => tracing::info!("[{module}] {message}"),
56 Level::WARN => tracing::warn!("[{module}] {message}"),
57 Level::ERROR => tracing::error!("[{module}] {message}"),
58 }
59 }
60}
61
62#[derive(Default)]
63pub struct RichConsoleLogger;
64
65impl RichConsoleLogger {
66 pub fn new() -> Self {
67 Self
68 }
69}
70
71impl ILogger for RichConsoleLogger {
72 fn log(&self, level: Level, module: &str, message: &str) {
73 let (prefix, color) = match level {
74 Level::TRACE => ("TRACE", "\x1b[90m"),
75 Level::DEBUG => ("DEBUG", "\x1b[36m"),
76 Level::INFO => ("INFO ", "\x1b[32m"),
77 Level::WARN => ("WARN ", "\x1b[33m"),
78 Level::ERROR => ("ERROR", "\x1b[31m"),
79 };
80 println!("{color}[{prefix}] [{module}] {message}\x1b[0m");
81 }
82}
83
84#[derive(Clone)]
85pub struct LogService {
86 logger: Arc<dyn ILogger>,
87 level: Arc<RwLock<LogLevel>>,
88 muted_modules: Arc<RwLock<HashSet<String>>>,
89}
90
91impl LogService {
92 pub fn new(logger: Arc<dyn ILogger>) -> Self {
93 Self {
94 logger,
95 level: Arc::new(RwLock::new(LogLevel::default())),
96 muted_modules: Arc::new(RwLock::new(HashSet::new())),
97 }
98 }
99
100 pub fn logger(&self) -> Arc<dyn ILogger> {
101 Arc::clone(&self.logger)
102 }
103
104 pub fn set_level(&self, level: LogLevel) {
105 *self.level.write().unwrap() = level;
106 }
107
108 pub fn level(&self) -> LogLevel {
109 *self.level.read().unwrap()
110 }
111
112 pub fn mute_module(&self, module: &str) {
113 self.muted_modules
114 .write()
115 .unwrap()
116 .insert(module.to_owned());
117 }
118
119 pub fn unmute_module(&self, module: &str) {
120 self.muted_modules.write().unwrap().remove(module);
121 }
122
123 pub fn is_muted(&self, module: &str) -> bool {
124 self.muted_modules.read().unwrap().contains(module)
125 }
126
127 fn should_log(&self, level: LogLevel, module: &str) -> bool {
128 if self.is_muted(module) {
129 return false;
130 }
131 let current_level = self.level();
132 level >= current_level
133 }
134
135 pub fn trace(&self, module: &str, message: &str) {
136 if self.should_log(LogLevel::Trace, module) {
137 self.logger.trace(module, message);
138 }
139 }
140
141 pub fn debug(&self, module: &str, message: &str) {
142 if self.should_log(LogLevel::Debug, module) {
143 self.logger.debug(module, message);
144 }
145 }
146
147 pub fn info(&self, module: &str, message: &str) {
148 if self.should_log(LogLevel::Info, module) {
149 self.logger.info(module, message);
150 }
151 }
152
153 pub fn warn(&self, module: &str, message: &str) {
154 if self.should_log(LogLevel::Warn, module) {
155 self.logger.warn(module, message);
156 }
157 }
158
159 pub fn error(&self, module: &str, message: &str) {
160 if self.should_log(LogLevel::Error, module) {
161 self.logger.error(module, message);
162 }
163 }
164}