env_filter/
filter.rs

1use std::env;
2use std::fmt;
3use std::mem;
4
5use log::{LevelFilter, Metadata, Record};
6
7use crate::enabled;
8use crate::parse_spec;
9use crate::Directive;
10use crate::FilterOp;
11
12/// A builder for a log filter.
13///
14/// It can be used to parse a set of directives from a string before building
15/// a [`Filter`] instance.
16///
17/// ## Example
18///
19/// ```
20/// # use std::env;
21/// use env_filter::Builder;
22///
23/// let mut builder = Builder::new();
24///
25/// // Parse a logging filter from an environment variable.
26/// if let Ok(rust_log) = env::var("RUST_LOG") {
27///     builder.parse(&rust_log);
28/// }
29///
30/// let filter = builder.build();
31/// ```
32pub struct Builder {
33    directives: Vec<Directive>,
34    filter: Option<FilterOp>,
35    built: bool,
36}
37
38impl Builder {
39    /// Initializes the filter builder with defaults.
40    pub fn new() -> Builder {
41        Builder {
42            directives: Vec::new(),
43            filter: None,
44            built: false,
45        }
46    }
47
48    /// Initializes the filter builder from an environment.
49    pub fn from_env(env: &str) -> Builder {
50        let mut builder = Builder::new();
51
52        if let Ok(s) = env::var(env) {
53            builder.parse(&s);
54        }
55
56        builder
57    }
58
59    /// Insert the directive replacing any directive with the same name.
60    fn insert_directive(&mut self, mut directive: Directive) {
61        if let Some(pos) = self
62            .directives
63            .iter()
64            .position(|d| d.name == directive.name)
65        {
66            mem::swap(&mut self.directives[pos], &mut directive);
67        } else {
68            self.directives.push(directive);
69        }
70    }
71
72    /// Adds a directive to the filter for a specific module.
73    pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
74        self.filter(Some(module), level)
75    }
76
77    /// Adds a directive to the filter for all modules.
78    pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
79        self.filter(None, level)
80    }
81
82    /// Adds a directive to the filter.
83    ///
84    /// The given module (if any) will log at most the specified level provided.
85    /// If no module is provided then the filter will apply to all log messages.
86    pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
87        self.insert_directive(Directive {
88            name: module.map(|s| s.to_string()),
89            level,
90        });
91        self
92    }
93
94    /// Parses the directives string.
95    ///
96    /// See the [Enabling Logging] section for more details.
97    ///
98    /// [Enabling Logging]: ../index.html#enabling-logging
99    pub fn parse(&mut self, filters: &str) -> &mut Self {
100        let (directives, filter) = parse_spec(filters);
101
102        self.filter = filter;
103
104        for directive in directives {
105            self.insert_directive(directive);
106        }
107        self
108    }
109
110    /// Build a log filter.
111    pub fn build(&mut self) -> Filter {
112        assert!(!self.built, "attempt to re-use consumed builder");
113        self.built = true;
114
115        let mut directives = Vec::new();
116        if self.directives.is_empty() {
117            // Adds the default filter if none exist
118            directives.push(Directive {
119                name: None,
120                level: LevelFilter::Error,
121            });
122        } else {
123            // Consume directives.
124            directives = mem::take(&mut self.directives);
125            // Sort the directives by length of their name, this allows a
126            // little more efficient lookup at runtime.
127            directives.sort_by(|a, b| {
128                let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
129                let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
130                alen.cmp(&blen)
131            });
132        }
133
134        Filter {
135            directives: mem::take(&mut directives),
136            filter: mem::take(&mut self.filter),
137        }
138    }
139}
140
141impl Default for Builder {
142    fn default() -> Self {
143        Builder::new()
144    }
145}
146
147impl fmt::Debug for Builder {
148    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
149        if self.built {
150            f.debug_struct("Filter").field("built", &true).finish()
151        } else {
152            f.debug_struct("Filter")
153                .field("filter", &self.filter)
154                .field("directives", &self.directives)
155                .finish()
156        }
157    }
158}
159
160/// A log filter.
161///
162/// This struct can be used to determine whether or not a log record
163/// should be written to the output.
164/// Use the [`Builder`] type to parse and construct a `Filter`.
165///
166/// [`Builder`]: struct.Builder.html
167pub struct Filter {
168    directives: Vec<Directive>,
169    filter: Option<FilterOp>,
170}
171
172impl Filter {
173    /// Returns the maximum `LevelFilter` that this filter instance is
174    /// configured to output.
175    ///
176    /// # Example
177    ///
178    /// ```rust
179    /// use log::LevelFilter;
180    /// use env_filter::Builder;
181    ///
182    /// let mut builder = Builder::new();
183    /// builder.filter(Some("module1"), LevelFilter::Info);
184    /// builder.filter(Some("module2"), LevelFilter::Error);
185    ///
186    /// let filter = builder.build();
187    /// assert_eq!(filter.filter(), LevelFilter::Info);
188    /// ```
189    pub fn filter(&self) -> LevelFilter {
190        self.directives
191            .iter()
192            .map(|d| d.level)
193            .max()
194            .unwrap_or(LevelFilter::Off)
195    }
196
197    /// Checks if this record matches the configured filter.
198    pub fn matches(&self, record: &Record) -> bool {
199        if !self.enabled(record.metadata()) {
200            return false;
201        }
202
203        if let Some(filter) = self.filter.as_ref() {
204            if !filter.is_match(&record.args().to_string()) {
205                return false;
206            }
207        }
208
209        true
210    }
211
212    /// Determines if a log message with the specified metadata would be logged.
213    pub fn enabled(&self, metadata: &Metadata) -> bool {
214        let level = metadata.level();
215        let target = metadata.target();
216
217        enabled(&self.directives, level, target)
218    }
219}
220
221impl fmt::Debug for Filter {
222    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
223        f.debug_struct("Filter")
224            .field("filter", &self.filter)
225            .field("directives", &self.directives)
226            .finish()
227    }
228}
229
230#[cfg(test)]
231mod tests {
232    use log::{Level, LevelFilter};
233
234    use super::{enabled, Builder, Directive, Filter};
235
236    fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
237        let mut logger = Builder::new().build();
238        logger.directives = dirs;
239        logger
240    }
241
242    #[test]
243    fn filter_info() {
244        let logger = Builder::new().filter(None, LevelFilter::Info).build();
245        assert!(enabled(&logger.directives, Level::Info, "crate1"));
246        assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
247    }
248
249    #[test]
250    fn filter_beginning_longest_match() {
251        let logger = Builder::new()
252            .filter(Some("crate2"), LevelFilter::Info)
253            .filter(Some("crate2::mod"), LevelFilter::Debug)
254            .filter(Some("crate1::mod1"), LevelFilter::Warn)
255            .build();
256        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
257        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
258    }
259
260    // Some of our tests are only correct or complete when they cover the full
261    // universe of variants for log::Level. In the unlikely event that a new
262    // variant is added in the future, this test will detect the scenario and
263    // alert us to the need to review and update the tests. In such a
264    // situation, this test will fail to compile, and the error message will
265    // look something like this:
266    //
267    //     error[E0004]: non-exhaustive patterns: `NewVariant` not covered
268    //        --> src/filter/mod.rs:413:15
269    //         |
270    //     413 |         match level_universe {
271    //         |               ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
272    #[test]
273    fn ensure_tests_cover_level_universe() {
274        let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
275        match level_universe {
276            Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
277        }
278    }
279
280    #[test]
281    fn parse_default() {
282        let logger = Builder::new().parse("info,crate1::mod1=warn").build();
283        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
284        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
285    }
286
287    #[test]
288    fn parse_default_bare_level_off_lc() {
289        let logger = Builder::new().parse("off").build();
290        assert!(!enabled(&logger.directives, Level::Error, ""));
291        assert!(!enabled(&logger.directives, Level::Warn, ""));
292        assert!(!enabled(&logger.directives, Level::Info, ""));
293        assert!(!enabled(&logger.directives, Level::Debug, ""));
294        assert!(!enabled(&logger.directives, Level::Trace, ""));
295    }
296
297    #[test]
298    fn parse_default_bare_level_off_uc() {
299        let logger = Builder::new().parse("OFF").build();
300        assert!(!enabled(&logger.directives, Level::Error, ""));
301        assert!(!enabled(&logger.directives, Level::Warn, ""));
302        assert!(!enabled(&logger.directives, Level::Info, ""));
303        assert!(!enabled(&logger.directives, Level::Debug, ""));
304        assert!(!enabled(&logger.directives, Level::Trace, ""));
305    }
306
307    #[test]
308    fn parse_default_bare_level_error_lc() {
309        let logger = Builder::new().parse("error").build();
310        assert!(enabled(&logger.directives, Level::Error, ""));
311        assert!(!enabled(&logger.directives, Level::Warn, ""));
312        assert!(!enabled(&logger.directives, Level::Info, ""));
313        assert!(!enabled(&logger.directives, Level::Debug, ""));
314        assert!(!enabled(&logger.directives, Level::Trace, ""));
315    }
316
317    #[test]
318    fn parse_default_bare_level_error_uc() {
319        let logger = Builder::new().parse("ERROR").build();
320        assert!(enabled(&logger.directives, Level::Error, ""));
321        assert!(!enabled(&logger.directives, Level::Warn, ""));
322        assert!(!enabled(&logger.directives, Level::Info, ""));
323        assert!(!enabled(&logger.directives, Level::Debug, ""));
324        assert!(!enabled(&logger.directives, Level::Trace, ""));
325    }
326
327    #[test]
328    fn parse_default_bare_level_warn_lc() {
329        let logger = Builder::new().parse("warn").build();
330        assert!(enabled(&logger.directives, Level::Error, ""));
331        assert!(enabled(&logger.directives, Level::Warn, ""));
332        assert!(!enabled(&logger.directives, Level::Info, ""));
333        assert!(!enabled(&logger.directives, Level::Debug, ""));
334        assert!(!enabled(&logger.directives, Level::Trace, ""));
335    }
336
337    #[test]
338    fn parse_default_bare_level_warn_uc() {
339        let logger = Builder::new().parse("WARN").build();
340        assert!(enabled(&logger.directives, Level::Error, ""));
341        assert!(enabled(&logger.directives, Level::Warn, ""));
342        assert!(!enabled(&logger.directives, Level::Info, ""));
343        assert!(!enabled(&logger.directives, Level::Debug, ""));
344        assert!(!enabled(&logger.directives, Level::Trace, ""));
345    }
346
347    #[test]
348    fn parse_default_bare_level_info_lc() {
349        let logger = Builder::new().parse("info").build();
350        assert!(enabled(&logger.directives, Level::Error, ""));
351        assert!(enabled(&logger.directives, Level::Warn, ""));
352        assert!(enabled(&logger.directives, Level::Info, ""));
353        assert!(!enabled(&logger.directives, Level::Debug, ""));
354        assert!(!enabled(&logger.directives, Level::Trace, ""));
355    }
356
357    #[test]
358    fn parse_default_bare_level_info_uc() {
359        let logger = Builder::new().parse("INFO").build();
360        assert!(enabled(&logger.directives, Level::Error, ""));
361        assert!(enabled(&logger.directives, Level::Warn, ""));
362        assert!(enabled(&logger.directives, Level::Info, ""));
363        assert!(!enabled(&logger.directives, Level::Debug, ""));
364        assert!(!enabled(&logger.directives, Level::Trace, ""));
365    }
366
367    #[test]
368    fn parse_default_bare_level_debug_lc() {
369        let logger = Builder::new().parse("debug").build();
370        assert!(enabled(&logger.directives, Level::Error, ""));
371        assert!(enabled(&logger.directives, Level::Warn, ""));
372        assert!(enabled(&logger.directives, Level::Info, ""));
373        assert!(enabled(&logger.directives, Level::Debug, ""));
374        assert!(!enabled(&logger.directives, Level::Trace, ""));
375    }
376
377    #[test]
378    fn parse_default_bare_level_debug_uc() {
379        let logger = Builder::new().parse("DEBUG").build();
380        assert!(enabled(&logger.directives, Level::Error, ""));
381        assert!(enabled(&logger.directives, Level::Warn, ""));
382        assert!(enabled(&logger.directives, Level::Info, ""));
383        assert!(enabled(&logger.directives, Level::Debug, ""));
384        assert!(!enabled(&logger.directives, Level::Trace, ""));
385    }
386
387    #[test]
388    fn parse_default_bare_level_trace_lc() {
389        let logger = Builder::new().parse("trace").build();
390        assert!(enabled(&logger.directives, Level::Error, ""));
391        assert!(enabled(&logger.directives, Level::Warn, ""));
392        assert!(enabled(&logger.directives, Level::Info, ""));
393        assert!(enabled(&logger.directives, Level::Debug, ""));
394        assert!(enabled(&logger.directives, Level::Trace, ""));
395    }
396
397    #[test]
398    fn parse_default_bare_level_trace_uc() {
399        let logger = Builder::new().parse("TRACE").build();
400        assert!(enabled(&logger.directives, Level::Error, ""));
401        assert!(enabled(&logger.directives, Level::Warn, ""));
402        assert!(enabled(&logger.directives, Level::Info, ""));
403        assert!(enabled(&logger.directives, Level::Debug, ""));
404        assert!(enabled(&logger.directives, Level::Trace, ""));
405    }
406
407    // In practice, the desired log level is typically specified by a token
408    // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
409    // 'TRACE'), but this tests serves as a reminder that
410    // log::Level::from_str() ignores all case variants.
411    #[test]
412    fn parse_default_bare_level_debug_mixed() {
413        {
414            let logger = Builder::new().parse("Debug").build();
415            assert!(enabled(&logger.directives, Level::Error, ""));
416            assert!(enabled(&logger.directives, Level::Warn, ""));
417            assert!(enabled(&logger.directives, Level::Info, ""));
418            assert!(enabled(&logger.directives, Level::Debug, ""));
419            assert!(!enabled(&logger.directives, Level::Trace, ""));
420        }
421        {
422            let logger = Builder::new().parse("debuG").build();
423            assert!(enabled(&logger.directives, Level::Error, ""));
424            assert!(enabled(&logger.directives, Level::Warn, ""));
425            assert!(enabled(&logger.directives, Level::Info, ""));
426            assert!(enabled(&logger.directives, Level::Debug, ""));
427            assert!(!enabled(&logger.directives, Level::Trace, ""));
428        }
429        {
430            let logger = Builder::new().parse("deBug").build();
431            assert!(enabled(&logger.directives, Level::Error, ""));
432            assert!(enabled(&logger.directives, Level::Warn, ""));
433            assert!(enabled(&logger.directives, Level::Info, ""));
434            assert!(enabled(&logger.directives, Level::Debug, ""));
435            assert!(!enabled(&logger.directives, Level::Trace, ""));
436        }
437        {
438            let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
439            assert!(enabled(&logger.directives, Level::Error, ""));
440            assert!(enabled(&logger.directives, Level::Warn, ""));
441            assert!(enabled(&logger.directives, Level::Info, ""));
442            assert!(enabled(&logger.directives, Level::Debug, ""));
443            assert!(!enabled(&logger.directives, Level::Trace, ""));
444        }
445    }
446
447    #[test]
448    fn match_full_path() {
449        let logger = make_logger_filter(vec![
450            Directive {
451                name: Some("crate2".to_string()),
452                level: LevelFilter::Info,
453            },
454            Directive {
455                name: Some("crate1::mod1".to_string()),
456                level: LevelFilter::Warn,
457            },
458        ]);
459        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
460        assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
461        assert!(enabled(&logger.directives, Level::Info, "crate2"));
462        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
463    }
464
465    #[test]
466    fn no_match() {
467        let logger = make_logger_filter(vec![
468            Directive {
469                name: Some("crate2".to_string()),
470                level: LevelFilter::Info,
471            },
472            Directive {
473                name: Some("crate1::mod1".to_string()),
474                level: LevelFilter::Warn,
475            },
476        ]);
477        assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
478    }
479
480    #[test]
481    fn match_beginning() {
482        let logger = make_logger_filter(vec![
483            Directive {
484                name: Some("crate2".to_string()),
485                level: LevelFilter::Info,
486            },
487            Directive {
488                name: Some("crate1::mod1".to_string()),
489                level: LevelFilter::Warn,
490            },
491        ]);
492        assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
493    }
494
495    #[test]
496    fn match_beginning_longest_match() {
497        let logger = make_logger_filter(vec![
498            Directive {
499                name: Some("crate2".to_string()),
500                level: LevelFilter::Info,
501            },
502            Directive {
503                name: Some("crate2::mod".to_string()),
504                level: LevelFilter::Debug,
505            },
506            Directive {
507                name: Some("crate1::mod1".to_string()),
508                level: LevelFilter::Warn,
509            },
510        ]);
511        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
512        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
513    }
514
515    #[test]
516    fn match_default() {
517        let logger = make_logger_filter(vec![
518            Directive {
519                name: None,
520                level: LevelFilter::Info,
521            },
522            Directive {
523                name: Some("crate1::mod1".to_string()),
524                level: LevelFilter::Warn,
525            },
526        ]);
527        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
528        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
529    }
530
531    #[test]
532    fn zero_level() {
533        let logger = make_logger_filter(vec![
534            Directive {
535                name: None,
536                level: LevelFilter::Info,
537            },
538            Directive {
539                name: Some("crate1::mod1".to_string()),
540                level: LevelFilter::Off,
541            },
542        ]);
543        assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
544        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
545    }
546}