• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 //! ```
56 //!
57 //! [Enabling Logging]: ../index.html#enabling-logging
58 //! [`Builder::parse`]: struct.Builder.html#method.parse
59 //! [`Filter::matches`]: struct.Filter.html#method.matches
60 
61 use log::{Level, LevelFilter, Metadata, Record};
62 use std::collections::HashMap;
63 use std::env;
64 use std::fmt;
65 use std::mem;
66 
67 #[cfg(feature = "regex")]
68 #[path = "regex.rs"]
69 mod inner;
70 
71 #[cfg(not(feature = "regex"))]
72 #[path = "string.rs"]
73 mod 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
82 pub 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] extern crate log;
96 /// # use std::env;
97 /// use env_logger::filter::Builder;
98 ///
99 /// let mut builder = Builder::new();
100 ///
101 /// // Parse a logging filter from an environment variable.
102 /// if let Ok(rust_log) = env::var("RUST_LOG") {
103 ///     builder.parse(&rust_log);
104 /// }
105 ///
106 /// let filter = builder.build();
107 /// ```
108 ///
109 /// [`Filter`]: struct.Filter.html
110 pub struct Builder {
111     directives: HashMap<Option<String>, LevelFilter>,
112     filter: Option<inner::Filter>,
113     built: bool,
114 }
115 
116 #[derive(Debug)]
117 struct Directive {
118     name: Option<String>,
119     level: LevelFilter,
120 }
121 
122 impl Filter {
123     /// Returns the maximum `LevelFilter` that this filter instance is
124     /// configured to output.
125     ///
126     /// # Example
127     ///
128     /// ```rust
129     /// use log::LevelFilter;
130     /// use env_logger::filter::Builder;
131     ///
132     /// let mut builder = Builder::new();
133     /// builder.filter(Some("module1"), LevelFilter::Info);
134     /// builder.filter(Some("module2"), LevelFilter::Error);
135     ///
136     /// let filter = builder.build();
137     /// assert_eq!(filter.filter(), LevelFilter::Info);
138     /// ```
filter(&self) -> LevelFilter139     pub fn filter(&self) -> LevelFilter {
140         self.directives
141             .iter()
142             .map(|d| d.level)
143             .max()
144             .unwrap_or(LevelFilter::Off)
145     }
146 
147     /// Checks if this record matches the configured filter.
matches(&self, record: &Record) -> bool148     pub fn matches(&self, record: &Record) -> bool {
149         if !self.enabled(record.metadata()) {
150             return false;
151         }
152 
153         if let Some(filter) = self.filter.as_ref() {
154             if !filter.is_match(&*record.args().to_string()) {
155                 return false;
156             }
157         }
158 
159         true
160     }
161 
162     /// Determines if a log message with the specified metadata would be logged.
enabled(&self, metadata: &Metadata) -> bool163     pub fn enabled(&self, metadata: &Metadata) -> bool {
164         let level = metadata.level();
165         let target = metadata.target();
166 
167         enabled(&self.directives, level, target)
168     }
169 }
170 
171 impl Builder {
172     /// Initializes the filter builder with defaults.
new() -> Builder173     pub fn new() -> Builder {
174         Builder {
175             directives: HashMap::new(),
176             filter: None,
177             built: false,
178         }
179     }
180 
181     /// Initializes the filter builder from an environment.
from_env(env: &str) -> Builder182     pub fn from_env(env: &str) -> Builder {
183         let mut builder = Builder::new();
184 
185         if let Ok(s) = env::var(env) {
186             builder.parse(&s);
187         }
188 
189         builder
190     }
191 
192     /// Adds a directive to the filter for a specific module.
filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self193     pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
194         self.filter(Some(module), level)
195     }
196 
197     /// Adds a directive to the filter for all modules.
filter_level(&mut self, level: LevelFilter) -> &mut Self198     pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
199         self.filter(None, level)
200     }
201 
202     /// Adds a directive to the filter.
203     ///
204     /// The given module (if any) will log at most the specified level provided.
205     /// If no module is provided then the filter will apply to all log messages.
filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self206     pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
207         self.directives.insert(module.map(|s| s.to_string()), level);
208         self
209     }
210 
211     /// Parses the directives string.
212     ///
213     /// See the [Enabling Logging] section for more details.
214     ///
215     /// [Enabling Logging]: ../index.html#enabling-logging
parse(&mut self, filters: &str) -> &mut Self216     pub fn parse(&mut self, filters: &str) -> &mut Self {
217         let (directives, filter) = parse_spec(filters);
218 
219         self.filter = filter;
220 
221         for directive in directives {
222             self.directives.insert(directive.name, directive.level);
223         }
224         self
225     }
226 
227     /// Build a log filter.
build(&mut self) -> Filter228     pub fn build(&mut self) -> Filter {
229         assert!(!self.built, "attempt to re-use consumed builder");
230         self.built = true;
231 
232         let mut directives = Vec::new();
233         if self.directives.is_empty() {
234             // Adds the default filter if none exist
235             directives.push(Directive {
236                 name: None,
237                 level: LevelFilter::Error,
238             });
239         } else {
240             // Consume map of directives.
241             let directives_map = mem::take(&mut self.directives);
242             directives = directives_map
243                 .into_iter()
244                 .map(|(name, level)| Directive { name, level })
245                 .collect();
246             // Sort the directives by length of their name, this allows a
247             // little more efficient lookup at runtime.
248             directives.sort_by(|a, b| {
249                 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
250                 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
251                 alen.cmp(&blen)
252             });
253         }
254 
255         Filter {
256             directives: mem::take(&mut directives),
257             filter: mem::replace(&mut self.filter, None),
258         }
259     }
260 }
261 
262 impl Default for Builder {
default() -> Self263     fn default() -> Self {
264         Builder::new()
265     }
266 }
267 
268 impl fmt::Debug for Filter {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result269     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
270         f.debug_struct("Filter")
271             .field("filter", &self.filter)
272             .field("directives", &self.directives)
273             .finish()
274     }
275 }
276 
277 impl fmt::Debug for Builder {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result278     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
279         if self.built {
280             f.debug_struct("Filter").field("built", &true).finish()
281         } else {
282             f.debug_struct("Filter")
283                 .field("filter", &self.filter)
284                 .field("directives", &self.directives)
285                 .finish()
286         }
287     }
288 }
289 
290 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
291 /// and return a vector with log directives.
parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>)292 fn parse_spec(spec: &str) -> (Vec<Directive>, Option<inner::Filter>) {
293     let mut dirs = Vec::new();
294 
295     let mut parts = spec.split('/');
296     let mods = parts.next();
297     let filter = parts.next();
298     if parts.next().is_some() {
299         eprintln!(
300             "warning: invalid logging spec '{}', \
301              ignoring it (too many '/'s)",
302             spec
303         );
304         return (dirs, None);
305     }
306     if let Some(m) = mods {
307         for s in m.split(',').map(|ss| ss.trim()) {
308             if s.is_empty() {
309                 continue;
310             }
311             let mut parts = s.split('=');
312             let (log_level, name) =
313                 match (parts.next(), parts.next().map(|s| s.trim()), parts.next()) {
314                     (Some(part0), None, None) => {
315                         // if the single argument is a log-level string or number,
316                         // treat that as a global fallback
317                         match part0.parse() {
318                             Ok(num) => (num, None),
319                             Err(_) => (LevelFilter::max(), Some(part0)),
320                         }
321                     }
322                     (Some(part0), Some(""), None) => (LevelFilter::max(), Some(part0)),
323                     (Some(part0), Some(part1), None) => match part1.parse() {
324                         Ok(num) => (num, Some(part0)),
325                         _ => {
326                             eprintln!(
327                                 "warning: invalid logging spec '{}', \
328                                  ignoring it",
329                                 part1
330                             );
331                             continue;
332                         }
333                     },
334                     _ => {
335                         eprintln!(
336                             "warning: invalid logging spec '{}', \
337                              ignoring it",
338                             s
339                         );
340                         continue;
341                     }
342                 };
343             dirs.push(Directive {
344                 name: name.map(|s| s.to_string()),
345                 level: log_level,
346             });
347         }
348     }
349 
350     let filter = filter.and_then(|filter| match inner::Filter::new(filter) {
351         Ok(re) => Some(re),
352         Err(e) => {
353             eprintln!("warning: invalid regex filter - {}", e);
354             None
355         }
356     });
357 
358     (dirs, filter)
359 }
360 
361 // Check whether a level and target are enabled by the set of directives.
enabled(directives: &[Directive], level: Level, target: &str) -> bool362 fn enabled(directives: &[Directive], level: Level, target: &str) -> bool {
363     // Search for the longest match, the vector is assumed to be pre-sorted.
364     for directive in directives.iter().rev() {
365         match directive.name {
366             Some(ref name) if !target.starts_with(&**name) => {}
367             Some(..) | None => return level <= directive.level,
368         }
369     }
370     false
371 }
372 
373 #[cfg(test)]
374 mod tests {
375     use log::{Level, LevelFilter};
376 
377     use super::{enabled, parse_spec, Builder, Directive, Filter};
378 
make_logger_filter(dirs: Vec<Directive>) -> Filter379     fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
380         let mut logger = Builder::new().build();
381         logger.directives = dirs;
382         logger
383     }
384 
385     #[test]
filter_info()386     fn filter_info() {
387         let logger = Builder::new().filter(None, LevelFilter::Info).build();
388         assert!(enabled(&logger.directives, Level::Info, "crate1"));
389         assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
390     }
391 
392     #[test]
filter_beginning_longest_match()393     fn filter_beginning_longest_match() {
394         let logger = Builder::new()
395             .filter(Some("crate2"), LevelFilter::Info)
396             .filter(Some("crate2::mod"), LevelFilter::Debug)
397             .filter(Some("crate1::mod1"), LevelFilter::Warn)
398             .build();
399         assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
400         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
401     }
402 
403     // Some of our tests are only correct or complete when they cover the full
404     // universe of variants for log::Level. In the unlikely event that a new
405     // variant is added in the future, this test will detect the scenario and
406     // alert us to the need to review and update the tests. In such a
407     // situation, this test will fail to compile, and the error message will
408     // look something like this:
409     //
410     //     error[E0004]: non-exhaustive patterns: `NewVariant` not covered
411     //        --> src/filter/mod.rs:413:15
412     //         |
413     //     413 |         match level_universe {
414     //         |               ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
415     #[test]
ensure_tests_cover_level_universe()416     fn ensure_tests_cover_level_universe() {
417         let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
418         match level_universe {
419             Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
420         }
421     }
422 
423     #[test]
parse_default()424     fn parse_default() {
425         let logger = Builder::new().parse("info,crate1::mod1=warn").build();
426         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
427         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
428     }
429 
430     #[test]
parse_default_bare_level_off_lc()431     fn parse_default_bare_level_off_lc() {
432         let logger = Builder::new().parse("off").build();
433         assert!(!enabled(&logger.directives, Level::Error, ""));
434         assert!(!enabled(&logger.directives, Level::Warn, ""));
435         assert!(!enabled(&logger.directives, Level::Info, ""));
436         assert!(!enabled(&logger.directives, Level::Debug, ""));
437         assert!(!enabled(&logger.directives, Level::Trace, ""));
438     }
439 
440     #[test]
parse_default_bare_level_off_uc()441     fn parse_default_bare_level_off_uc() {
442         let logger = Builder::new().parse("OFF").build();
443         assert!(!enabled(&logger.directives, Level::Error, ""));
444         assert!(!enabled(&logger.directives, Level::Warn, ""));
445         assert!(!enabled(&logger.directives, Level::Info, ""));
446         assert!(!enabled(&logger.directives, Level::Debug, ""));
447         assert!(!enabled(&logger.directives, Level::Trace, ""));
448     }
449 
450     #[test]
parse_default_bare_level_error_lc()451     fn parse_default_bare_level_error_lc() {
452         let logger = Builder::new().parse("error").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     #[test]
parse_default_bare_level_error_uc()461     fn parse_default_bare_level_error_uc() {
462         let logger = Builder::new().parse("ERROR").build();
463         assert!(enabled(&logger.directives, Level::Error, ""));
464         assert!(!enabled(&logger.directives, Level::Warn, ""));
465         assert!(!enabled(&logger.directives, Level::Info, ""));
466         assert!(!enabled(&logger.directives, Level::Debug, ""));
467         assert!(!enabled(&logger.directives, Level::Trace, ""));
468     }
469 
470     #[test]
parse_default_bare_level_warn_lc()471     fn parse_default_bare_level_warn_lc() {
472         let logger = Builder::new().parse("warn").build();
473         assert!(enabled(&logger.directives, Level::Error, ""));
474         assert!(enabled(&logger.directives, Level::Warn, ""));
475         assert!(!enabled(&logger.directives, Level::Info, ""));
476         assert!(!enabled(&logger.directives, Level::Debug, ""));
477         assert!(!enabled(&logger.directives, Level::Trace, ""));
478     }
479 
480     #[test]
parse_default_bare_level_warn_uc()481     fn parse_default_bare_level_warn_uc() {
482         let logger = Builder::new().parse("WARN").build();
483         assert!(enabled(&logger.directives, Level::Error, ""));
484         assert!(enabled(&logger.directives, Level::Warn, ""));
485         assert!(!enabled(&logger.directives, Level::Info, ""));
486         assert!(!enabled(&logger.directives, Level::Debug, ""));
487         assert!(!enabled(&logger.directives, Level::Trace, ""));
488     }
489 
490     #[test]
parse_default_bare_level_info_lc()491     fn parse_default_bare_level_info_lc() {
492         let logger = Builder::new().parse("info").build();
493         assert!(enabled(&logger.directives, Level::Error, ""));
494         assert!(enabled(&logger.directives, Level::Warn, ""));
495         assert!(enabled(&logger.directives, Level::Info, ""));
496         assert!(!enabled(&logger.directives, Level::Debug, ""));
497         assert!(!enabled(&logger.directives, Level::Trace, ""));
498     }
499 
500     #[test]
parse_default_bare_level_info_uc()501     fn parse_default_bare_level_info_uc() {
502         let logger = Builder::new().parse("INFO").build();
503         assert!(enabled(&logger.directives, Level::Error, ""));
504         assert!(enabled(&logger.directives, Level::Warn, ""));
505         assert!(enabled(&logger.directives, Level::Info, ""));
506         assert!(!enabled(&logger.directives, Level::Debug, ""));
507         assert!(!enabled(&logger.directives, Level::Trace, ""));
508     }
509 
510     #[test]
parse_default_bare_level_debug_lc()511     fn parse_default_bare_level_debug_lc() {
512         let logger = Builder::new().parse("debug").build();
513         assert!(enabled(&logger.directives, Level::Error, ""));
514         assert!(enabled(&logger.directives, Level::Warn, ""));
515         assert!(enabled(&logger.directives, Level::Info, ""));
516         assert!(enabled(&logger.directives, Level::Debug, ""));
517         assert!(!enabled(&logger.directives, Level::Trace, ""));
518     }
519 
520     #[test]
parse_default_bare_level_debug_uc()521     fn parse_default_bare_level_debug_uc() {
522         let logger = Builder::new().parse("DEBUG").build();
523         assert!(enabled(&logger.directives, Level::Error, ""));
524         assert!(enabled(&logger.directives, Level::Warn, ""));
525         assert!(enabled(&logger.directives, Level::Info, ""));
526         assert!(enabled(&logger.directives, Level::Debug, ""));
527         assert!(!enabled(&logger.directives, Level::Trace, ""));
528     }
529 
530     #[test]
parse_default_bare_level_trace_lc()531     fn parse_default_bare_level_trace_lc() {
532         let logger = Builder::new().parse("trace").build();
533         assert!(enabled(&logger.directives, Level::Error, ""));
534         assert!(enabled(&logger.directives, Level::Warn, ""));
535         assert!(enabled(&logger.directives, Level::Info, ""));
536         assert!(enabled(&logger.directives, Level::Debug, ""));
537         assert!(enabled(&logger.directives, Level::Trace, ""));
538     }
539 
540     #[test]
parse_default_bare_level_trace_uc()541     fn parse_default_bare_level_trace_uc() {
542         let logger = Builder::new().parse("TRACE").build();
543         assert!(enabled(&logger.directives, Level::Error, ""));
544         assert!(enabled(&logger.directives, Level::Warn, ""));
545         assert!(enabled(&logger.directives, Level::Info, ""));
546         assert!(enabled(&logger.directives, Level::Debug, ""));
547         assert!(enabled(&logger.directives, Level::Trace, ""));
548     }
549 
550     // In practice, the desired log level is typically specified by a token
551     // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
552     // 'TRACE'), but this tests serves as a reminder that
553     // log::Level::from_str() ignores all case variants.
554     #[test]
parse_default_bare_level_debug_mixed()555     fn parse_default_bare_level_debug_mixed() {
556         {
557             let logger = Builder::new().parse("Debug").build();
558             assert!(enabled(&logger.directives, Level::Error, ""));
559             assert!(enabled(&logger.directives, Level::Warn, ""));
560             assert!(enabled(&logger.directives, Level::Info, ""));
561             assert!(enabled(&logger.directives, Level::Debug, ""));
562             assert!(!enabled(&logger.directives, Level::Trace, ""));
563         }
564         {
565             let logger = Builder::new().parse("debuG").build();
566             assert!(enabled(&logger.directives, Level::Error, ""));
567             assert!(enabled(&logger.directives, Level::Warn, ""));
568             assert!(enabled(&logger.directives, Level::Info, ""));
569             assert!(enabled(&logger.directives, Level::Debug, ""));
570             assert!(!enabled(&logger.directives, Level::Trace, ""));
571         }
572         {
573             let logger = Builder::new().parse("deBug").build();
574             assert!(enabled(&logger.directives, Level::Error, ""));
575             assert!(enabled(&logger.directives, Level::Warn, ""));
576             assert!(enabled(&logger.directives, Level::Info, ""));
577             assert!(enabled(&logger.directives, Level::Debug, ""));
578             assert!(!enabled(&logger.directives, Level::Trace, ""));
579         }
580         {
581             let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
582             assert!(enabled(&logger.directives, Level::Error, ""));
583             assert!(enabled(&logger.directives, Level::Warn, ""));
584             assert!(enabled(&logger.directives, Level::Info, ""));
585             assert!(enabled(&logger.directives, Level::Debug, ""));
586             assert!(!enabled(&logger.directives, Level::Trace, ""));
587         }
588     }
589 
590     #[test]
match_full_path()591     fn match_full_path() {
592         let logger = make_logger_filter(vec![
593             Directive {
594                 name: Some("crate2".to_string()),
595                 level: LevelFilter::Info,
596             },
597             Directive {
598                 name: Some("crate1::mod1".to_string()),
599                 level: LevelFilter::Warn,
600             },
601         ]);
602         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
603         assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
604         assert!(enabled(&logger.directives, Level::Info, "crate2"));
605         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
606     }
607 
608     #[test]
no_match()609     fn no_match() {
610         let logger = make_logger_filter(vec![
611             Directive {
612                 name: Some("crate2".to_string()),
613                 level: LevelFilter::Info,
614             },
615             Directive {
616                 name: Some("crate1::mod1".to_string()),
617                 level: LevelFilter::Warn,
618             },
619         ]);
620         assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
621     }
622 
623     #[test]
match_beginning()624     fn match_beginning() {
625         let logger = make_logger_filter(vec![
626             Directive {
627                 name: Some("crate2".to_string()),
628                 level: LevelFilter::Info,
629             },
630             Directive {
631                 name: Some("crate1::mod1".to_string()),
632                 level: LevelFilter::Warn,
633             },
634         ]);
635         assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
636     }
637 
638     #[test]
match_beginning_longest_match()639     fn match_beginning_longest_match() {
640         let logger = make_logger_filter(vec![
641             Directive {
642                 name: Some("crate2".to_string()),
643                 level: LevelFilter::Info,
644             },
645             Directive {
646                 name: Some("crate2::mod".to_string()),
647                 level: LevelFilter::Debug,
648             },
649             Directive {
650                 name: Some("crate1::mod1".to_string()),
651                 level: LevelFilter::Warn,
652             },
653         ]);
654         assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
655         assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
656     }
657 
658     #[test]
match_default()659     fn match_default() {
660         let logger = make_logger_filter(vec![
661             Directive {
662                 name: None,
663                 level: LevelFilter::Info,
664             },
665             Directive {
666                 name: Some("crate1::mod1".to_string()),
667                 level: LevelFilter::Warn,
668             },
669         ]);
670         assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
671         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
672     }
673 
674     #[test]
zero_level()675     fn zero_level() {
676         let logger = make_logger_filter(vec![
677             Directive {
678                 name: None,
679                 level: LevelFilter::Info,
680             },
681             Directive {
682                 name: Some("crate1::mod1".to_string()),
683                 level: LevelFilter::Off,
684             },
685         ]);
686         assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
687         assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
688     }
689 
690     #[test]
parse_spec_valid()691     fn parse_spec_valid() {
692         let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug");
693         assert_eq!(dirs.len(), 3);
694         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
695         assert_eq!(dirs[0].level, LevelFilter::Error);
696 
697         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
698         assert_eq!(dirs[1].level, LevelFilter::max());
699 
700         assert_eq!(dirs[2].name, Some("crate2".to_string()));
701         assert_eq!(dirs[2].level, LevelFilter::Debug);
702         assert!(filter.is_none());
703     }
704 
705     #[test]
parse_spec_invalid_crate()706     fn parse_spec_invalid_crate() {
707         // test parse_spec with multiple = in specification
708         let (dirs, filter) = parse_spec("crate1::mod1=warn=info,crate2=debug");
709         assert_eq!(dirs.len(), 1);
710         assert_eq!(dirs[0].name, Some("crate2".to_string()));
711         assert_eq!(dirs[0].level, LevelFilter::Debug);
712         assert!(filter.is_none());
713     }
714 
715     #[test]
parse_spec_invalid_level()716     fn parse_spec_invalid_level() {
717         // test parse_spec with 'noNumber' as log level
718         let (dirs, filter) = parse_spec("crate1::mod1=noNumber,crate2=debug");
719         assert_eq!(dirs.len(), 1);
720         assert_eq!(dirs[0].name, Some("crate2".to_string()));
721         assert_eq!(dirs[0].level, LevelFilter::Debug);
722         assert!(filter.is_none());
723     }
724 
725     #[test]
parse_spec_string_level()726     fn parse_spec_string_level() {
727         // test parse_spec with 'warn' as log level
728         let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=warn");
729         assert_eq!(dirs.len(), 1);
730         assert_eq!(dirs[0].name, Some("crate2".to_string()));
731         assert_eq!(dirs[0].level, LevelFilter::Warn);
732         assert!(filter.is_none());
733     }
734 
735     #[test]
parse_spec_empty_level()736     fn parse_spec_empty_level() {
737         // test parse_spec with '' as log level
738         let (dirs, filter) = parse_spec("crate1::mod1=wrong,crate2=");
739         assert_eq!(dirs.len(), 1);
740         assert_eq!(dirs[0].name, Some("crate2".to_string()));
741         assert_eq!(dirs[0].level, LevelFilter::max());
742         assert!(filter.is_none());
743     }
744 
745     #[test]
parse_spec_empty_level_isolated()746     fn parse_spec_empty_level_isolated() {
747         // test parse_spec with "" as log level (and the entire spec str)
748         let (dirs, filter) = parse_spec(""); // should be ignored
749         assert_eq!(dirs.len(), 0);
750         assert!(filter.is_none());
751     }
752 
753     #[test]
parse_spec_blank_level_isolated()754     fn parse_spec_blank_level_isolated() {
755         // test parse_spec with a white-space-only string specified as the log
756         // level (and the entire spec str)
757         let (dirs, filter) = parse_spec("     "); // should be ignored
758         assert_eq!(dirs.len(), 0);
759         assert!(filter.is_none());
760     }
761 
762     #[test]
parse_spec_blank_level_isolated_comma_only()763     fn parse_spec_blank_level_isolated_comma_only() {
764         // The spec should contain zero or more comma-separated string slices,
765         // so a comma-only string should be interpretted as two empty strings
766         // (which should both be treated as invalid, so ignored).
767         let (dirs, filter) = parse_spec(","); // should be ignored
768         assert_eq!(dirs.len(), 0);
769         assert!(filter.is_none());
770     }
771 
772     #[test]
parse_spec_blank_level_isolated_comma_blank()773     fn parse_spec_blank_level_isolated_comma_blank() {
774         // The spec should contain zero or more comma-separated string slices,
775         // so this bogus spec should be interpretted as containing one empty
776         // string and one blank string. Both should both be treated as
777         // invalid, so ignored.
778         let (dirs, filter) = parse_spec(",     "); // should be ignored
779         assert_eq!(dirs.len(), 0);
780         assert!(filter.is_none());
781     }
782 
783     #[test]
parse_spec_blank_level_isolated_blank_comma()784     fn parse_spec_blank_level_isolated_blank_comma() {
785         // The spec should contain zero or more comma-separated string slices,
786         // so this bogus spec should be interpretted as containing one blank
787         // string and one empty string. Both should both be treated as
788         // invalid, so ignored.
789         let (dirs, filter) = parse_spec("     ,"); // should be ignored
790         assert_eq!(dirs.len(), 0);
791         assert!(filter.is_none());
792     }
793 
794     #[test]
parse_spec_global()795     fn parse_spec_global() {
796         // test parse_spec with no crate
797         let (dirs, filter) = parse_spec("warn,crate2=debug");
798         assert_eq!(dirs.len(), 2);
799         assert_eq!(dirs[0].name, None);
800         assert_eq!(dirs[0].level, LevelFilter::Warn);
801         assert_eq!(dirs[1].name, Some("crate2".to_string()));
802         assert_eq!(dirs[1].level, LevelFilter::Debug);
803         assert!(filter.is_none());
804     }
805 
806     #[test]
parse_spec_global_bare_warn_lc()807     fn parse_spec_global_bare_warn_lc() {
808         // test parse_spec with no crate, in isolation, all lowercase
809         let (dirs, filter) = parse_spec("warn");
810         assert_eq!(dirs.len(), 1);
811         assert_eq!(dirs[0].name, None);
812         assert_eq!(dirs[0].level, LevelFilter::Warn);
813         assert!(filter.is_none());
814     }
815 
816     #[test]
parse_spec_global_bare_warn_uc()817     fn parse_spec_global_bare_warn_uc() {
818         // test parse_spec with no crate, in isolation, all uppercase
819         let (dirs, filter) = parse_spec("WARN");
820         assert_eq!(dirs.len(), 1);
821         assert_eq!(dirs[0].name, None);
822         assert_eq!(dirs[0].level, LevelFilter::Warn);
823         assert!(filter.is_none());
824     }
825 
826     #[test]
parse_spec_global_bare_warn_mixed()827     fn parse_spec_global_bare_warn_mixed() {
828         // test parse_spec with no crate, in isolation, mixed case
829         let (dirs, filter) = parse_spec("wArN");
830         assert_eq!(dirs.len(), 1);
831         assert_eq!(dirs[0].name, None);
832         assert_eq!(dirs[0].level, LevelFilter::Warn);
833         assert!(filter.is_none());
834     }
835 
836     #[test]
parse_spec_valid_filter()837     fn parse_spec_valid_filter() {
838         let (dirs, filter) = parse_spec("crate1::mod1=error,crate1::mod2,crate2=debug/abc");
839         assert_eq!(dirs.len(), 3);
840         assert_eq!(dirs[0].name, Some("crate1::mod1".to_string()));
841         assert_eq!(dirs[0].level, LevelFilter::Error);
842 
843         assert_eq!(dirs[1].name, Some("crate1::mod2".to_string()));
844         assert_eq!(dirs[1].level, LevelFilter::max());
845 
846         assert_eq!(dirs[2].name, Some("crate2".to_string()));
847         assert_eq!(dirs[2].level, LevelFilter::Debug);
848         assert!(filter.is_some() && filter.unwrap().to_string() == "abc");
849     }
850 
851     #[test]
parse_spec_invalid_crate_filter()852     fn parse_spec_invalid_crate_filter() {
853         let (dirs, filter) = parse_spec("crate1::mod1=error=warn,crate2=debug/a.c");
854         assert_eq!(dirs.len(), 1);
855         assert_eq!(dirs[0].name, Some("crate2".to_string()));
856         assert_eq!(dirs[0].level, LevelFilter::Debug);
857         assert!(filter.is_some() && filter.unwrap().to_string() == "a.c");
858     }
859 
860     #[test]
parse_spec_empty_with_filter()861     fn parse_spec_empty_with_filter() {
862         let (dirs, filter) = parse_spec("crate1/a*c");
863         assert_eq!(dirs.len(), 1);
864         assert_eq!(dirs[0].name, Some("crate1".to_string()));
865         assert_eq!(dirs[0].level, LevelFilter::max());
866         assert!(filter.is_some() && filter.unwrap().to_string() == "a*c");
867     }
868 }
869