1 //! Filtering for log records.
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].
8 //! ## Using `env_logger` in your own logger
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.
17 //! extern crate env_logger;
18 //! use env_logger::filter::Filter;
19 //! use log::{Log, Metadata, Record};
26 //! fn new() -> MyLogger {
27 //! use env_logger::filter::Builder;
28 //! let mut builder = Builder::new();
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);
36 //! filter: builder.build()
41 //! impl Log for MyLogger {
42 //! fn enabled(&self, metadata: &Metadata) -> bool {
43 //! self.filter.enabled(metadata)
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);
53 //! fn flush(&self) {}
57 //! [Enabling Logging]: ../index.html#enabling-logging
58 //! [`Builder::parse`]: struct.Builder.html#method.parse
59 //! [`Filter::matches`]: struct.Filter.html#method.matches
61 use log
::{Level, LevelFilter, Metadata, Record}
;
62 use std
::collections
::HashMap
;
67 #[cfg(feature = "regex")]
71 #[cfg(not(feature = "regex"))]
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`.
81 /// [`Builder`]: struct.Builder.html
83 directives
: Vec
<Directive
>,
84 filter
: Option
<inner
::Filter
>,
87 /// A builder for a log filter.
89 /// It can be used to parse a set of directives from a string before building
90 /// a [`Filter`] instance.
95 /// # #[macro_use] extern crate log;
97 /// use env_logger::filter::Builder;
99 /// let mut builder = Builder::new();
101 /// // Parse a logging filter from an environment variable.
102 /// if let Ok(rust_log) = env::var("RUST_LOG") {
103 /// builder.parse(&rust_log);
106 /// let filter = builder.build();
109 /// [`Filter`]: struct.Filter.html
111 directives
: HashMap
<Option
<String
>, LevelFilter
>,
112 filter
: Option
<inner
::Filter
>,
118 name
: Option
<String
>,
123 /// Returns the maximum `LevelFilter` that this filter instance is
124 /// configured to output.
129 /// use log::LevelFilter;
130 /// use env_logger::filter::Builder;
132 /// let mut builder = Builder::new();
133 /// builder.filter(Some("module1"), LevelFilter::Info);
134 /// builder.filter(Some("module2"), LevelFilter::Error);
136 /// let filter = builder.build();
137 /// assert_eq!(filter.filter(), LevelFilter::Info);
139 pub fn filter(&self) -> LevelFilter
{
144 .unwrap_or(LevelFilter
::Off
)
147 /// Checks if this record matches the configured filter.
148 pub fn matches(&self, record
: &Record
) -> bool
{
149 if !self.enabled(record
.metadata()) {
153 if let Some(filter
) = self.filter
.as_ref() {
154 if !filter
.is_match(&*record
.args().to_string()) {
162 /// Determines if a log message with the specified metadata would be logged.
163 pub fn enabled(&self, metadata
: &Metadata
) -> bool
{
164 let level
= metadata
.level();
165 let target
= metadata
.target();
167 enabled(&self.directives
, level
, target
)
172 /// Initializes the filter builder with defaults.
173 pub fn new() -> Builder
{
175 directives
: HashMap
::new(),
181 /// Initializes the filter builder from an environment.
182 pub fn from_env(env
: &str) -> Builder
{
183 let mut builder
= Builder
::new();
185 if let Ok(s
) = env
::var(env
) {
192 /// Adds a directive to the filter for a specific module.
193 pub fn filter_module(&mut self, module
: &str, level
: LevelFilter
) -> &mut Self {
194 self.filter(Some(module
), level
)
197 /// Adds a directive to the filter for all modules.
198 pub fn filter_level(&mut self, level
: LevelFilter
) -> &mut Self {
199 self.filter(None
, level
)
202 /// Adds a directive to the filter.
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.
206 pub fn filter(&mut self, module
: Option
<&str>, level
: LevelFilter
) -> &mut Self {
207 self.directives
.insert(module
.map(|s
| s
.to_string()), level
);
211 /// Parses the directives string.
213 /// See the [Enabling Logging] section for more details.
215 /// [Enabling Logging]: ../index.html#enabling-logging
216 pub fn parse(&mut self, filters
: &str) -> &mut Self {
217 let (directives
, filter
) = parse_spec(filters
);
219 self.filter
= filter
;
221 for directive
in directives
{
222 self.directives
.insert(directive
.name
, directive
.level
);
227 /// Build a log filter.
228 pub fn build(&mut self) -> Filter
{
229 assert
!(!self.built
, "attempt to re-use consumed builder");
232 let mut directives
= Vec
::new();
233 if self.directives
.is_empty() {
234 // Adds the default filter if none exist
235 directives
.push(Directive
{
237 level
: LevelFilter
::Error
,
240 // Consume map of directives.
241 let directives_map
= mem
::take(&mut self.directives
);
242 directives
= directives_map
244 .map(|(name
, level
)| Directive { name, level }
)
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);
256 directives
: mem
::take(&mut directives
),
257 filter
: mem
::replace(&mut self.filter
, None
),
262 impl Default
for Builder
{
263 fn default() -> Self {
268 impl fmt
::Debug
for Filter
{
269 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
270 f
.debug_struct("Filter")
271 .field("filter", &self.filter
)
272 .field("directives", &self.directives
)
277 impl fmt
::Debug
for Builder
{
278 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
280 f
.debug_struct("Filter").field("built", &true).finish()
282 f
.debug_struct("Filter")
283 .field("filter", &self.filter
)
284 .field("directives", &self.directives
)
290 /// Parse a logging specification string (e.g: "crate1,crate2::mod3,crate3::x=error/foo")
291 /// and return a vector with log directives.
292 fn parse_spec(spec
: &str) -> (Vec
<Directive
>, Option
<inner
::Filter
>) {
293 let mut dirs
= Vec
::new();
295 let mut parts
= spec
.split('
/'
);
296 let mods
= parts
.next();
297 let filter
= parts
.next();
298 if parts
.next().is_some() {
300 "warning: invalid logging spec '{}', \
301 ignoring it (too many '/'s)",
306 if let Some(m
) = mods
{
307 for s
in m
.split('
,'
).map(|ss
| ss
.trim()) {
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
)),
322 (Some(part0
), Some(""), None
) => (LevelFilter
::max(), Some(part0
)),
323 (Some(part0
), Some(part1
), None
) => match part1
.parse() {
324 Ok(num
) => (num
, Some(part0
)),
327 "warning: invalid logging spec '{}', \
336 "warning: invalid logging spec '{}', \
343 dirs
.push(Directive
{
344 name
: name
.map(|s
| s
.to_string()),
350 let filter
= filter
.and_then(|filter
| match inner
::Filter
::new(filter
) {
353 eprintln
!("warning: invalid regex filter - {}", e
);
361 // Check whether a level and target are enabled by the set of directives.
362 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
,
375 use log
::{Level, LevelFilter}
;
377 use super::{enabled, parse_spec, Builder, Directive, Filter}
;
379 fn make_logger_filter(dirs
: Vec
<Directive
>) -> Filter
{
380 let mut logger
= Builder
::new().build();
381 logger
.directives
= dirs
;
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"));
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
)
399 assert
!(enabled(&logger
.directives
, Level
::Debug
, "crate2::mod1"));
400 assert
!(!enabled(&logger
.directives
, Level
::Debug
, "crate2"));
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:
410 // error[E0004]: non-exhaustive patterns: `NewVariant` not covered
411 // --> src/filter/mod.rs:413:15
413 // 413 | match level_universe {
414 // | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
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
=> (),
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"));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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
, ""));
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.
555 fn parse_default_bare_level_debug_mixed() {
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
, ""));
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
, ""));
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
, ""));
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
, ""));
591 fn match_full_path() {
592 let logger
= make_logger_filter(vec
![
594 name
: Some("crate2".to_string()),
595 level
: LevelFilter
::Info
,
598 name
: Some("crate1::mod1".to_string()),
599 level
: LevelFilter
::Warn
,
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"));
610 let logger
= make_logger_filter(vec
![
612 name
: Some("crate2".to_string()),
613 level
: LevelFilter
::Info
,
616 name
: Some("crate1::mod1".to_string()),
617 level
: LevelFilter
::Warn
,
620 assert
!(!enabled(&logger
.directives
, Level
::Warn
, "crate3"));
624 fn match_beginning() {
625 let logger
= make_logger_filter(vec
![
627 name
: Some("crate2".to_string()),
628 level
: LevelFilter
::Info
,
631 name
: Some("crate1::mod1".to_string()),
632 level
: LevelFilter
::Warn
,
635 assert
!(enabled(&logger
.directives
, Level
::Info
, "crate2::mod1"));
639 fn match_beginning_longest_match() {
640 let logger
= make_logger_filter(vec
![
642 name
: Some("crate2".to_string()),
643 level
: LevelFilter
::Info
,
646 name
: Some("crate2::mod".to_string()),
647 level
: LevelFilter
::Debug
,
650 name
: Some("crate1::mod1".to_string()),
651 level
: LevelFilter
::Warn
,
654 assert
!(enabled(&logger
.directives
, Level
::Debug
, "crate2::mod1"));
655 assert
!(!enabled(&logger
.directives
, Level
::Debug
, "crate2"));
660 let logger
= make_logger_filter(vec
![
663 level
: LevelFilter
::Info
,
666 name
: Some("crate1::mod1".to_string()),
667 level
: LevelFilter
::Warn
,
670 assert
!(enabled(&logger
.directives
, Level
::Warn
, "crate1::mod1"));
671 assert
!(enabled(&logger
.directives
, Level
::Info
, "crate2::mod2"));
676 let logger
= make_logger_filter(vec
![
679 level
: LevelFilter
::Info
,
682 name
: Some("crate1::mod1".to_string()),
683 level
: LevelFilter
::Off
,
686 assert
!(!enabled(&logger
.directives
, Level
::Error
, "crate1::mod1"));
687 assert
!(enabled(&logger
.directives
, Level
::Info
, "crate2::mod2"));
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
);
697 assert_eq
!(dirs
[1].name
, Some("crate1::mod2".to_string()));
698 assert_eq
!(dirs
[1].level
, LevelFilter
::max());
700 assert_eq
!(dirs
[2].name
, Some("crate2".to_string()));
701 assert_eq
!(dirs
[2].level
, LevelFilter
::Debug
);
702 assert
!(filter
.is_none());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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());
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
);
843 assert_eq
!(dirs
[1].name
, Some("crate1::mod2".to_string()));
844 assert_eq
!(dirs
[1].level
, LevelFilter
::max());
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");
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");
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");