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