Skip to main content

browser_commander/core/
logger.rs

1//! Logger configuration for browser automation.
2//!
3//! This module provides a simple interface for creating loggers
4//! with configurable verbosity levels.
5
6use tracing::Level;
7use tracing_subscriber::{fmt, prelude::*, EnvFilter};
8
9/// Check if verbose logging is enabled via environment or CLI args.
10///
11/// Checks for `VERBOSE` environment variable or `--verbose` CLI argument.
12///
13/// # Returns
14///
15/// `true` if verbose mode is enabled, `false` otherwise.
16pub fn is_verbose_enabled() -> bool {
17    std::env::var("VERBOSE").is_ok() || std::env::args().any(|arg| arg == "--verbose")
18}
19
20/// Logger configuration options.
21#[derive(Debug, Clone, Default)]
22pub struct LoggerOptions {
23    /// Enable verbose (debug level) logging.
24    pub verbose: bool,
25}
26
27/// Initialize the global tracing subscriber with the given options.
28///
29/// This should be called once at the start of the application.
30///
31/// # Arguments
32///
33/// * `options` - Logger configuration options
34///
35/// # Example
36///
37/// ```
38/// use browser_commander::core::logger::{init_logger, LoggerOptions};
39///
40/// init_logger(LoggerOptions { verbose: true });
41/// ```
42pub fn init_logger(options: LoggerOptions) {
43    let level = if options.verbose {
44        Level::DEBUG
45    } else {
46        Level::ERROR
47    };
48
49    let filter = EnvFilter::from_default_env().add_directive(level.into());
50
51    let subscriber = fmt::layer().with_target(true).with_level(true);
52
53    tracing_subscriber::registry()
54        .with(filter)
55        .with(subscriber)
56        .try_init()
57        .ok(); // Ignore error if already initialized
58}
59
60/// A simple logger wrapper that respects verbosity settings.
61#[derive(Debug, Clone)]
62pub struct Logger {
63    verbose: bool,
64}
65
66impl Logger {
67    /// Create a new logger instance.
68    ///
69    /// # Arguments
70    ///
71    /// * `options` - Logger configuration options
72    pub fn new(options: LoggerOptions) -> Self {
73        Self {
74            verbose: options.verbose,
75        }
76    }
77
78    /// Log a debug message (only shown in verbose mode).
79    ///
80    /// # Arguments
81    ///
82    /// * `message_fn` - A function that returns the message to log.
83    ///   This is only called if verbose mode is enabled.
84    pub fn debug<F>(&self, mut message_fn: F)
85    where
86        F: FnMut() -> String,
87    {
88        if self.verbose {
89            let msg = message_fn();
90            tracing::debug!("{}", msg);
91        }
92    }
93
94    /// Log an error message (always shown).
95    ///
96    /// # Arguments
97    ///
98    /// * `message` - The error message to log
99    pub fn error(&self, message: &str) {
100        tracing::error!("{}", message);
101    }
102
103    /// Log an info message.
104    ///
105    /// # Arguments
106    ///
107    /// * `message` - The info message to log
108    pub fn info(&self, message: &str) {
109        tracing::info!("{}", message);
110    }
111
112    /// Log a warning message.
113    ///
114    /// # Arguments
115    ///
116    /// * `message` - The warning message to log
117    pub fn warn(&self, message: &str) {
118        tracing::warn!("{}", message);
119    }
120
121    /// Check if verbose logging is enabled.
122    pub fn is_verbose(&self) -> bool {
123        self.verbose
124    }
125}
126
127impl Default for Logger {
128    fn default() -> Self {
129        Self {
130            verbose: is_verbose_enabled(),
131        }
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn logger_default_is_not_verbose() {
141        // Note: This test may fail if VERBOSE env var is set
142        // In a clean environment, default should be non-verbose
143        std::env::remove_var("VERBOSE");
144        let logger = Logger::default();
145        // We can't assert on is_verbose since it also checks CLI args
146        // Just verify the logger can be created
147        assert!(!logger.is_verbose() || is_verbose_enabled());
148    }
149
150    #[test]
151    fn logger_can_be_created_with_verbose_true() {
152        let logger = Logger::new(LoggerOptions { verbose: true });
153        assert!(logger.is_verbose());
154    }
155
156    #[test]
157    fn logger_can_be_created_with_verbose_false() {
158        let logger = Logger::new(LoggerOptions { verbose: false });
159        assert!(!logger.is_verbose());
160    }
161
162    #[test]
163    fn logger_options_default_is_not_verbose() {
164        let options = LoggerOptions::default();
165        assert!(!options.verbose);
166    }
167
168    #[test]
169    fn debug_message_fn_not_called_when_not_verbose() {
170        let logger = Logger::new(LoggerOptions { verbose: false });
171        let mut was_called = false;
172
173        logger.debug(|| {
174            was_called = true;
175            "test message".to_string()
176        });
177
178        assert!(!was_called);
179    }
180
181    #[test]
182    fn debug_message_fn_called_when_verbose() {
183        let logger = Logger::new(LoggerOptions { verbose: true });
184        let mut was_called = false;
185
186        logger.debug(|| {
187            was_called = true;
188            "test message".to_string()
189        });
190
191        assert!(was_called);
192    }
193}