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
195pub struct Filter {
196    directives: Vec<Directive>,
197    filter: Option<FilterOp>,
198}
199
200impl Filter {
201    /// Returns the maximum `LevelFilter` that this filter instance is
202    /// configured to output.
203    ///
204    /// # Example
205    ///
206    /// ```rust
207    /// use log::LevelFilter;
208    /// use env_filter::Builder;
209    ///
210    /// let mut builder = Builder::new();
211    /// builder.filter(Some("module1"), LevelFilter::Info);
212    /// builder.filter(Some("module2"), LevelFilter::Error);
213    ///
214    /// let filter = builder.build();
215    /// assert_eq!(filter.filter(), LevelFilter::Info);
216    /// ```
217    pub fn filter(&self) -> LevelFilter {
218        self.directives
219            .iter()
220            .map(|d| d.level)
221            .max()
222            .unwrap_or(LevelFilter::Off)
223    }
224
225    /// Checks if this record matches the configured filter.
226    pub fn matches(&self, record: &Record<'_>) -> bool {
227        if !self.enabled(record.metadata()) {
228            return false;
229        }
230
231        if let Some(filter) = self.filter.as_ref() {
232            if !filter.is_match(&record.args().to_string()) {
233                return false;
234            }
235        }
236
237        true
238    }
239
240    /// Determines if a log message with the specified metadata would be logged.
241    pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
242        let level = metadata.level();
243        let target = metadata.target();
244
245        enabled(&self.directives, level, target)
246    }
247}
248
249impl fmt::Debug for Filter {
250    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251        f.debug_struct("Filter")
252            .field("filter", &self.filter)
253            .field("directives", &self.directives)
254            .finish()
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use log::{Level, LevelFilter};
261    use snapbox::{assert_data_eq, str};
262
263    use super::{enabled, Builder, Directive, Filter};
264
265    fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
266        let mut logger = Builder::new().build();
267        logger.directives = dirs;
268        logger
269    }
270
271    #[test]
272    fn filter_info() {
273        let logger = Builder::new().filter(None, LevelFilter::Info).build();
274        assert!(enabled(&logger.directives, Level::Info, "crate1"));
275        assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
276    }
277
278    #[test]
279    fn filter_beginning_longest_match() {
280        let logger = Builder::new()
281            .filter(Some("crate2"), LevelFilter::Info)
282            .filter(Some("crate2::mod"), LevelFilter::Debug)
283            .filter(Some("crate1::mod1"), LevelFilter::Warn)
284            .build();
285        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
286        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
287    }
288
289    // Some of our tests are only correct or complete when they cover the full
290    // universe of variants for log::Level. In the unlikely event that a new
291    // variant is added in the future, this test will detect the scenario and
292    // alert us to the need to review and update the tests. In such a
293    // situation, this test will fail to compile, and the error message will
294    // look something like this:
295    //
296    //     error[E0004]: non-exhaustive patterns: `NewVariant` not covered
297    //        --> src/filter/mod.rs:413:15
298    //         |
299    //     413 |         match level_universe {
300    //         |               ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
301    #[test]
302    fn ensure_tests_cover_level_universe() {
303        let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
304        match level_universe {
305            Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
306        }
307    }
308
309    #[test]
310    fn parse_default() {
311        let logger = Builder::new().parse("info,crate1::mod1=warn").build();
312        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
313        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
314    }
315
316    #[test]
317    fn parse_default_bare_level_off_lc() {
318        let logger = Builder::new().parse("off").build();
319        assert!(!enabled(&logger.directives, Level::Error, ""));
320        assert!(!enabled(&logger.directives, Level::Warn, ""));
321        assert!(!enabled(&logger.directives, Level::Info, ""));
322        assert!(!enabled(&logger.directives, Level::Debug, ""));
323        assert!(!enabled(&logger.directives, Level::Trace, ""));
324    }
325
326    #[test]
327    fn parse_default_bare_level_off_uc() {
328        let logger = Builder::new().parse("OFF").build();
329        assert!(!enabled(&logger.directives, Level::Error, ""));
330        assert!(!enabled(&logger.directives, Level::Warn, ""));
331        assert!(!enabled(&logger.directives, Level::Info, ""));
332        assert!(!enabled(&logger.directives, Level::Debug, ""));
333        assert!(!enabled(&logger.directives, Level::Trace, ""));
334    }
335
336    #[test]
337    fn parse_default_bare_level_error_lc() {
338        let logger = Builder::new().parse("error").build();
339        assert!(enabled(&logger.directives, Level::Error, ""));
340        assert!(!enabled(&logger.directives, Level::Warn, ""));
341        assert!(!enabled(&logger.directives, Level::Info, ""));
342        assert!(!enabled(&logger.directives, Level::Debug, ""));
343        assert!(!enabled(&logger.directives, Level::Trace, ""));
344    }
345
346    #[test]
347    fn parse_default_bare_level_error_uc() {
348        let logger = Builder::new().parse("ERROR").build();
349        assert!(enabled(&logger.directives, Level::Error, ""));
350        assert!(!enabled(&logger.directives, Level::Warn, ""));
351        assert!(!enabled(&logger.directives, Level::Info, ""));
352        assert!(!enabled(&logger.directives, Level::Debug, ""));
353        assert!(!enabled(&logger.directives, Level::Trace, ""));
354    }
355
356    #[test]
357    fn parse_default_bare_level_warn_lc() {
358        let logger = Builder::new().parse("warn").build();
359        assert!(enabled(&logger.directives, Level::Error, ""));
360        assert!(enabled(&logger.directives, Level::Warn, ""));
361        assert!(!enabled(&logger.directives, Level::Info, ""));
362        assert!(!enabled(&logger.directives, Level::Debug, ""));
363        assert!(!enabled(&logger.directives, Level::Trace, ""));
364    }
365
366    #[test]
367    fn parse_default_bare_level_warn_uc() {
368        let logger = Builder::new().parse("WARN").build();
369        assert!(enabled(&logger.directives, Level::Error, ""));
370        assert!(enabled(&logger.directives, Level::Warn, ""));
371        assert!(!enabled(&logger.directives, Level::Info, ""));
372        assert!(!enabled(&logger.directives, Level::Debug, ""));
373        assert!(!enabled(&logger.directives, Level::Trace, ""));
374    }
375
376    #[test]
377    fn parse_default_bare_level_info_lc() {
378        let logger = Builder::new().parse("info").build();
379        assert!(enabled(&logger.directives, Level::Error, ""));
380        assert!(enabled(&logger.directives, Level::Warn, ""));
381        assert!(enabled(&logger.directives, Level::Info, ""));
382        assert!(!enabled(&logger.directives, Level::Debug, ""));
383        assert!(!enabled(&logger.directives, Level::Trace, ""));
384    }
385
386    #[test]
387    fn parse_default_bare_level_info_uc() {
388        let logger = Builder::new().parse("INFO").build();
389        assert!(enabled(&logger.directives, Level::Error, ""));
390        assert!(enabled(&logger.directives, Level::Warn, ""));
391        assert!(enabled(&logger.directives, Level::Info, ""));
392        assert!(!enabled(&logger.directives, Level::Debug, ""));
393        assert!(!enabled(&logger.directives, Level::Trace, ""));
394    }
395
396    #[test]
397    fn parse_default_bare_level_debug_lc() {
398        let logger = Builder::new().parse("debug").build();
399        assert!(enabled(&logger.directives, Level::Error, ""));
400        assert!(enabled(&logger.directives, Level::Warn, ""));
401        assert!(enabled(&logger.directives, Level::Info, ""));
402        assert!(enabled(&logger.directives, Level::Debug, ""));
403        assert!(!enabled(&logger.directives, Level::Trace, ""));
404    }
405
406    #[test]
407    fn parse_default_bare_level_debug_uc() {
408        let logger = Builder::new().parse("DEBUG").build();
409        assert!(enabled(&logger.directives, Level::Error, ""));
410        assert!(enabled(&logger.directives, Level::Warn, ""));
411        assert!(enabled(&logger.directives, Level::Info, ""));
412        assert!(enabled(&logger.directives, Level::Debug, ""));
413        assert!(!enabled(&logger.directives, Level::Trace, ""));
414    }
415
416    #[test]
417    fn parse_default_bare_level_trace_lc() {
418        let logger = Builder::new().parse("trace").build();
419        assert!(enabled(&logger.directives, Level::Error, ""));
420        assert!(enabled(&logger.directives, Level::Warn, ""));
421        assert!(enabled(&logger.directives, Level::Info, ""));
422        assert!(enabled(&logger.directives, Level::Debug, ""));
423        assert!(enabled(&logger.directives, Level::Trace, ""));
424    }
425
426    #[test]
427    fn parse_default_bare_level_trace_uc() {
428        let logger = Builder::new().parse("TRACE").build();
429        assert!(enabled(&logger.directives, Level::Error, ""));
430        assert!(enabled(&logger.directives, Level::Warn, ""));
431        assert!(enabled(&logger.directives, Level::Info, ""));
432        assert!(enabled(&logger.directives, Level::Debug, ""));
433        assert!(enabled(&logger.directives, Level::Trace, ""));
434    }
435
436    // In practice, the desired log level is typically specified by a token
437    // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
438    // 'TRACE'), but this tests serves as a reminder that
439    // log::Level::from_str() ignores all case variants.
440    #[test]
441    fn parse_default_bare_level_debug_mixed() {
442        {
443            let logger = Builder::new().parse("Debug").build();
444            assert!(enabled(&logger.directives, Level::Error, ""));
445            assert!(enabled(&logger.directives, Level::Warn, ""));
446            assert!(enabled(&logger.directives, Level::Info, ""));
447            assert!(enabled(&logger.directives, Level::Debug, ""));
448            assert!(!enabled(&logger.directives, Level::Trace, ""));
449        }
450        {
451            let logger = Builder::new().parse("debuG").build();
452            assert!(enabled(&logger.directives, Level::Error, ""));
453            assert!(enabled(&logger.directives, Level::Warn, ""));
454            assert!(enabled(&logger.directives, Level::Info, ""));
455            assert!(enabled(&logger.directives, Level::Debug, ""));
456            assert!(!enabled(&logger.directives, Level::Trace, ""));
457        }
458        {
459            let logger = Builder::new().parse("deBug").build();
460            assert!(enabled(&logger.directives, Level::Error, ""));
461            assert!(enabled(&logger.directives, Level::Warn, ""));
462            assert!(enabled(&logger.directives, Level::Info, ""));
463            assert!(enabled(&logger.directives, Level::Debug, ""));
464            assert!(!enabled(&logger.directives, Level::Trace, ""));
465        }
466        {
467            let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
468            assert!(enabled(&logger.directives, Level::Error, ""));
469            assert!(enabled(&logger.directives, Level::Warn, ""));
470            assert!(enabled(&logger.directives, Level::Info, ""));
471            assert!(enabled(&logger.directives, Level::Debug, ""));
472            assert!(!enabled(&logger.directives, Level::Trace, ""));
473        }
474    }
475
476    #[test]
477    fn try_parse_valid_filter() {
478        let logger = Builder::new()
479            .try_parse("info,crate1::mod1=warn")
480            .expect("valid filter returned error")
481            .build();
482        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
483        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
484    }
485
486    #[test]
487    fn try_parse_invalid_filter() {
488        let error = Builder::new().try_parse("info,crate1=invalid").unwrap_err();
489        assert_data_eq!(
490            error,
491            str!["error parsing logger filter: invalid logging spec 'invalid'"]
492        );
493    }
494
495    #[test]
496    fn match_full_path() {
497        let logger = make_logger_filter(vec![
498            Directive {
499                name: Some("crate2".to_owned()),
500                level: LevelFilter::Info,
501            },
502            Directive {
503                name: Some("crate1::mod1".to_owned()),
504                level: LevelFilter::Warn,
505            },
506        ]);
507        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
508        assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
509        assert!(enabled(&logger.directives, Level::Info, "crate2"));
510        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
511    }
512
513    #[test]
514    fn no_match() {
515        let logger = make_logger_filter(vec![
516            Directive {
517                name: Some("crate2".to_owned()),
518                level: LevelFilter::Info,
519            },
520            Directive {
521                name: Some("crate1::mod1".to_owned()),
522                level: LevelFilter::Warn,
523            },
524        ]);
525        assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
526    }
527
528    #[test]
529    fn match_beginning() {
530        let logger = make_logger_filter(vec![
531            Directive {
532                name: Some("crate2".to_owned()),
533                level: LevelFilter::Info,
534            },
535            Directive {
536                name: Some("crate1::mod1".to_owned()),
537                level: LevelFilter::Warn,
538            },
539        ]);
540        assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
541    }
542
543    #[test]
544    fn match_beginning_longest_match() {
545        let logger = make_logger_filter(vec![
546            Directive {
547                name: Some("crate2".to_owned()),
548                level: LevelFilter::Info,
549            },
550            Directive {
551                name: Some("crate2::mod".to_owned()),
552                level: LevelFilter::Debug,
553            },
554            Directive {
555                name: Some("crate1::mod1".to_owned()),
556                level: LevelFilter::Warn,
557            },
558        ]);
559        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
560        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
561    }
562
563    #[test]
564    fn match_default() {
565        let logger = make_logger_filter(vec![
566            Directive {
567                name: None,
568                level: LevelFilter::Info,
569            },
570            Directive {
571                name: Some("crate1::mod1".to_owned()),
572                level: LevelFilter::Warn,
573            },
574        ]);
575        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
576        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
577    }
578
579    #[test]
580    fn zero_level() {
581        let logger = make_logger_filter(vec![
582            Directive {
583                name: None,
584                level: LevelFilter::Info,
585            },
586            Directive {
587                name: Some("crate1::mod1".to_owned()),
588                level: LevelFilter::Off,
589            },
590        ]);
591        assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
592        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
593    }
594}