mabi_core/logging/
dynamic.rs1use std::sync::{Arc, OnceLock};
26
27use parking_lot::RwLock;
28use tracing_subscriber::reload;
29use tracing_subscriber::EnvFilter;
30
31use super::config::LogLevel;
32use crate::error::Result;
33
34pub type ReloadHandle = reload::Handle<EnvFilter, tracing_subscriber::Registry>;
36
37static GLOBAL_CONTROLLER: OnceLock<LogLevelController> = OnceLock::new();
39
40#[derive(Clone)]
45pub struct LogLevelController {
46 inner: Arc<LogLevelControllerInner>,
47}
48
49struct LogLevelControllerInner {
50 handle: ReloadHandle,
52
53 current_level: RwLock<LogLevel>,
55
56 module_levels: RwLock<std::collections::HashMap<String, LogLevel>>,
58}
59
60impl LogLevelController {
61 pub fn new(handle: ReloadHandle, initial_level: LogLevel) -> Self {
63 Self {
64 inner: Arc::new(LogLevelControllerInner {
65 handle,
66 current_level: RwLock::new(initial_level),
67 module_levels: RwLock::new(std::collections::HashMap::new()),
68 }),
69 }
70 }
71
72 pub fn register_global(self) -> bool {
77 GLOBAL_CONTROLLER.set(self).is_ok()
78 }
79
80 pub fn global() -> Option<&'static LogLevelController> {
82 GLOBAL_CONTROLLER.get()
83 }
84
85 pub fn current_level(&self) -> LogLevel {
87 *self.inner.current_level.read()
88 }
89
90 pub fn module_levels(&self) -> std::collections::HashMap<String, LogLevel> {
92 self.inner.module_levels.read().clone()
93 }
94
95 pub fn set_level(&self, level: LogLevel) -> Result<()> {
100 {
101 let mut current = self.inner.current_level.write();
102 *current = level;
103 }
104 self.apply_filter()
105 }
106
107 pub fn set_module_level(&self, module: impl Into<String>, level: LogLevel) -> Result<()> {
113 {
114 let mut levels = self.inner.module_levels.write();
115 levels.insert(module.into(), level);
116 }
117 self.apply_filter()
118 }
119
120 pub fn remove_module_level(&self, module: &str) -> Result<()> {
122 {
123 let mut levels = self.inner.module_levels.write();
124 levels.remove(module);
125 }
126 self.apply_filter()
127 }
128
129 pub fn clear_module_levels(&self) -> Result<()> {
131 {
132 let mut levels = self.inner.module_levels.write();
133 levels.clear();
134 }
135 self.apply_filter()
136 }
137
138 pub fn enable_debug_mode(&self) -> Result<DebugModeGuard> {
143 let previous_level = self.current_level();
144 let previous_modules = self.module_levels();
145
146 self.set_level(LogLevel::Debug)?;
147
148 Ok(DebugModeGuard {
149 controller: self.clone(),
150 previous_level,
151 previous_modules,
152 })
153 }
154
155 pub fn enable_module_trace(&self, module: impl Into<String>) -> Result<ModuleTraceGuard> {
157 let module = module.into();
158 let previous_level = self.inner.module_levels.read().get(&module).copied();
159
160 self.set_module_level(module.clone(), LogLevel::Trace)?;
161
162 Ok(ModuleTraceGuard {
163 controller: self.clone(),
164 module,
165 previous_level,
166 })
167 }
168
169 fn apply_filter(&self) -> Result<()> {
171 let filter = self.build_filter();
172 self.inner
173 .handle
174 .reload(filter)
175 .map_err(|e| crate::error::Error::Config(format!("Failed to reload log filter: {}", e)))
176 }
177
178 fn build_filter(&self) -> EnvFilter {
180 let level = *self.inner.current_level.read();
181 let modules = self.inner.module_levels.read();
182
183 let mut filter_str = format!("trap_sim={}", level.as_filter_str());
184
185 for (module, module_level) in modules.iter() {
186 filter_str.push_str(&format!(",{}={}", module, module_level.as_filter_str()));
187 }
188
189 if !modules.contains_key("tokio") {
191 filter_str.push_str(",tokio=warn");
192 }
193 if !modules.contains_key("hyper") {
194 filter_str.push_str(",hyper=warn");
195 }
196
197 EnvFilter::try_new(&filter_str).unwrap_or_else(|_| EnvFilter::new(level.as_filter_str()))
198 }
199
200 pub fn current_filter_string(&self) -> String {
202 let level = *self.inner.current_level.read();
203 let modules = self.inner.module_levels.read();
204
205 let mut parts = vec![format!("trap_sim={}", level.as_filter_str())];
206
207 for (module, module_level) in modules.iter() {
208 parts.push(format!("{}={}", module, module_level.as_filter_str()));
209 }
210
211 parts.join(",")
212 }
213}
214
215impl std::fmt::Debug for LogLevelController {
216 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
217 f.debug_struct("LogLevelController")
218 .field("current_level", &self.current_level())
219 .field("module_levels", &self.module_levels())
220 .finish()
221 }
222}
223
224#[must_use = "Debug mode will be reverted when the guard is dropped"]
228pub struct DebugModeGuard {
229 controller: LogLevelController,
230 previous_level: LogLevel,
231 previous_modules: std::collections::HashMap<String, LogLevel>,
232}
233
234impl Drop for DebugModeGuard {
235 fn drop(&mut self) {
236 let _ = self.controller.set_level(self.previous_level);
238
239 {
241 let mut levels = self.controller.inner.module_levels.write();
242 *levels = self.previous_modules.clone();
243 }
244 let _ = self.controller.apply_filter();
245 }
246}
247
248#[must_use = "Module trace will be disabled when the guard is dropped"]
250pub struct ModuleTraceGuard {
251 controller: LogLevelController,
252 module: String,
253 previous_level: Option<LogLevel>,
254}
255
256impl Drop for ModuleTraceGuard {
257 fn drop(&mut self) {
258 let _ = match self.previous_level {
259 Some(level) => self.controller.set_module_level(&self.module, level),
260 None => self.controller.remove_module_level(&self.module),
261 };
262 }
263}
264
265#[derive(Debug, Clone)]
267pub struct LevelChangeEvent {
268 pub change_type: LevelChangeType,
270 pub previous_level: Option<LogLevel>,
272 pub new_level: LogLevel,
274 pub timestamp: std::time::SystemTime,
276}
277
278#[derive(Debug, Clone, PartialEq, Eq)]
280pub enum LevelChangeType {
281 Global,
283 Module(String),
285}
286
287#[derive(Debug, Clone, Default)]
289pub struct LevelChangeStats {
290 pub total_changes: u64,
292 pub global_changes: u64,
294 pub module_changes: u64,
296 pub last_change: Option<std::time::SystemTime>,
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
309 fn test_level_change_event() {
310 let event = LevelChangeEvent {
311 change_type: LevelChangeType::Global,
312 previous_level: Some(LogLevel::Info),
313 new_level: LogLevel::Debug,
314 timestamp: std::time::SystemTime::now(),
315 };
316
317 assert_eq!(event.change_type, LevelChangeType::Global);
318 assert_eq!(event.previous_level, Some(LogLevel::Info));
319 assert_eq!(event.new_level, LogLevel::Debug);
320 }
321
322 #[test]
323 fn test_level_change_type() {
324 let global = LevelChangeType::Global;
325 let module = LevelChangeType::Module("test::module".to_string());
326
327 assert_ne!(global, module);
328 }
329
330 #[test]
331 fn test_level_change_stats() {
332 let mut stats = LevelChangeStats::default();
333 stats.total_changes = 5;
334 stats.global_changes = 2;
335 stats.module_changes = 3;
336
337 assert_eq!(stats.total_changes, 5);
338 assert_eq!(stats.global_changes + stats.module_changes, 5);
339 }
340}