env_logger/filter/
mod.rs

1//! Filtering for log records.
2//!
3//! This module contains the log filtering used by `env_logger` to match records.
4//! You can use the `Filter` type in your own logger implementation to use the same
5//! filter parsing and matching as `env_logger`. For more details about the format
6//! for directive strings see [Enabling Logging].
7//!
8//! ## Using `env_logger` in your own logger
9//!
10//! You can use `env_logger`'s filtering functionality with your own logger.
11//! Call [`Builder::parse`] to parse directives from a string when constructing
12//! your logger. Call [`Filter::matches`] to check whether a record should be
13//! logged based on the parsed filters when log records are received.
14//!
15//! ```
16//! extern crate log;
17//! extern crate env_logger;
18//! use env_logger::filter::Filter;
19//! use log::{Log, Metadata, Record};
20//!
21//! struct MyLogger {
22//!     filter: Filter
23//! }
24//!
25//! impl MyLogger {
26//!     fn new() -> MyLogger {
27//!         use env_logger::filter::Builder;
28//!         let mut builder = Builder::new();
29//!
30//!         // Parse a directives string from an environment variable
31//!         if let Ok(ref filter) = std::env::var("MY_LOG_LEVEL") {
32//!            builder.parse(filter);
33//!         }
34//!
35//!         MyLogger {
36//!             filter: builder.build()
37//!         }
38//!     }
39//! }
40//!
41//! impl Log for MyLogger {
42//!     fn enabled(&self, metadata: &Metadata) -> bool {
43//!         self.filter.enabled(metadata)
44//!     }
45//!
46//!     fn log(&self, record: &Record) {
47//!         // Check if the record is matched by the filter
48//!         if self.filter.matches(record) {
49//!             println!("{:?}", record);
50//!         }
51//!     }
52//!
53//!     fn flush(&self) {}
54//! }
55//! # fn main() {}
56//! ```
57//!
58//! [Enabling Logging]: ../index.html#enabling-logging
59//! [`Builder::parse`]: struct.Builder.html#method.parse
60//! [`Filter::matches`]: struct.Filter.html#method.matches
61
62use log::{Level, LevelFilter, Metadata, Record};
63use std::env;
64use std::fmt;
65use std::mem;
66
67#[cfg(feature = "regex")]
68#[path = "regex.rs"]
69mod inner;
70
71#[cfg(not(feature = "regex"))]
72#[path = "string.rs"]
73mod inner;
74
75/// A log filter.
76///
77/// This struct can be used to determine whether or not a log record
78/// should be written to the output.
79/// Use the [`Builder`] type to parse and construct a `Filter`.
80///
81/// [`Builder`]: struct.Builder.html
82pub struct Filter {
83    directives: Vec<Directive>,
84    filter: Option<inner::Filter>,
85}
86
87/// A builder for a log filter.
88///
89/// It can be used to parse a set of directives from a string before building
90/// a [`Filter`] instance.
91///
92/// ## Example
93///
94/// ```
95/// #[macro_use]
96/// extern crate log;
97/// extern crate env_logger;
98///
99/// use std::env;
100/// use std::io;
101/// use env_logger::filter::Builder;
102///
103/// fn main() {
104///     let mut builder = Builder::new();
105///
106///     // Parse a logging filter from an environment variable.
107///     if let Ok(rust_log) = env::var("RUST_LOG") {
108///        builder.parse(&rust_log);
109///     }
110///
111///     let filter = builder.build();
112/// }
113/// ```
114///
115/// [`Filter`]: struct.Filter.html
116pub struct Builder {
117    directives: Vec<Directive>,
118    filter: Option<inner::Filter>,
119    built: bool,
120}
121
122#[derive(Debug)]
123struct Directive {
124    name: Option<String>,
125    level: LevelFilter,
126}
127
128impl Filter {
129    /// Returns the maximum `LevelFilter` that this filter instance is
130    /// configured to output.
131    ///
132    /// # Example
133    ///
134    /// ```rust
135    /// extern crate log;
136    /// extern crate env_logger;
137    ///
138    /// use log::LevelFilter;
139    /// use env_logger::filter::Builder;
140    ///
141    /// fn main() {
142    ///     let mut builder = Builder::new();
143    ///     builder.filter(Some("module1"), LevelFilter::Info);
144    ///     builder.filter(Some("module2"), LevelFilter::Error);
145    ///
146    ///     let filter = builder.build();
147    ///     assert_eq!(filter.filter(), LevelFilter::Info);
148    /// }
149    /// ```
150    pub fn filter(&self) -> LevelFilter {
151        self.directives
152            .iter()
153            .map(|d| d.level)
154            .max()
155            .unwrap_or(LevelFilter::Off)
156    }
157
158    /// Checks if this record matches the configured filter.
159    pub fn matches(&self, record: &Record) -> bool {
160        if !self.enabled(record.metadata()) {
161            return false;
162        }
163
164        if let Some(filter) = self.filter.as_ref() {
165            if !filter.is_match(&*record.args().to_string()) {
166                return false;
167            }
168        }
169
170        true
171    }
172
173    /// Determines if a log message with the specified metadata would be logged.
174    pub fn enabled(&self, metadata: &Metadata) -> bool {
175        let level = metadata.level();
176        let target = metadata.target();
177
178        enabled(&self.directives, level, target)
179    }
180}
181
182impl Builder {
183    /// Initializes the filter builder with defaults.
184    pub fn new() -> Builder {
185        Builder {
186            directives: Vec::new(),
187            filter: None,
188            built: false,
189        }
190    }
191
192    /// Initializes the filter builder from an environment.
193    pub fn from_env(env: &str) -> Builder {
194        let mut builder = Builder::new();
195
196        if let Ok(s) = env::var(env) {
197            builder.parse(&s);
198        }
199
200        builder
201    }
202
203    /// Adds a directive to the filter for a specific module.
204    pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
205        self.filter(Some(module), level)
206    }
207
208    /// Adds a directive to the filter for all modules.
209    pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
210        self.filter(None, level)
211    }
212
213    /// Adds a directive to the filter.
214    ///
215    /// The given module (if any) will log at most the specified level provided.
216    /// If no module is provided then the filter will apply to all log messages.
217    pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
218        self.directives.push(Directive {
219            name: module.map(|s| s.to_string()),
220            level,
221        });
222        self
223    }
224
225    /// Parses the directives string.
226    ///
227    /// See the [Enabling Logging] section for more details.
228    ///
229    /// [Enabling Logging]: ../index.html#enabling-logging
230    pub fn parse(&mut self, filters: &str) -> &mut Self {
231        let (directives, filter) = parse_spec(filters);
232
233        self.filter = filter;
234
235        for directive in directives {
236            self.directives.push(directive);
237        }
238        self
239    }
240
241    /// Build a log filter.
242    pub fn build(&mut self) -> Filter {
243        assert!(!self.built, "attempt to re-use consumed builder");
244        self.built = true;
245
246        if self.directives.is_empty() {
247            // Adds the default filter if none exist
248            self.directives.push(Directive {
249                name: None,
250                level: LevelFilter::Error,
251            });
252        } else {
253            // Sort the directives by length of their name, this allows a
254            // little more efficient lookup at runtime.
255            self.directives.sort_by(|a, b| {
256                let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
257                let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
258                alen.cmp(&blen)
259            });
260        }
261
262        Filter {
263            directives: mem::replace(&mut self.directives, Vec::new()),
264            filter: mem::replace(&mut self.filter, None),
265        }
266    }
267}
268
269impl Default for Builder {
270    fn default() -> Self {
271        Builder::new()
272    }
273}
274
275impl fmt::Debug for Filter {
276    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
277        f.debug_struct("Filter")
278            .field("filter", &self.filter)
279            .field("directives", &self.directives)
280            .finish()
281    }
282}
283
284impl fmt::Debug for Builder {
285    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
286        if self.built {
287            f.debug_struct("Filter").field("built", &true).finish()
288        } else {
289            f.debug_struct("Filter")
290                .field("filter", &self.filter)
291                .field("directives", &self.directives)
292                .finish()
293        }
294    }
295}
296
297/// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
298/// and return a vector with log directives.
299fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
300    let mut dirs = Vec::new();
301
302    let mut parts = spec.split('/');
303    let mods = parts.next();
304    let filter = parts.next();
305    if parts.next().is_some() {
306        eprintln!(
307            "warning: invalid logging spec '{}', \
308             ignoring it (too many '/'s)",
309            spec
310        );
311        return (dirs, None);
312    }
313    mods.map(|m| {
314        for s in m.split(',') {
315            if s.len() == 0 {
316                continue;
317            }
318            let mut parts = s.split('=');
319            let (log_level, name) =
320                match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
321                    (Some(part0), None, None) => {
322                        // if the single argument is a log-level string or number,
323                        // treat that as a global fallback
324                        match part0.parse() {
325                            Ok(num) => (num, None),
326                            Err(_) => (LevelFilter::max(), Some(part0)),
327                        }
328                    }
329                    (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
330                    (Some(part0), Some(part1), None) => match part1.parse() {
331                        Ok(num) => (num, Some(part0)),
332                        _ => {
333                            eprintln!(
334                                "warning: invalid logging spec '{}', \
335                                 ignoring it",
336                                part1
337                            );
338                            continue;
339                        }
340                    },
341                    _ => {
342                        eprintln!(
343                            "warning: invalid logging spec '{}', \
344                             ignoring it",
345                            s
346                        );
347                        continue;
348                    }
349                };
350            dirs.push(Directive {
351                name: name.map(|s| s.to_string()),
352                level: log_level,
353            });
354        }
355    });
356
357    let filter = filter.map_or(None, |filter| match inner::Filter::new(filter) {
358        Ok(re) => Some(re),
359        Err(e) => {
360            eprintln!("warning: invalid regex filter - {}", e);
361            None
362        }
363    });
364
365    return (dirs, filter);
366}
367
368// Check whether a level and target are enabled by the set of directives.
369fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
370    // Search for the longest match, the vector is assumed to be pre-sorted.
371    for directive in directives.iter().rev() {
372        match directive.name {
373            Some(ref name) if !target.starts_with(&**name) => {}
374            Some(..) | None => return level <= directive.level,
375        }
376    }
377    false
378}
379
380#[cfg(test)]
381mod tests {
382    use log::{Level, LevelFilter};
383
384    use super::{enabled, parse_spec, Builder, Directive, Filter};
385
386    fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
387        let mut logger = Builder::new().build();
388        logger.directives = dirs;
389        logger
390    }
391
392    #[test]
393    fn filter_info() {
394        let logger = Builder::new().filter(None, LevelFilter::Info).build();
395        assert!(enabled(&logger.directives, Level::Info, "crate1"));
396        assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
397    }
398
399    #[test]
400    fn filter_beginning_longest_match() {
401        let logger = Builder::new()
402            .filter(Some("crate2"), LevelFilter::Info)
403            .filter(Some("crate2::mod"), LevelFilter::Debug)
404            .filter(Some("crate1::mod1"), LevelFilter::Warn)
405            .build();
406        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
407        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
408    }
409
410    #[test]
411    fn parse_default() {
412        let logger = Builder::new().parse("info,crate1::mod1=warn").build();
413        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
414        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
415    }
416
417    #[test]
418    fn match_full_path() {
419        let logger = make_logger_filter(vec![
420            Directive {
421                name: Some("crate2".to_string()),
422                level: LevelFilter::Info,
423            },
424            Directive {
425                name: Some("crate1::mod1".to_string()),
426                level: LevelFilter::Warn,
427            },
428        ]);
429        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
430        assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
431        assert!(enabled(&logger.directives, Level::Info, "crate2"));
432        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
433    }
434
435    #[test]
436    fn no_match() {
437        let logger = make_logger_filter(vec![
438            Directive {
439                name: Some("crate2".to_string()),
440                level: LevelFilter::Info,
441            },
442            Directive {
443                name: Some("crate1::mod1".to_string()),
444                level: LevelFilter::Warn,
445            },
446        ]);
447        assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
448    }
449
450    #[test]
451    fn match_beginning() {
452        let logger = make_logger_filter(vec![
453            Directive {
454                name: Some("crate2".to_string()),
455                level: LevelFilter::Info,
456            },
457            Directive {
458                name: Some("crate1::mod1".to_string()),
459                level: LevelFilter::Warn,
460            },
461        ]);
462        assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
463    }
464
465    #[test]
466    fn match_beginning_longest_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("crate2::mod".to_string()),
474                level: LevelFilter::Debug,
475            },
476            Directive {
477                name: Some("crate1::mod1".to_string()),
478                level: LevelFilter::Warn,
479            },
480        ]);
481        assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
482        assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
483    }
484
485    #[test]
486    fn match_default() {
487        let logger = make_logger_filter(vec![
488            Directive {
489                name: None,
490                level: LevelFilter::Info,
491            },
492            Directive {
493                name: Some("crate1::mod1".to_string()),
494                level: LevelFilter::Warn,
495            },
496        ]);
497        assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
498        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
499    }
500
501    #[test]
502    fn zero_level() {
503        let logger = make_logger_filter(vec![
504            Directive {
505                name: None,
506                level: LevelFilter::Info,
507            },
508            Directive {
509                name: Some("crate1::mod1".to_string()),
510                level: LevelFilter::Off,
511            },
512        ]);
513        assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
514        assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
515    }
516
517    #[test]
518    fn parse_spec_valid() {
519        let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
520        assert_eq!(dirs.len(), 3);
521        assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
522        assert_eq!(dirs[0].level, LevelFilter::Error);
523
524        assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
525        assert_eq!(dirs[1].level, LevelFilter::max());
526
527        assert_eq!(dirs[2].name, Some("crate2".to_string()));
528        assert_eq!(dirs[2].level, LevelFilter::Debug);
529        assert!(filter.is_none());
530    }
531
532    #[test]
533    fn parse_spec_invalid_crate() {
534        // test parse_spec with multiple = in specification
535        let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
536        assert_eq!(dirs.len(), 1);
537        assert_eq!(dirs[0].name, Some("crate2".to_string()));
538        assert_eq!(dirs[0].level, LevelFilter::Debug);
539        assert!(filter.is_none());
540    }
541
542    #[test]
543    fn parse_spec_invalid_level() {
544        // test parse_spec with 'noNumber' as log level
545        let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
546        assert_eq!(dirs.len(), 1);
547        assert_eq!(dirs[0].name, Some("crate2".to_string()));
548        assert_eq!(dirs[0].level, LevelFilter::Debug);
549        assert!(filter.is_none());
550    }
551
552    #[test]
553    fn parse_spec_string_level() {
554        // test parse_spec with 'warn' as log level
555        let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
556        assert_eq!(dirs.len(), 1);
557        assert_eq!(dirs[0].name, Some("crate2".to_string()));
558        assert_eq!(dirs[0].level, LevelFilter::Warn);
559        assert!(filter.is_none());
560    }
561
562    #[test]
563    fn parse_spec_empty_level() {
564        // test parse_spec with '' as log level
565        let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
566        assert_eq!(dirs.len(), 1);
567        assert_eq!(dirs[0].name, Some("crate2".to_string()));
568        assert_eq!(dirs[0].level, LevelFilter::max());
569        assert!(filter.is_none());
570    }
571
572    #[test]
573    fn parse_spec_global() {
574        // test parse_spec with no crate
575        let (dirs, filter) = parse_spec("warn,crate2=debug");
576        assert_eq!(dirs.len(), 2);
577        assert_eq!(dirs[0].name, None);
578        assert_eq!(dirs[0].level, LevelFilter::Warn);
579        assert_eq!(dirs[1].name, Some("crate2".to_string()));
580        assert_eq!(dirs[1].level, LevelFilter::Debug);
581        assert!(filter.is_none());
582    }
583
584    #[test]
585    fn parse_spec_valid_filter() {
586        let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
587        assert_eq!(dirs.len(), 3);
588        assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
589        assert_eq!(dirs[0].level, LevelFilter::Error);
590
591        assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
592        assert_eq!(dirs[1].level, LevelFilter::max());
593
594        assert_eq!(dirs[2].name, Some("crate2".to_string()));
595        assert_eq!(dirs[2].level, LevelFilter::Debug);
596        assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
597    }
598
599    #[test]
600    fn parse_spec_invalid_crate_filter() {
601        let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
602        assert_eq!(dirs.len(), 1);
603        assert_eq!(dirs[0].name, Some("crate2".to_string()));
604        assert_eq!(dirs[0].level, LevelFilter::Debug);
605        assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
606    }
607
608    #[test]
609    fn parse_spec_empty_with_filter() {
610        let (dirs, filter) = parse_spec("crate1/a*c");
611        assert_eq!(dirs.len(), 1);
612        assert_eq!(dirs[0].name, Some("crate1".to_string()));
613        assert_eq!(dirs[0].level, LevelFilter::max());
614        assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
615    }
616}