nonblocking_logger/
global.rs

1use crate::enums::log_level::LogLevel;
2use crate::structs::logger::Logger;
3use std::io;
4use std::sync::{OnceLock, RwLock};
5
6/// Global mutable logger instance wrapped in RwLock for thread-safe mutable access
7static GLOBAL_LOGGER: OnceLock<RwLock<Logger>> = OnceLock::new();
8
9fn init_global_logger() -> &'static RwLock<Logger> {
10    GLOBAL_LOGGER.get_or_init(|| RwLock::new(Logger::from_env()))
11}
12
13/// Get the global logger instance
14///
15/// This will initialize the logger with default settings if not already initialized.
16pub fn get_global_logger() -> &'static RwLock<Logger> {
17    init_global_logger()
18}
19
20/// Get a mutable reference to the global logger
21///
22/// This function returns a `RwLockWriteGuard` that provides mutable access to the global logger.
23/// The logger will be initialized with default settings if not already initialized.
24///
25/// # Examples
26///
27/// ```rust
28/// use nonblocking_logger::{get_global_mut, LogLevel};
29///
30/// fn main() -> Result<(), Box<dyn std::error::Error>> {
31///     let mut logger = get_global_mut();
32///     logger.set_level(LogLevel::Debug);
33///     logger.set_time_format("%Y-%m-%d %H:%M:%S%.3f");
34///     logger.set_format_for_level(LogLevel::Error, "[ERROR] {time} - {message}");
35///     Ok(())
36/// }
37/// ```
38pub fn get_global_mut() -> std::sync::RwLockWriteGuard<'static, Logger> {
39    GLOBAL_LOGGER
40        .get_or_init(|| RwLock::new(Logger::from_env()))
41        .write()
42        .unwrap()
43}
44
45/// Log an error message using the global logger
46///
47/// # Examples
48///
49/// ```rust
50/// use nonblocking_logger::error;
51///
52/// fn main() -> Result<(), Box<dyn std::error::Error>> {
53///     error("Something went wrong")?;
54///     Ok(())
55/// }
56/// ```
57pub fn error(message: &str) -> io::Result<()> {
58    get_global_logger().read().unwrap().error(message)
59}
60
61/// Log a warning message using the global logger
62pub fn warning(message: &str) -> io::Result<()> {
63    get_global_logger().read().unwrap().warning(message)
64}
65
66/// Log an info message using the global logger
67pub fn info(message: &str) -> io::Result<()> {
68    get_global_logger().read().unwrap().info(message)
69}
70
71/// Log a debug message using the global logger
72pub fn debug(message: &str) -> io::Result<()> {
73    get_global_logger().read().unwrap().debug(message)
74}
75
76/// Log a trace message using the global logger
77pub fn trace(message: &str) -> io::Result<()> {
78    get_global_logger().read().unwrap().trace(message)
79}
80
81/// Log a message using the global logger (always outputs, no level filtering)
82pub fn log(message: &str) -> io::Result<()> {
83    get_global_logger().read().unwrap().log(message)
84}
85
86/// Log a message with a specific level using the global logger (with filtering)
87pub fn log_with_level(level: LogLevel, message: &str) -> io::Result<()> {
88    get_global_logger().read().unwrap().log_with_level(level, message)
89}
90
91/// Log with lazy evaluation using the global logger
92///
93/// The closure is only executed if the log level is sufficient.
94///
95/// # Examples
96///
97/// ```rust
98/// use nonblocking_logger::debug_lazy;
99///
100/// fn expensive_function() -> String {
101///     "result".to_string()
102/// }
103///
104/// fn main() -> Result<(), Box<dyn std::error::Error>> {
105///     // This expensive computation only runs if debug logging is enabled
106///     debug_lazy(|| {
107///         format!("Expensive computation: {}", expensive_function())
108///     })?;
109///     Ok(())
110/// }
111/// ```
112pub fn error_lazy<F>(message_fn: F) -> io::Result<()>
113where
114    F: FnOnce() -> String,
115{
116    get_global_logger().read().unwrap().error_lazy(message_fn)
117}
118
119pub fn warning_lazy<F>(message_fn: F) -> io::Result<()>
120where
121    F: FnOnce() -> String,
122{
123    get_global_logger().read().unwrap().warning_lazy(message_fn)
124}
125
126pub fn info_lazy<F>(message_fn: F) -> io::Result<()>
127where
128    F: FnOnce() -> String,
129{
130    get_global_logger().read().unwrap().info_lazy(message_fn)
131}
132
133pub fn debug_lazy<F>(message_fn: F) -> io::Result<()>
134where
135    F: FnOnce() -> String,
136{
137    get_global_logger().read().unwrap().debug_lazy(message_fn)
138}
139
140pub fn trace_lazy<F>(message_fn: F) -> io::Result<()>
141where
142    F: FnOnce() -> String,
143{
144    get_global_logger().read().unwrap().trace_lazy(message_fn)
145}
146
147pub fn log_lazy<F>(message_fn: F) -> io::Result<()>
148where
149    F: FnOnce() -> String,
150{
151    get_global_logger()
152        .read()
153        .unwrap()
154        .log_lazy(message_fn)
155}
156
157
158#[cfg(test)]
159mod tests {
160    use super::*;
161
162    #[test]
163    fn test_convenience_functions() -> io::Result<()> {
164        error("Test error")?;
165        warning("Test warning")?;
166        info("Test info")?;
167        debug("Test debug")?;
168        trace("Test trace")?;
169
170        let global_logger = get_global_logger();
171        let logger = global_logger.read().unwrap();
172        assert_eq!(logger.level(), LogLevel::Info);
173
174        Ok(())
175    }
176
177    #[test]
178    fn test_lazy_convenience_functions() -> io::Result<()> {
179        let mut called = false;
180
181        debug_lazy(|| {
182            called = true;
183            "Debug message".to_string()
184        })?;
185
186        called = false;
187
188        info_lazy(|| {
189            called = true;
190            "Info message".to_string()
191        })?;
192
193        assert!(
194            called,
195            "Lazy closure should have been called for info level"
196        );
197
198        Ok(())
199    }
200}