log4rs/config/
runtime.rs

1//! log4rs configuration
2
3use log::LevelFilter;
4use std::collections::HashSet;
5use thiserror::Error;
6
7use crate::{append::Append, filter::Filter};
8
9/// A log4rs configuration.
10#[derive(Debug)]
11pub struct Config {
12    appenders: Vec<Appender>,
13    root: Root,
14    loggers: Vec<Logger>,
15}
16
17impl Config {
18    /// Creates a new `ConfigBuilder`.
19    pub fn builder() -> ConfigBuilder {
20        ConfigBuilder {
21            appenders: vec![],
22            loggers: vec![],
23        }
24    }
25
26    /// Returns the `Appender`s associated with the `Config`.
27    pub fn appenders(&self) -> &[Appender] {
28        &self.appenders
29    }
30
31    /// Returns the `Root` associated with the `Config`.
32    pub fn root(&self) -> &Root {
33        &self.root
34    }
35
36    /// Returns a mutable handle for the `Root` associated with the `Config`.
37    pub fn root_mut(&mut self) -> &mut Root {
38        &mut self.root
39    }
40
41    /// Returns the `Logger`s associated with the `Config`.
42    pub fn loggers(&self) -> &[Logger] {
43        &self.loggers
44    }
45
46    pub(crate) fn unpack(self) -> (Vec<Appender>, Root, Vec<Logger>) {
47        let Config {
48            appenders,
49            root,
50            loggers,
51        } = self;
52        (appenders, root, loggers)
53    }
54}
55
56/// A builder for `Config`s.
57#[derive(Debug, Default)]
58pub struct ConfigBuilder {
59    appenders: Vec<Appender>,
60    loggers: Vec<Logger>,
61}
62
63impl ConfigBuilder {
64    /// Adds an appender.
65    pub fn appender(mut self, appender: Appender) -> ConfigBuilder {
66        self.appenders.push(appender);
67        self
68    }
69
70    /// Adds appenders.
71    pub fn appenders<I>(mut self, appenders: I) -> ConfigBuilder
72    where
73        I: IntoIterator<Item = Appender>,
74    {
75        self.appenders.extend(appenders);
76        self
77    }
78
79    /// Adds a logger.
80    pub fn logger(mut self, logger: Logger) -> ConfigBuilder {
81        self.loggers.push(logger);
82        self
83    }
84
85    /// Adds loggers.
86    pub fn loggers<I>(mut self, loggers: I) -> ConfigBuilder
87    where
88        I: IntoIterator<Item = Logger>,
89    {
90        self.loggers.extend(loggers);
91        self
92    }
93
94    /// Consumes the `ConfigBuilder`, returning the `Config`.
95    ///
96    /// Unlike `build`, this method will always return a `Config` by stripping
97    /// portions of the configuration that are incorrect.
98    pub fn build_lossy(self, mut root: Root) -> (Config, ConfigErrors) {
99        let mut errors: Vec<ConfigError> = vec![];
100
101        let ConfigBuilder { appenders, loggers } = self;
102
103        let mut ok_appenders = vec![];
104        let mut appender_names = HashSet::new();
105        for appender in appenders {
106            if appender_names.insert(appender.name.clone()) {
107                ok_appenders.push(appender);
108            } else {
109                errors.push(ConfigError::DuplicateAppenderName(appender.name));
110            }
111        }
112
113        let mut ok_root_appenders = vec![];
114        for appender in root.appenders {
115            if appender_names.contains(&appender) {
116                ok_root_appenders.push(appender);
117            } else {
118                errors.push(ConfigError::NonexistentAppender(appender));
119            }
120        }
121        root.appenders = ok_root_appenders;
122
123        let mut ok_loggers = vec![];
124        let mut logger_names = HashSet::new();
125        for mut logger in loggers {
126            if !logger_names.insert(logger.name.clone()) {
127                errors.push(ConfigError::DuplicateLoggerName(logger.name));
128                continue;
129            }
130
131            if let Err(err) = check_logger_name(&logger.name) {
132                errors.push(err);
133                continue;
134            }
135
136            let mut ok_logger_appenders = vec![];
137            for appender in logger.appenders {
138                if appender_names.contains(&appender) {
139                    ok_logger_appenders.push(appender);
140                } else {
141                    errors.push(ConfigError::NonexistentAppender(appender));
142                }
143            }
144            logger.appenders = ok_logger_appenders;
145
146            ok_loggers.push(logger);
147        }
148
149        let config = Config {
150            appenders: ok_appenders,
151            root,
152            loggers: ok_loggers,
153        };
154
155        (config, ConfigErrors(errors))
156    }
157
158    /// Consumes the `ConfigBuilder`, returning the `Config`.
159    pub fn build(self, root: Root) -> Result<Config, ConfigErrors> {
160        let (config, errors) = self.build_lossy(root);
161        if errors.is_empty() {
162            Ok(config)
163        } else {
164            Err(errors)
165        }
166    }
167}
168
169/// Configuration for the root logger.
170#[derive(Debug)]
171pub struct Root {
172    level: LevelFilter,
173    appenders: Vec<String>,
174}
175
176impl Root {
177    /// Creates a new `RootBuilder` with no appenders.
178    pub fn builder() -> RootBuilder {
179        RootBuilder { appenders: vec![] }
180    }
181
182    /// Returns the minimum level of log messages that the root logger will accept.
183    pub fn level(&self) -> LevelFilter {
184        self.level
185    }
186
187    /// Returns the list of names of appenders that will be attached to the root logger.
188    pub fn appenders(&self) -> &[String] {
189        &self.appenders
190    }
191
192    /// Sets the minimum level of log messages that the root logger will accept.
193    pub fn set_level(&mut self, level: LevelFilter) {
194        self.level = level;
195    }
196}
197
198/// A builder for `Root`s.
199#[derive(Clone, Eq, PartialEq, Hash, Debug)]
200pub struct RootBuilder {
201    appenders: Vec<String>,
202}
203
204impl RootBuilder {
205    /// Adds an appender.
206    pub fn appender<T>(mut self, appender: T) -> RootBuilder
207    where
208        T: Into<String>,
209    {
210        self.appenders.push(appender.into());
211        self
212    }
213
214    /// Adds appenders.
215    pub fn appenders<I>(mut self, appenders: I) -> RootBuilder
216    where
217        I: IntoIterator,
218        I::Item: Into<String>,
219    {
220        self.appenders.extend(appenders.into_iter().map(Into::into));
221        self
222    }
223
224    /// Consumes the `RootBuilder`, returning the `Root`.
225    pub fn build(self, level: LevelFilter) -> Root {
226        Root {
227            level,
228            appenders: self.appenders,
229        }
230    }
231}
232
233/// Configuration for an appender.
234#[derive(Debug)]
235pub struct Appender {
236    name: String,
237    appender: Box<dyn Append>,
238    filters: Vec<Box<dyn Filter>>,
239}
240
241impl Appender {
242    /// Creates a new `AppenderBuilder` with the specified name and `Append` trait object.
243    pub fn builder() -> AppenderBuilder {
244        AppenderBuilder { filters: vec![] }
245    }
246
247    /// Returns the name of the appender.
248    pub fn name(&self) -> &str {
249        &self.name
250    }
251
252    /// Returns the appender.
253    pub fn appender(&self) -> &dyn Append {
254        &*self.appender
255    }
256
257    /// Returns the filters attached to the appender.
258    pub fn filters(&self) -> &[Box<dyn Filter>] {
259        &self.filters
260    }
261
262    pub(crate) fn unpack(self) -> (String, Box<dyn Append>, Vec<Box<dyn Filter>>) {
263        let Appender {
264            name,
265            appender,
266            filters,
267        } = self;
268        (name, appender, filters)
269    }
270}
271
272/// A builder for `Appender`s.
273#[derive(Debug)]
274pub struct AppenderBuilder {
275    filters: Vec<Box<dyn Filter>>,
276}
277
278impl AppenderBuilder {
279    /// Adds a filter.
280    pub fn filter(mut self, filter: Box<dyn Filter>) -> AppenderBuilder {
281        self.filters.push(filter);
282        self
283    }
284
285    /// Adds filters.
286    pub fn filters<I>(mut self, filters: I) -> AppenderBuilder
287    where
288        I: IntoIterator<Item = Box<dyn Filter>>,
289    {
290        self.filters.extend(filters);
291        self
292    }
293
294    /// Consumes the `AppenderBuilder`, returning the `Appender`.
295    pub fn build<T>(self, name: T, appender: Box<dyn Append>) -> Appender
296    where
297        T: Into<String>,
298    {
299        Appender {
300            name: name.into(),
301            appender,
302            filters: self.filters,
303        }
304    }
305}
306
307/// Configuration for a logger.
308#[derive(Clone, Eq, PartialEq, Hash, Debug)]
309pub struct Logger {
310    name: String,
311    level: LevelFilter,
312    appenders: Vec<String>,
313    additive: bool,
314}
315
316impl Logger {
317    /// Creates a new `LoggerBuilder` with the specified name and level.
318    ///
319    /// There are initially no appenders attached and `additive` is `true`.
320    pub fn builder() -> LoggerBuilder {
321        LoggerBuilder {
322            appenders: vec![],
323            additive: true,
324        }
325    }
326
327    /// Returns the name of the logger.
328    pub fn name(&self) -> &str {
329        &self.name
330    }
331
332    /// Returns the minimum level of log messages that the logger will accept.
333    pub fn level(&self) -> LevelFilter {
334        self.level
335    }
336
337    /// Returns the list of names of appenders that will be attached to the logger.
338    pub fn appenders(&self) -> &[String] {
339        &self.appenders
340    }
341
342    /// Determines if appenders of parent loggers will also be attached to this logger.
343    pub fn additive(&self) -> bool {
344        self.additive
345    }
346}
347
348/// A builder for `Logger`s.
349#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)]
350pub struct LoggerBuilder {
351    appenders: Vec<String>,
352    additive: bool,
353}
354
355impl LoggerBuilder {
356    /// Adds an appender.
357    pub fn appender<T>(mut self, appender: T) -> LoggerBuilder
358    where
359        T: Into<String>,
360    {
361        self.appenders.push(appender.into());
362        self
363    }
364
365    /// Adds appenders.
366    pub fn appenders<I>(mut self, appenders: I) -> LoggerBuilder
367    where
368        I: IntoIterator,
369        I::Item: Into<String>,
370    {
371        self.appenders.extend(appenders.into_iter().map(Into::into));
372        self
373    }
374
375    /// Sets the additivity of the logger.
376    pub fn additive(mut self, additive: bool) -> LoggerBuilder {
377        self.additive = additive;
378        self
379    }
380
381    /// Consumes the `LoggerBuilder`, returning the `Logger`.
382    pub fn build<T>(self, name: T, level: LevelFilter) -> Logger
383    where
384        T: Into<String>,
385    {
386        Logger {
387            name: name.into(),
388            level,
389            appenders: self.appenders,
390            additive: self.additive,
391        }
392    }
393}
394
395fn check_logger_name(name: &str) -> Result<(), ConfigError> {
396    if name.is_empty() {
397        return Err(ConfigError::InvalidLoggerName(name.to_owned()));
398    }
399
400    let mut streak = 0;
401    for ch in name.chars() {
402        if ch == ':' {
403            streak += 1;
404            if streak > 2 {
405                return Err(ConfigError::InvalidLoggerName(name.to_owned()));
406            }
407        } else {
408            if streak > 0 && streak != 2 {
409                return Err(ConfigError::InvalidLoggerName(name.to_owned()));
410            }
411            streak = 0;
412        }
413    }
414
415    if streak > 0 {
416        Err(ConfigError::InvalidLoggerName(name.to_owned()))
417    } else {
418        Ok(())
419    }
420}
421
422/// Errors encountered when validating a log4rs `Config`.
423#[derive(Debug, Error)]
424#[error("Configuration errors: {0:#?}")]
425pub struct ConfigErrors(Vec<ConfigError>);
426
427impl ConfigErrors {
428    /// There were no config errors.
429    pub fn is_empty(&self) -> bool {
430        self.0.is_empty()
431    }
432    /// Returns a slice of `Error`s.
433    pub fn errors(&self) -> &[ConfigError] {
434        &self.0
435    }
436    /// Handle non-fatal errors (by logging them to stderr.)
437    pub fn handle(&mut self) {
438        for e in self.0.drain(..) {
439            crate::handle_error(&e.into());
440        }
441    }
442}
443
444/// An error validating a log4rs `Config`.
445#[derive(Debug, Error)]
446pub enum ConfigError {
447    /// Multiple appenders were registered with the same name.
448    #[error("Duplicate appender name `{0}`")]
449    DuplicateAppenderName(String),
450
451    /// A reference to a nonexistant appender.
452    #[error("Reference to nonexistent appender: `{0}`")]
453    NonexistentAppender(String),
454
455    /// Multiple loggers were registered with the same name.
456    #[error("Duplicate logger name `{0}`")]
457    DuplicateLoggerName(String),
458
459    /// A logger name was invalid.
460    #[error("Invalid logger name `{0}`")]
461    InvalidLoggerName(String),
462
463    #[doc(hidden)]
464    #[error("Reserved for future use")]
465    __Extensible,
466}
467
468#[cfg(test)]
469mod test {
470    #[test]
471    fn check_logger_name() {
472        let tests = [
473            ("", false),
474            ("asdf", true),
475            ("asdf::jkl", true),
476            ("::", false),
477            ("asdf::jkl::", false),
478            ("asdf:jkl", false),
479            ("asdf:::jkl", false),
480            ("asdf::jkl::", false),
481        ];
482
483        for &(ref name, expected) in &tests {
484            assert!(
485                expected == super::check_logger_name(name).is_ok(),
486                "{}",
487                name
488            );
489        }
490    }
491}