rust_loguru/
config.rs

1use crate::handler::HandlerRef;
2use crate::level::LogLevel;
3use std::fs;
4use std::io;
5use std::path::PathBuf;
6use toml;
7
8/// Configuration for the logger.
9#[derive(Debug)]
10pub struct LoggerConfig {
11    /// The minimum log level to record.
12    pub level: LogLevel,
13    /// The handlers to use for logging.
14    pub handlers: Vec<HandlerRef>,
15    /// Whether to capture source location information.
16    pub capture_source: bool,
17    /// Whether to use colors in console output.
18    pub use_colors: bool,
19    /// The format string for log messages.
20    pub format: String,
21}
22
23impl Clone for LoggerConfig {
24    fn clone(&self) -> Self {
25        Self {
26            level: self.level,
27            handlers: self.handlers.clone(),
28            capture_source: self.capture_source,
29            use_colors: self.use_colors,
30            format: self.format.clone(),
31        }
32    }
33}
34
35impl Default for LoggerConfig {
36    fn default() -> Self {
37        Self {
38            level: LogLevel::Info,
39            handlers: Vec::new(),
40            capture_source: true,
41            use_colors: true,
42            format: "{time} {level} {message}".to_string(),
43        }
44    }
45}
46
47/// Builder for creating logger configurations.
48#[derive(Debug)]
49pub struct LoggerConfigBuilder {
50    config: LoggerConfig,
51}
52
53impl Default for LoggerConfigBuilder {
54    fn default() -> Self {
55        Self::new()
56    }
57}
58
59impl LoggerConfigBuilder {
60    /// Create a new builder with default configuration.
61    pub fn new() -> Self {
62        Self {
63            config: LoggerConfig::default(),
64        }
65    }
66
67    /// Set the minimum log level.
68    pub fn level(mut self, level: LogLevel) -> Self {
69        self.config.level = level;
70        self
71    }
72
73    /// Add a handler to the configuration.
74    pub fn add_handler(mut self, handler: HandlerRef) -> Self {
75        self.config.handlers.push(handler);
76        self
77    }
78
79    /// Set whether to capture source location information.
80    pub fn capture_source(mut self, capture: bool) -> Self {
81        self.config.capture_source = capture;
82        self
83    }
84
85    /// Set whether to use colors in console output.
86    pub fn use_colors(mut self, use_colors: bool) -> Self {
87        self.config.use_colors = use_colors;
88        self
89    }
90
91    /// Set the format string for log messages.
92    pub fn format(mut self, format: String) -> Self {
93        self.config.format = format;
94        self
95    }
96
97    /// Apply environment variable overrides to the configuration.
98    ///
99    /// Supported environment variables:
100    /// - LOGURU_LEVEL (e.g., "Debug", "Info", "Warn", "Error")
101    /// - LOGURU_CAPTURE_SOURCE ("true"/"false")
102    /// - LOGURU_USE_COLORS ("true"/"false")
103    /// - LOGURU_FORMAT (format string)
104    pub fn with_env_overrides(mut self) -> Self {
105        use std::env;
106        if let Ok(level) = env::var("LOGURU_LEVEL") {
107            if let Ok(parsed) = level.parse::<LogLevel>() {
108                self.config.level = parsed;
109            } else {
110                // Accept lowercase too
111                let level_up = level.to_ascii_uppercase();
112                if let Ok(parsed) = level_up.parse::<LogLevel>() {
113                    self.config.level = parsed;
114                }
115            }
116        }
117        if let Ok(capture_source) = env::var("LOGURU_CAPTURE_SOURCE") {
118            self.config.capture_source =
119                matches!(capture_source.as_str(), "1" | "true" | "TRUE" | "True");
120        }
121        if let Ok(use_colors) = env::var("LOGURU_USE_COLORS") {
122            self.config.use_colors = matches!(use_colors.as_str(), "1" | "true" | "TRUE" | "True");
123        }
124        if let Ok(format) = env::var("LOGURU_FORMAT") {
125            self.config.format = format;
126        }
127        self
128    }
129
130    /// Build the final configuration.
131    pub fn build(self) -> LoggerConfig {
132        self.config
133    }
134
135    /// Load configuration from a TOML string. Builder/env overrides take precedence.
136    ///
137    /// Supported TOML keys:
138    /// - level (e.g., "Debug", "Info", "Warn", "Error")
139    /// - capture_source (bool)
140    /// - use_colors (bool)
141    /// - format (string)
142    pub fn from_toml_str(mut self, toml_str: &str) -> Result<Self, toml::de::Error> {
143        let toml_cfg: LoggerConfigToml = toml::from_str(toml_str)?;
144        if let Some(level) = toml_cfg.level {
145            if let Ok(parsed) = level.parse::<LogLevel>() {
146                self.config.level = parsed;
147            }
148        }
149        if let Some(capture_source) = toml_cfg.capture_source {
150            self.config.capture_source = capture_source;
151        }
152        if let Some(use_colors) = toml_cfg.use_colors {
153            self.config.use_colors = use_colors;
154        }
155        if let Some(format) = toml_cfg.format {
156            self.config.format = format;
157        }
158        Ok(self)
159    }
160
161    /// Load configuration from a TOML file. Builder/env overrides take precedence.
162    pub fn from_toml_file<P: AsRef<std::path::Path>>(self, path: P) -> Result<Self, io::Error> {
163        let content = fs::read_to_string(path)?;
164        // Propagate TOML parse errors as io::Error
165        self.from_toml_str(&content)
166            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
167    }
168}
169
170/// Helper functions for creating common configurations.
171impl LoggerConfig {
172    /// Create a basic console configuration.
173    pub fn basic_console() -> LoggerConfig {
174        LoggerConfigBuilder::new()
175            .level(LogLevel::Info)
176            .use_colors(true)
177            .build()
178    }
179
180    /// Create a file logging configuration.
181    pub fn file_logging(_path: PathBuf) -> LoggerConfig {
182        LoggerConfigBuilder::new()
183            .level(LogLevel::Info)
184            .use_colors(false)
185            .build()
186    }
187
188    /// Create a development configuration with detailed logging.
189    pub fn development() -> LoggerConfig {
190        LoggerConfigBuilder::new()
191            .level(LogLevel::Debug)
192            .use_colors(true)
193            .capture_source(true)
194            .format("{time} {level} {file}:{line} {message}".to_string())
195            .build()
196    }
197
198    /// Create a production configuration with minimal logging.
199    pub fn production() -> LoggerConfig {
200        LoggerConfigBuilder::new()
201            .level(LogLevel::Info)
202            .use_colors(false)
203            .capture_source(false)
204            .format("{time} {level} {message}".to_string())
205            .build()
206    }
207}
208
209#[derive(serde::Deserialize, Debug, Default)]
210struct LoggerConfigToml {
211    level: Option<String>,
212    capture_source: Option<bool>,
213    use_colors: Option<bool>,
214    format: Option<String>,
215}