Skip to main content

env_filter/
filter.rs

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