Skip to main content

physdes/
logging.rs

1//! Logging module for physdes-rs.
2//!
3//! This module provides optional logging capabilities when the `std` feature is enabled.
4//! It uses `env_logger` for flexible logging configuration via environment variables.
5//!
6//! ## Usage
7//!
8//! ```rust
9//! use physdes::logging::init_logger;
10//!
11//! fn main() {
12//!     init_logger();
13//!     log::info!("Application started");
14//! }
15//! ```
16//!
17//! ## Environment Variables
18//!
19//! - `RUST_LOG`: Controls log level (debug, info, warn, error)
20//! - `RUST_LOG_STYLE`: Controls colored output
21//!
22//! Example:
23//! ```bash
24//! RUST_LOG=debug cargo run --features std
25//! ```
26
27use log::LevelFilter;
28use std::sync::atomic::{AtomicBool, Ordering};
29
30/// Indicates whether the logger has been initialized.
31static LOGGER_INITIALIZED: AtomicBool = AtomicBool::new(false);
32
33/// Initialize the logger with default filter (info).
34///
35/// # Panics
36///
37/// Panics if the logger has already been initialized.
38pub fn init_logger() {
39    env_logger::Builder::from_default_env()
40        .filter_level(LevelFilter::Info)
41        .init();
42    LOGGER_INITIALIZED.store(true, Ordering::SeqCst);
43}
44
45/// Initialize the logger with a custom filter string.
46///
47/// # Panics
48///
49/// Panics if the logger has already been initialized.
50///
51/// # Arguments
52///
53/// * `filter` - A filter string (e.g., "debug", "warn", "error")
54pub fn init_logger_with_filter(filter: &str) {
55    env_logger::Builder::from_default_env()
56        .filter_level(filter.parse().unwrap_or(LevelFilter::Info))
57        .init();
58    LOGGER_INITIALIZED.store(true, Ordering::SeqCst);
59}
60
61/// Try to initialize the logger with default filter (info).
62///
63/// Returns `Ok(())` if initialization succeeded, or `Err(&str)` if the
64/// logger has already been initialized.
65pub fn try_init_logger() -> Result<(), &'static str> {
66    env_logger::Builder::from_default_env()
67        .filter_level(LevelFilter::Info)
68        .try_init()
69        .map(|_| {
70            LOGGER_INITIALIZED.store(true, Ordering::SeqCst);
71        })
72        .map_err(|_| "Logger already initialized")
73}
74
75/// Try to initialize the logger with a custom filter string.
76///
77/// Returns `Ok(())` if initialization succeeded, or `Err(&str)` if the
78/// logger has already been initialized.
79///
80/// # Arguments
81///
82/// * `filter` - A filter string (e.g., "debug", "warn", "error")
83pub fn try_init_logger_with_filter(filter: &str) -> Result<(), &'static str> {
84    env_logger::Builder::from_default_env()
85        .filter_level(filter.parse().unwrap_or(LevelFilter::Info))
86        .try_init()
87        .map(|_| {
88            LOGGER_INITIALIZED.store(true, Ordering::SeqCst);
89        })
90        .map_err(|_| "Logger already initialized")
91}
92
93/// Check if the logger has been initialized.
94///
95/// Returns `true` if the logger is active, `false` otherwise.
96pub fn is_logger_initialized() -> bool {
97    LOGGER_INITIALIZED.load(Ordering::SeqCst)
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_try_init_logger() {
106        // Should succeed since we haven't initialized yet in this test
107        let result = try_init_logger();
108        // Note: This may fail if another test initialized the logger
109        // In that case, we just check it's either Ok or Err
110        if result.is_ok() {
111            assert!(is_logger_initialized());
112        }
113    }
114
115    #[test]
116    fn test_try_init_logger_with_filter() {
117        // Try with a valid filter
118        let result = try_init_logger_with_filter("debug");
119        // Note: This may fail if logger already initialized
120        // We're just testing the function doesn't panic
121        let _ = result;
122    }
123
124    #[test]
125    fn test_is_logger_initialized() {
126        // Just verify the function works without panicking
127        let _ = is_logger_initialized();
128    }
129
130    #[test]
131    fn test_init_logger_panics() {
132        // Check that init_logger doesn't crash when called
133        // It may panic if already initialized, which is expected
134        let _ = std::panic::catch_unwind(|| {
135            init_logger();
136        });
137    }
138
139    #[test]
140    fn test_init_logger_with_filter_panics() {
141        let _ = std::panic::catch_unwind(|| {
142            init_logger_with_filter("debug");
143        });
144    }
145}