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}