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