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