rust_loguru/
logger.rs

1use lazy_static::lazy_static;
2use parking_lot::RwLock;
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5
6use crate::handler::Handler;
7use crate::level::LogLevel;
8use crate::record::Record;
9use crate::AsyncLoggerBuilder;
10use crate::AsyncLoggerHandle;
11
12/// Debug print macro that only prints when the debug_logging feature is enabled
13#[macro_export]
14macro_rules! debug_println {
15    ($($arg:tt)*) => {
16        #[cfg(feature = "debug_logging")]
17        println!(
18            "log_sync: record level = {:?}, logger level = {:?}",
19            record.level(),
20            self.level
21        );
22    };
23}
24
25/// A logger that can handle log records
26#[derive(Debug)]
27pub struct Logger {
28    /// The log level
29    level: LogLevel,
30    /// The handlers
31    handlers: Vec<Arc<RwLock<dyn Handler>>>,
32    /// Whether async logging is enabled
33    async_mode: bool,
34    /// The async logger handle
35    async_handle: Option<AsyncLoggerHandle>,
36    /// Whether the logger is active
37    active: Arc<AtomicBool>,
38}
39
40impl Logger {
41    /// Create a new logger with the given log level
42    pub fn new(level: LogLevel) -> Self {
43        Self {
44            level,
45            handlers: Vec::new(),
46            async_mode: false,
47            async_handle: None,
48            active: Arc::new(AtomicBool::new(true)),
49        }
50    }
51
52    /// Get the log level
53    pub fn level(&self) -> LogLevel {
54        self.level
55    }
56
57    /// Set the log level
58    pub fn set_level(&mut self, level: LogLevel) -> &mut Self {
59        self.level = level;
60        self
61    }
62
63    /// Add a handler to the logger
64    pub fn add_handler(&mut self, handler: Arc<RwLock<dyn Handler>>) -> &mut Self {
65        self.handlers.push(handler);
66        self
67    }
68
69    /// Remove a handler from the logger
70    pub fn remove_handler(&mut self, handler: Arc<RwLock<dyn Handler>>) -> &mut Self {
71        self.handlers.retain(|h| !Arc::ptr_eq(h, &handler));
72        self
73    }
74
75    /// Enable or disable async logging
76    pub fn set_async(&mut self, enable: bool, queue_size: Option<usize>) -> &mut Self {
77        // Don't make any changes if already in the requested state
78        if self.async_mode == enable {
79            return self;
80        }
81
82        if enable {
83            // Create a new async logger if needed
84            let builder = AsyncLoggerBuilder::new()
85                .with_queue_size(queue_size.unwrap_or(10000))
86                .with_handlers(self.handlers.clone())
87                .with_level(self.level);
88
89            self.async_handle = Some(builder.build());
90            self.async_mode = true;
91        } else {
92            // Shut down the async logger
93            if let Some(handle) = self.async_handle.take() {
94                handle.shutdown();
95            }
96            self.async_mode = false;
97        }
98
99        self
100    }
101
102    /// Set the number of worker threads for async logging
103    pub fn set_worker_threads(&mut self, count: usize) -> &mut Self {
104        if self.async_mode && self.async_handle.is_some() {
105            // Recreate the async logger with the new worker count
106            self.set_async(false, None);
107            let builder = AsyncLoggerBuilder::new()
108                .with_queue_size(10000)
109                .with_handlers(self.handlers.clone())
110                .with_level(self.level)
111                .with_workers(count);
112
113            self.async_handle = Some(builder.build());
114            self.async_mode = true;
115        }
116        self
117    }
118
119    /// Log a record
120    pub fn log(&self, record: &Record) -> bool {
121        if record.level() >= self.level() && self.active.load(Ordering::Relaxed) {
122            // If async logging is enabled, dispatch to the async logger
123            if self.async_mode {
124                if let Some(handle) = &self.async_handle {
125                    return handle.log(record.clone());
126                }
127            }
128
129            // Otherwise, log synchronously
130            return self.log_sync(record);
131        }
132        false
133    }
134
135    /// Log a record synchronously
136    fn log_sync(&self, record: &Record) -> bool {
137        let mut any_handled = false;
138        for handler in &self.handlers {
139            let guard = handler.write();
140            if guard.is_enabled() && record.level() >= guard.level() && guard.handle(record).is_ok()
141            {
142                any_handled = true;
143            }
144        }
145        any_handled
146    }
147
148    /// Log a message at the given level with fluent API
149    pub fn log_message(&self, level: LogLevel, message: impl Into<String>) -> bool {
150        let record = Record::new(level, message, None::<String>, None::<String>, None);
151        self.log(&record)
152    }
153
154    /// Log a debug message
155    pub fn debug(&self, message: impl Into<String>) -> bool {
156        self.log_message(LogLevel::Debug, message)
157    }
158
159    /// Log an info message
160    pub fn info(&self, message: impl Into<String>) -> bool {
161        self.log_message(LogLevel::Info, message)
162    }
163
164    /// Log a warning message
165    pub fn warn(&self, message: impl Into<String>) -> bool {
166        self.log_message(LogLevel::Warning, message)
167    }
168
169    /// Log an error message
170    pub fn error(&self, message: impl Into<String>) -> bool {
171        self.log_message(LogLevel::Error, message)
172    }
173
174    /// Enable or disable the logger
175    pub fn set_enabled(&self, enabled: bool) -> &Self {
176        self.active.store(enabled, Ordering::Relaxed);
177        self
178    }
179
180    /// Check if the logger is enabled
181    pub fn is_enabled(&self) -> bool {
182        self.active.load(Ordering::Relaxed)
183    }
184
185    /// Get all registered handlers
186    pub fn handlers(&self) -> &[Arc<RwLock<dyn Handler>>] {
187        &self.handlers
188    }
189
190    /// Check if async logging is enabled
191    pub fn is_async(&self) -> bool {
192        self.async_mode
193    }
194}
195
196impl Clone for Logger {
197    fn clone(&self) -> Self {
198        Self {
199            level: self.level,
200            handlers: self.handlers.clone(),
201            async_mode: self.async_mode,
202            async_handle: self.async_handle.clone(),
203            active: Arc::new(AtomicBool::new(self.active.load(Ordering::Relaxed))),
204        }
205    }
206}
207
208lazy_static! {
209    /// The global logger instance.
210    static ref GLOBAL_LOGGER: RwLock<Logger> = RwLock::new(Logger::new(LogLevel::Info));
211}
212
213/// Initialize the global logger
214pub fn init(logger: Logger) -> Logger {
215    let mut global = GLOBAL_LOGGER.write();
216    global.set_level(logger.level());
217    global.set_enabled(logger.is_enabled());
218    // Clear existing handlers
219    global.handlers.clear();
220    // Add new handlers
221    for handler in logger.handlers() {
222        global.add_handler(handler.clone());
223    }
224    logger
225}
226
227/// Returns a reference to the global logger.
228pub fn global() -> &'static RwLock<Logger> {
229    &GLOBAL_LOGGER
230}
231
232/// Logs a record using the global logger.
233///
234/// # Arguments
235///
236/// * `record` - The record to log
237///
238/// # Returns
239///
240/// `true` if the record was successfully logged by at least one handler, `false` otherwise.
241pub fn log(record: &Record) -> bool {
242    GLOBAL_LOGGER.read().log(record)
243}
244
245#[cfg(test)]
246mod tests {
247    use super::*;
248    use crate::handler::NullHandler;
249
250    #[test]
251    fn test_logger_creation() {
252        let logger = Logger::new(LogLevel::Info);
253        assert_eq!(logger.level(), LogLevel::Info);
254        assert!(logger.handlers.is_empty());
255    }
256
257    #[test]
258    fn test_logger_level() {
259        let mut logger = Logger::new(LogLevel::Info);
260        logger.set_level(LogLevel::Debug);
261        assert_eq!(logger.level(), LogLevel::Debug);
262    }
263
264    #[test]
265    fn test_logger_handler() {
266        let mut logger = Logger::new(LogLevel::Info);
267        let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
268        logger.add_handler(handler.clone());
269        assert_eq!(logger.handlers.len(), 1);
270        logger.remove_handler(handler);
271        assert!(logger.handlers.is_empty());
272    }
273
274    #[test]
275    fn test_logger_log() {
276        let mut logger = Logger::new(LogLevel::Info);
277        let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
278        logger.add_handler(handler);
279
280        // Test logging at different levels
281        assert!(!logger.debug("Debug message")); // Should not log (below Info)
282        assert!(logger.info("Info message")); // Should log
283        assert!(logger.warn("Warning message")); // Should log
284        assert!(logger.error("Error message")); // Should log
285    }
286
287    #[test]
288    fn test_logger_level_filtering() {
289        let mut logger = Logger::new(LogLevel::Warning);
290        let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
291        logger.add_handler(handler);
292
293        // Test level filtering
294        assert!(!logger.debug("Debug message")); // Should not log
295        assert!(!logger.info("Info message")); // Should not log
296        assert!(logger.warn("Warning message")); // Should log
297        assert!(logger.error("Error message")); // Should log
298    }
299
300    #[test]
301    fn test_logger_handler_filtering() {
302        let mut logger = Logger::new(LogLevel::Info);
303        let mut handler = NullHandler::new(LogLevel::Info);
304        handler.set_level(LogLevel::Warning);
305        let handler = Arc::new(RwLock::new(handler));
306        logger.add_handler(handler);
307
308        // Test handler level filtering
309        assert!(!logger.debug("Debug message")); // Should not log
310        assert!(!logger.info("Info message")); // Should not log
311        assert!(logger.warn("Warning message")); // Should log
312        assert!(logger.error("Error message")); // Should log
313    }
314
315    #[test]
316    fn test_logger_disabled_handler() {
317        let mut logger = Logger::new(LogLevel::Info);
318        let mut handler = NullHandler::new(LogLevel::Info);
319        handler.set_enabled(false);
320        let handler = Arc::new(RwLock::new(handler));
321        logger.add_handler(handler);
322
323        // Test disabled handler
324        assert!(!logger.info("Info message")); // Should not log
325    }
326
327    #[test]
328    fn test_logger_init() {
329        let logger = Logger::new(LogLevel::Info);
330        let logger = init(logger);
331        assert_eq!(logger.level(), LogLevel::Info);
332    }
333
334    #[test]
335    fn test_logger_enabled() {
336        let logger = Logger::new(LogLevel::Info);
337        assert!(logger.is_enabled());
338
339        logger.set_enabled(false);
340        assert!(!logger.is_enabled());
341
342        logger.set_enabled(true);
343        assert!(logger.is_enabled());
344    }
345
346    #[test]
347    fn test_async_logging() {
348        let mut logger = Logger::new(LogLevel::Info);
349        let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
350        logger.add_handler(handler);
351
352        // Test sync logging first
353        assert!(logger.info("Sync message"));
354        assert!(!logger.is_async());
355
356        // Enable async logging
357        logger.set_async(true, Some(1000));
358        assert!(logger.is_async());
359
360        // Test async logging
361        assert!(logger.info("Async message"));
362
363        // Test changing worker threads
364        logger.set_worker_threads(2);
365        assert!(logger.info("Message with 2 workers"));
366
367        // Disable async logging
368        logger.set_async(false, None);
369        assert!(!logger.is_async());
370
371        // Test sync logging again
372        assert!(logger.info("Sync message again"));
373    }
374
375    #[test]
376    fn test_compile_time_filtering() {
377        let logger = Logger::new(LogLevel::Info);
378
379        // This would be optimized out at compile time with feature flags
380        if !crate::compile_time_level_enabled!(LogLevel::Debug) {
381            assert!(!logger.debug("Debug message"));
382        }
383
384        // This would pass compile-time check and then be checked at runtime
385        if crate::compile_time_level_enabled!(LogLevel::Info) {
386            // Runtime check happens inside logger.log
387            let handler = Arc::new(RwLock::new(NullHandler::new(LogLevel::Info)));
388            let mut logger = Logger::new(LogLevel::Info);
389            logger.add_handler(handler);
390            assert!(logger.info("Info message"));
391        }
392    }
393}