godot_logger/
builder.rs

1use log::{Level, LevelFilter, SetLoggerError};
2use log4rs::config::{Appender, Logger, Root};
3use log4rs::Config;
4
5use crate::appender::GodotAppender;
6use crate::filter::Filter;
7
8const APPENDER_NAME: &str = "godot-logger";
9
10/// A `Builder` that configures and initializes the Godot logger
11///
12/// [godot-logger] implements the builder pattern as the primary interface to configure and
13/// initialize the logger. The configuration has sensible defaults that can be overwritten by
14/// calling the corresponding setters on the `Builder` struct. Once the configuration is done, the
15/// logger can be initialized by calling the `build` method.
16///
17/// # Examples
18///
19/// ```
20/// use log::Level;
21/// use godot_logger::GodotLogger;
22///
23/// GodotLogger::builder()
24///     .default_log_level(Level::Debug)
25///     .init();
26/// ```
27///
28/// [godot-logger]: https://crates.io/crates/godot-logger
29#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
30pub struct Builder {
31    default_log_level: Level,
32    filters: Vec<Filter>,
33}
34
35impl Builder {
36    /// Sets the default log level
37    ///
38    /// `GodotLogger` matches all log records against a default log level. By default, only warnings
39    /// and errors are logged.
40    ///
41    /// # Examples
42    ///
43    /// ```
44    /// use log::Level;
45    /// use godot_logger::GodotLogger;
46    ///
47    /// let mut builder = GodotLogger::builder();
48    /// builder = builder.default_log_level(Level::Debug);
49    /// ```
50    pub fn default_log_level(mut self, default_log_level: Level) -> Self {
51        self.default_log_level = default_log_level;
52        self
53    }
54
55    /// Adds a filter
56    ///
57    /// Filters override the default log level for specific Rust modules.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use godot_logger::GodotLogger;
63    /// use log::LevelFilter;
64    ///
65    /// GodotLogger::builder().add_filter("godot_logger", LevelFilter::Off);
66    /// ```
67    pub fn add_filter(mut self, module: &'static str, level: LevelFilter) -> Self {
68        self.filters.push(Filter::new(module, level));
69        self
70    }
71
72    /// Initializes the logger
73    ///
74    /// This method consumes the builder and initializes the logger with the current configuration
75    /// of the builder. After calling this method, log records will be written to Godot's output
76    /// console.
77    ///
78    /// # Examples
79    ///
80    /// ```
81    /// use log::Level;
82    /// use godot_logger::GodotLogger;
83    ///
84    /// GodotLogger::builder().init();
85    /// ```
86    pub fn init(self) -> Result<(), SetLoggerError> {
87        let loggers: Vec<Logger> = self
88            .filters
89            .iter()
90            .map(|filter| {
91                Logger::builder()
92                    .appender(APPENDER_NAME)
93                    .build(filter.module(), filter.level())
94            })
95            .collect();
96
97        let config = Config::builder()
98            .appender(Appender::builder().build(APPENDER_NAME, Box::new(GodotAppender)))
99            .loggers(loggers)
100            .build(
101                Root::builder()
102                    .appender(APPENDER_NAME)
103                    .build(self.default_log_level.to_level_filter()),
104            )
105            .unwrap();
106
107        let _handle = log4rs::init_config(config)?;
108        Ok(())
109    }
110}
111
112impl Default for Builder {
113    fn default() -> Self {
114        Self {
115            default_log_level: Level::Warn,
116            filters: Vec::new(),
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use log::{Level, LevelFilter};
124
125    use super::Builder;
126
127    #[test]
128    fn default_log_level() {
129        let mut builder = Builder::default();
130
131        builder = builder.default_log_level(Level::Debug);
132
133        assert!(matches!(builder.default_log_level, Level::Debug));
134    }
135
136    #[test]
137    fn add_filter() {
138        let mut builder = Builder::default();
139
140        builder = builder.add_filter("godot_logger::builder", LevelFilter::Off);
141
142        assert_eq!(builder.filters.len(), 1);
143    }
144
145    #[test]
146    fn trait_default() {
147        let builder = Builder::default();
148        assert!(matches!(builder.default_log_level, Level::Warn));
149    }
150
151    #[test]
152    fn trait_send() {
153        fn assert_send<T: Send>() {}
154        assert_send::<Builder>();
155    }
156
157    #[test]
158    fn trait_sync() {
159        fn assert_sync<T: Sync>() {}
160        assert_sync::<Builder>();
161    }
162}