FreedomLogger/
lib.rs

1/// FreedomLogger - A professional logging library for Rust
2///
3/// FreedomLogger provides clean, efficient logging with automatic rotation,
4/// multiple output formats, and error-proof operation. Designed for both
5/// development and production use.
6///
7/// Features:
8/// - Multiple log levels (ERROR, WARNING, INFO, DEBUG, TRACE) with filtering
9/// - Various output patterns (Basic, Detailed, Extended, JSON, Custom)
10/// - Automatic log rotation based on file size
11/// - Thread-safe concurrent logging
12/// - No external dependencies (except chrono for timestamps)
13/// - Error-proof operation (internal errors logged separately)
14/// - Easy single-initialization API
15/// - Flexible logging macros supporting formatted messages
16///
17/// Usage:
18/// 1. Initialize logger once in main(): logger::init(pattern, path, filename)
19/// 2. Log anywhere in your code:
20///    - Simple: logger::info("message")
21///    - Formatted: log_info!("User {} logged in", user_id)
22/// 3. All configuration is done at initialization time
23
24use std::sync::{Arc, Once};
25use std::path::Path;
26
27// Import all our modules
28pub mod error;
29pub mod core;
30pub mod format;
31pub mod rotation;
32
33// Re-export main types for user convenience
34pub use core::{LogLevel, Pattern, LoggerConfig, Logger};
35pub use error::LoggerError;
36
37/// Global logger instance - initialized once, used everywhere
38static mut GLOBAL_LOGGER: Option<Arc<Logger>> = None;
39static INIT_ONCE: Once = Once::new();
40
41/// Initialize the global logger with basic configuration
42///
43/// This is the simplest initialization - logs all levels with default settings.
44/// Uses 10MB max file size and keeps 5 backup files.
45///
46/// # Arguments
47/// * `pattern` - Log formatting pattern (Basic, Detailed, etc.)
48/// * `file_path` - Directory path where log files will be created
49/// * `file_name` - Base name for log files (without extension)
50///
51/// # Panics
52/// Panics if called more than once or if initialization fails
53pub fn log_init<P: AsRef<Path>>(pattern: Pattern, file_path: P, file_name: &str) {
54    let path_buf = file_path.as_ref().to_path_buf();
55    let config = LoggerConfig::basic(pattern, path_buf, file_name.to_string());
56    log_init_with_config(config);
57}
58
59/// Initialize the global logger with log level filtering
60///
61/// Logs only messages at or above the specified level.
62/// Uses default rotation settings (10MB, 5 backups).
63///
64/// # Arguments
65/// * `pattern` - Log formatting pattern
66/// * `file_path` - Directory path where log files will be created
67/// * `file_name` - Base name for log files (without extension)
68/// * `log_level` - Minimum log level to write
69///
70/// # Panics
71/// Panics if called more than once or if initialization fails
72pub fn log_init_with_level<P: AsRef<Path>>(
73    pattern: Pattern,
74    file_path: P,
75    file_name: &str,
76    log_level: LogLevel
77) {
78    let path_buf = file_path.as_ref().to_path_buf();
79    let config = LoggerConfig::with_level(pattern, path_buf, file_name.to_string(), log_level);
80    log_init_with_config(config);
81}
82
83/// Initialize the global logger with custom rotation settings
84///
85/// Full control over all logging parameters including rotation behavior.
86///
87/// # Arguments
88/// * `pattern` - Log formatting pattern
89/// * `file_path` - Directory path where log files will be created
90/// * `file_name` - Base name for log files (without extension)
91/// * `log_level` - Minimum log level to write
92/// * `max_file_size` - Maximum file size in bytes before rotation
93/// * `max_backup_files` - Number of backup files to keep
94///
95/// # Panics
96/// Panics if called more than once or if initialization fails
97pub fn log_init_with_rotation<P: AsRef<Path>>(
98    pattern: Pattern,
99    file_path: P,
100    file_name: &str,
101    log_level: LogLevel,
102    max_file_size: u64,
103    max_backup_files: u32,
104) {
105    let path_buf = file_path.as_ref().to_path_buf();
106    let config = LoggerConfig::with_rotation(
107        pattern,
108        path_buf,
109        file_name.to_string(),
110        log_level,
111        max_file_size,
112        max_backup_files
113    );
114    log_init_with_config(config);
115}
116
117/// Initialize with a complete configuration object
118///
119/// Internal method used by all public init functions.
120/// Ensures thread-safe single initialization.
121fn log_init_with_config(config: LoggerConfig) {
122    INIT_ONCE.call_once(|| {
123        let logger = Logger::new(config);
124        unsafe {
125            GLOBAL_LOGGER = Some(Arc::new(logger));
126        }
127    });
128}
129
130/// Get reference to the global logger instance
131///
132/// Returns the initialized logger or panics if not initialized.
133/// This is used internally by the logging functions.
134#[allow(static_mut_refs)]
135fn get_logger() -> &'static Arc<Logger> {
136    unsafe {
137        GLOBAL_LOGGER
138            .as_ref()
139            .expect("Logger not initialized - call logger::init() first")
140    }
141}
142
143/// Log an ERROR level message
144///
145/// Logs critical errors that indicate serious problems.
146/// These messages are always written regardless of log level filtering.
147///
148/// # Arguments
149/// * `message` - The error message to log
150pub fn log_error(message: &str) {
151    get_logger().error(message);
152}
153
154/// Log a WARNING level message
155///
156/// Logs warning messages that indicate potential issues.
157/// Written when log level is WARNING or higher.
158///
159/// # Arguments
160/// * `message` - The warning message to log
161pub fn log_warning(message: &str) {
162    get_logger().warning(message);
163}
164
165/// Log an INFO level message
166///
167/// Logs general information about application flow.
168/// Written when log level is INFO or higher.
169///
170/// # Arguments
171/// * `message` - The info message to log
172pub fn log_info(message: &str) {
173    get_logger().info(message);
174}
175
176/// Log a DEBUG level message
177///
178/// Logs detailed information useful for debugging.
179/// Written when log level is DEBUG or higher.
180///
181/// # Arguments
182/// * `message` - The debug message to log
183pub fn log_debug(message: &str) {
184    get_logger().debug(message);
185}
186
187/// Log a TRACE level message
188///
189/// Logs very detailed trace information.
190/// Written when log level is TRACE (logs everything).
191///
192/// # Arguments
193/// * `message` - The trace message to log
194pub fn log_trace(message: &str) {
195    get_logger().trace(message);
196}
197
198// ============================================================================
199// MACROS VOOR FORMATTED LOGGING
200// ============================================================================
201
202/// Macro for logging ERROR messages with formatting support
203///
204/// Supports both simple messages and formatted strings with arguments.
205/// Uses Rust's built-in format! macro for automatic type handling.
206///
207
208#[macro_export]
209macro_rules! log_error {
210    // Simple message zonder formatting
211    ($msg:expr) => {
212        $crate::log_error($msg);
213    };
214
215    // Formatted message met argumenten
216    ($fmt:expr, $($arg:expr),+ $(,)?) => {
217        $crate::log_error(&format!($fmt, $($arg),+));
218    };
219}
220
221/// Macro for logging WARNING messages with formatting support
222///
223/// Supports both simple messages and formatted strings with arguments.
224///
225
226/// ```
227#[macro_export]
228macro_rules! log_warning {
229    ($msg:expr) => {
230        $crate::log_warning($msg);
231    };
232
233    ($fmt:expr, $($arg:expr),+ $(,)?) => {
234        $crate::log_warning(&format!($fmt, $($arg),+));
235    };
236}
237
238/// Macro for logging INFO messages with formatting support
239///
240/// Supports both simple messages and formatted strings with arguments.
241///
242
243#[macro_export]
244macro_rules! log_info {
245    ($msg:expr) => {
246        $crate::log_info($msg);
247    };
248
249    ($fmt:expr, $($arg:expr),+ $(,)?) => {
250        $crate::log_info(&format!($fmt, $($arg),+));
251    };
252}
253
254/// Macro for logging DEBUG messages with formatting support
255///
256/// This macro solves the original problem! It supports both simple messages
257/// and formatted strings with arguments, automatically handling any type
258/// that implements Display or Debug.
259///
260
261#[macro_export]
262macro_rules! log_debug {
263    ($msg:expr) => {
264        $crate::log_debug($msg);
265    };
266
267    ($fmt:expr, $($arg:expr),+ $(,)?) => {
268        $crate::log_debug(&format!($fmt, $($arg),+));
269    };
270}
271
272/// Macro for logging TRACE messages with formatting support
273///
274/// Supports both simple messages and formatted strings with arguments.
275///
276
277#[macro_export]
278macro_rules! log_trace {
279    ($msg:expr) => {
280        $crate::log_trace($msg);
281    };
282
283    ($fmt:expr, $($arg:expr),+ $(,)?) => {
284        $crate::log_trace(&format!($fmt, $($arg),+));
285    };
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291    use tempfile::tempdir;
292    use std::fs;
293    use std::path::PathBuf;
294
295    #[test]
296    fn test_basic_logging_integration() {
297        let temp_dir = tempdir().unwrap();
298
299        // Initialize logger
300        log_init(Pattern::Basic, temp_dir.path(), "test");
301
302        // Test basic function logging
303        log_info("Test info message");
304        log_warning("Test warning message");
305        log_error("Test error message");
306
307        // Test the new macros with formatting
308        let database_path = PathBuf::from("/var/lib/myapp/database.db");
309        let user_id = 12345;
310        let status = "active";
311
312        // This should now work without any errors!
313        log_debug!("Database path: {:?}", database_path);
314        log_info!("User {} has status: {}", user_id, status);
315        log_warning!("Processing {} items", 42);
316        log_error!("Failed to connect to {}", "localhost:5432");
317
318        // Test simple messages still work
319        log_debug!("Simple debug message");
320        log_info!("Simple info message");
321
322        // Check that log file was created
323        let log_file = temp_dir.path().join("test.log");
324        assert!(log_file.exists());
325
326        // Check log content
327        let content = fs::read_to_string(&log_file).unwrap();
328
329        // Test basic function messages
330        assert!(content.contains("INFO: Test info message"));
331        assert!(content.contains("WARNING: Test warning message"));
332        assert!(content.contains("ERROR: Test error message"));
333
334        // Test macro messages
335        assert!(content.contains("Database path:"));
336        assert!(content.contains("User 12345 has status: active"));
337        assert!(content.contains("Processing 42 items"));
338        assert!(content.contains("Failed to connect to localhost:5432"));
339        assert!(content.contains("Simple debug message"));
340    }
341}