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 | /// ``` |
139 | 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. |
148 | 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. |
163 | 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. |
173 | 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. |
182 | 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. |
193 | 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. |
198 | 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. |
206 | 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 |
216 | 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. |
228 | 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 { |
263 | fn default() -> Self { |
264 | Builder::new() |
265 | } |
266 | } |
267 | |
268 | impl fmt::Debug for Filter { |
269 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
270 | f&mut DebugStruct<'_, '_>.debug_struct("Filter" ) |
271 | .field("filter" , &self.filter) |
272 | .field(name:"directives" , &self.directives) |
273 | .finish() |
274 | } |
275 | } |
276 | |
277 | impl fmt::Debug for Builder { |
278 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
279 | if self.built { |
280 | f.debug_struct("Filter" ).field(name:"built" , &true).finish() |
281 | } else { |
282 | f&mut DebugStruct<'_, '_>.debug_struct("Filter" ) |
283 | .field("filter" , &self.filter) |
284 | .field(name:"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. |
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. |
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: &Directive in directives.iter().rev() { |
365 | match directive.name { |
366 | Some(ref name: &String) 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 | |
379 | 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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 interpreted 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 ] |
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 interpreted 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 ] |
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 interpreted 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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 ] |
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 | |