1use std::env;
2use std::fmt;
3use std::mem;
4
5use log::{LevelFilter, Metadata, Record};
6
7use crate::enabled;
8use crate::parse_spec;
9use crate::parser::ParseResult;
10use crate::Directive;
11use crate::FilterOp;
12use crate::ParseError;
13
14/// A builder for a log filter.
15///
16/// It can be used to parse a set of directives from a string before building
17/// a [`Filter`] instance.
18///
19/// ## Example
20///
21/// ```
22/// # use std::env;
23/// use env_filter::Builder;
24///
25/// let mut builder = Builder::new();
26///
27/// // Parse a logging filter from an environment variable.
28/// if let Ok(rust_log) = env::var("RUST_LOG") {
29/// builder.parse(&rust_log);
30/// }
31///
32/// let filter = builder.build();
33/// ```
34pub struct Builder {
35 directives: Vec<Directive>,
36 filter: Option<FilterOp>,
37 built: bool,
38}
39
40impl Builder {
41 /// Initializes the filter builder with defaults.
42 pub fn new() -> Builder {
43 Builder {
44 directives: Vec::new(),
45 filter: None,
46 built: false,
47 }
48 }
49
50 /// Initializes the filter builder from an environment.
51 pub fn from_env(env: &str) -> Builder {
52 let mut builder = Builder::new();
53
54 if let Ok(s) = env::var(env) {
55 builder.parse(&s);
56 }
57
58 builder
59 }
60
61 /// Insert the directive replacing any directive with the same name.
62 fn insert_directive(&mut self, mut directive: Directive) {
63 if let Some(pos) = self
64 .directives
65 .iter()
66 .position(|d| d.name == directive.name)
67 {
68 mem::swap(&mut self.directives[pos], &mut directive);
69 } else {
70 self.directives.push(directive);
71 }
72 }
73
74 /// Adds a directive to the filter for a specific module.
75 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
76 self.filter(Some(module), level)
77 }
78
79 /// Adds a directive to the filter for all modules.
80 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
81 self.filter(None, level)
82 }
83
84 /// Adds a directive to the filter.
85 ///
86 /// The given module (if any) will log at most the specified level provided.
87 /// If no module is provided then the filter will apply to all log messages.
88 pub fn filter(&mut self, module: Option<&str>, level: LevelFilter) -> &mut Self {
89 self.insert_directive(Directive {
90 name: module.map(|s| s.to_owned()),
91 level,
92 });
93 self
94 }
95
96 /// Parses the directives string.
97 ///
98 /// See the [Enabling Logging] section for more details.
99 ///
100 /// [Enabling Logging]: ../index.html#enabling-logging
101 pub fn parse(&mut self, filters: &str) -> &mut Self {
102 #![allow(clippy::print_stderr)] // compatibility
103
104 let ParseResult {
105 directives,
106 filter,
107 errors,
108 } = parse_spec(filters);
109
110 for error in errors {
111 eprintln!("warning: {error}, ignoring it");
112 }
113
114 self.filter = filter;
115
116 for directive in directives {
117 self.insert_directive(directive);
118 }
119 self
120 }
121
122 /// Parses the directive string, returning an error if the given directive string is invalid.
123 ///
124 /// See the [Enabling Logging] section for more details.
125 ///
126 /// [Enabling Logging]: ../index.html#enabling-logging
127 pub fn try_parse(&mut self, filters: &str) -> Result<&mut Self, ParseError> {
128 let (directives, filter) = parse_spec(filters).ok()?;
129
130 self.filter = filter;
131
132 for directive in directives {
133 self.insert_directive(directive);
134 }
135 Ok(self)
136 }
137
138 /// Build a log filter.
139 pub fn build(&mut self) -> Filter {
140 assert!(!self.built, "attempt to re-use consumed builder");
141 self.built = true;
142
143 let mut directives = Vec::new();
144 if self.directives.is_empty() {
145 // Adds the default filter if none exist
146 directives.push(Directive {
147 name: None,
148 level: LevelFilter::Error,
149 });
150 } else {
151 // Consume directives.
152 directives = mem::take(&mut self.directives);
153 // Sort the directives by length of their name, this allows a
154 // little more efficient lookup at runtime.
155 directives.sort_by(|a, b| {
156 let alen = a.name.as_ref().map(|a| a.len()).unwrap_or(0);
157 let blen = b.name.as_ref().map(|b| b.len()).unwrap_or(0);
158 alen.cmp(&blen)
159 });
160 }
161
162 Filter {
163 directives: mem::take(&mut directives),
164 filter: mem::take(&mut self.filter),
165 }
166 }
167}
168
169impl Default for Builder {
170 fn default() -> Self {
171 Builder::new()
172 }
173}
174
175impl fmt::Debug for Builder {
176 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177 if self.built {
178 f.debug_struct("Filter").field(name:"built", &true).finish()
179 } else {
180 f&mut DebugStruct<'_, '_>.debug_struct("Filter")
181 .field("filter", &self.filter)
182 .field(name:"directives", &self.directives)
183 .finish()
184 }
185 }
186}
187
188/// A log filter.
189///
190/// This struct can be used to determine whether or not a log record
191/// should be written to the output.
192/// Use the [`Builder`] type to parse and construct a `Filter`.
193///
194/// [`Builder`]: struct.Builder.html
195pub struct Filter {
196 directives: Vec<Directive>,
197 filter: Option<FilterOp>,
198}
199
200impl Filter {
201 /// Returns the maximum `LevelFilter` that this filter instance is
202 /// configured to output.
203 ///
204 /// # Example
205 ///
206 /// ```rust
207 /// use log::LevelFilter;
208 /// use env_filter::Builder;
209 ///
210 /// let mut builder = Builder::new();
211 /// builder.filter(Some("module1"), LevelFilter::Info);
212 /// builder.filter(Some("module2"), LevelFilter::Error);
213 ///
214 /// let filter = builder.build();
215 /// assert_eq!(filter.filter(), LevelFilter::Info);
216 /// ```
217 pub fn filter(&self) -> LevelFilter {
218 self.directives
219 .iter()
220 .map(|d| d.level)
221 .max()
222 .unwrap_or(LevelFilter::Off)
223 }
224
225 /// Checks if this record matches the configured filter.
226 pub fn matches(&self, record: &Record<'_>) -> bool {
227 if !self.enabled(record.metadata()) {
228 return false;
229 }
230
231 if let Some(filter) = self.filter.as_ref() {
232 if !filter.is_match(&record.args().to_string()) {
233 return false;
234 }
235 }
236
237 true
238 }
239
240 /// Determines if a log message with the specified metadata would be logged.
241 pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
242 let level = metadata.level();
243 let target = metadata.target();
244
245 enabled(&self.directives, level, target)
246 }
247}
248
249impl fmt::Debug for Filter {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 f&mut DebugStruct<'_, '_>.debug_struct("Filter")
252 .field("filter", &self.filter)
253 .field(name:"directives", &self.directives)
254 .finish()
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use log::{Level, LevelFilter};
261 use snapbox::{assert_data_eq, str};
262
263 use super::{enabled, Builder, Directive, Filter};
264
265 fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
266 let mut logger = Builder::new().build();
267 logger.directives = dirs;
268 logger
269 }
270
271 #[test]
272 fn filter_info() {
273 let logger = Builder::new().filter(None, LevelFilter::Info).build();
274 assert!(enabled(&logger.directives, Level::Info, "crate1"));
275 assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
276 }
277
278 #[test]
279 fn filter_beginning_longest_match() {
280 let logger = Builder::new()
281 .filter(Some("crate2"), LevelFilter::Info)
282 .filter(Some("crate2::mod"), LevelFilter::Debug)
283 .filter(Some("crate1::mod1"), LevelFilter::Warn)
284 .build();
285 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
286 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
287 }
288
289 // Some of our tests are only correct or complete when they cover the full
290 // universe of variants for log::Level. In the unlikely event that a new
291 // variant is added in the future, this test will detect the scenario and
292 // alert us to the need to review and update the tests. In such a
293 // situation, this test will fail to compile, and the error message will
294 // look something like this:
295 //
296 // error[E0004]: non-exhaustive patterns: `NewVariant` not covered
297 // --> src/filter/mod.rs:413:15
298 // |
299 // 413 | match level_universe {
300 // | ^^^^^^^^^^^^^^ pattern `NewVariant` not covered
301 #[test]
302 fn ensure_tests_cover_level_universe() {
303 let level_universe: Level = Level::Trace; // use of trace variant is arbitrary
304 match level_universe {
305 Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
306 }
307 }
308
309 #[test]
310 fn parse_default() {
311 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
312 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
313 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
314 }
315
316 #[test]
317 fn parse_default_bare_level_off_lc() {
318 let logger = Builder::new().parse("off").build();
319 assert!(!enabled(&logger.directives, Level::Error, ""));
320 assert!(!enabled(&logger.directives, Level::Warn, ""));
321 assert!(!enabled(&logger.directives, Level::Info, ""));
322 assert!(!enabled(&logger.directives, Level::Debug, ""));
323 assert!(!enabled(&logger.directives, Level::Trace, ""));
324 }
325
326 #[test]
327 fn parse_default_bare_level_off_uc() {
328 let logger = Builder::new().parse("OFF").build();
329 assert!(!enabled(&logger.directives, Level::Error, ""));
330 assert!(!enabled(&logger.directives, Level::Warn, ""));
331 assert!(!enabled(&logger.directives, Level::Info, ""));
332 assert!(!enabled(&logger.directives, Level::Debug, ""));
333 assert!(!enabled(&logger.directives, Level::Trace, ""));
334 }
335
336 #[test]
337 fn parse_default_bare_level_error_lc() {
338 let logger = Builder::new().parse("error").build();
339 assert!(enabled(&logger.directives, Level::Error, ""));
340 assert!(!enabled(&logger.directives, Level::Warn, ""));
341 assert!(!enabled(&logger.directives, Level::Info, ""));
342 assert!(!enabled(&logger.directives, Level::Debug, ""));
343 assert!(!enabled(&logger.directives, Level::Trace, ""));
344 }
345
346 #[test]
347 fn parse_default_bare_level_error_uc() {
348 let logger = Builder::new().parse("ERROR").build();
349 assert!(enabled(&logger.directives, Level::Error, ""));
350 assert!(!enabled(&logger.directives, Level::Warn, ""));
351 assert!(!enabled(&logger.directives, Level::Info, ""));
352 assert!(!enabled(&logger.directives, Level::Debug, ""));
353 assert!(!enabled(&logger.directives, Level::Trace, ""));
354 }
355
356 #[test]
357 fn parse_default_bare_level_warn_lc() {
358 let logger = Builder::new().parse("warn").build();
359 assert!(enabled(&logger.directives, Level::Error, ""));
360 assert!(enabled(&logger.directives, Level::Warn, ""));
361 assert!(!enabled(&logger.directives, Level::Info, ""));
362 assert!(!enabled(&logger.directives, Level::Debug, ""));
363 assert!(!enabled(&logger.directives, Level::Trace, ""));
364 }
365
366 #[test]
367 fn parse_default_bare_level_warn_uc() {
368 let logger = Builder::new().parse("WARN").build();
369 assert!(enabled(&logger.directives, Level::Error, ""));
370 assert!(enabled(&logger.directives, Level::Warn, ""));
371 assert!(!enabled(&logger.directives, Level::Info, ""));
372 assert!(!enabled(&logger.directives, Level::Debug, ""));
373 assert!(!enabled(&logger.directives, Level::Trace, ""));
374 }
375
376 #[test]
377 fn parse_default_bare_level_info_lc() {
378 let logger = Builder::new().parse("info").build();
379 assert!(enabled(&logger.directives, Level::Error, ""));
380 assert!(enabled(&logger.directives, Level::Warn, ""));
381 assert!(enabled(&logger.directives, Level::Info, ""));
382 assert!(!enabled(&logger.directives, Level::Debug, ""));
383 assert!(!enabled(&logger.directives, Level::Trace, ""));
384 }
385
386 #[test]
387 fn parse_default_bare_level_info_uc() {
388 let logger = Builder::new().parse("INFO").build();
389 assert!(enabled(&logger.directives, Level::Error, ""));
390 assert!(enabled(&logger.directives, Level::Warn, ""));
391 assert!(enabled(&logger.directives, Level::Info, ""));
392 assert!(!enabled(&logger.directives, Level::Debug, ""));
393 assert!(!enabled(&logger.directives, Level::Trace, ""));
394 }
395
396 #[test]
397 fn parse_default_bare_level_debug_lc() {
398 let logger = Builder::new().parse("debug").build();
399 assert!(enabled(&logger.directives, Level::Error, ""));
400 assert!(enabled(&logger.directives, Level::Warn, ""));
401 assert!(enabled(&logger.directives, Level::Info, ""));
402 assert!(enabled(&logger.directives, Level::Debug, ""));
403 assert!(!enabled(&logger.directives, Level::Trace, ""));
404 }
405
406 #[test]
407 fn parse_default_bare_level_debug_uc() {
408 let logger = Builder::new().parse("DEBUG").build();
409 assert!(enabled(&logger.directives, Level::Error, ""));
410 assert!(enabled(&logger.directives, Level::Warn, ""));
411 assert!(enabled(&logger.directives, Level::Info, ""));
412 assert!(enabled(&logger.directives, Level::Debug, ""));
413 assert!(!enabled(&logger.directives, Level::Trace, ""));
414 }
415
416 #[test]
417 fn parse_default_bare_level_trace_lc() {
418 let logger = Builder::new().parse("trace").build();
419 assert!(enabled(&logger.directives, Level::Error, ""));
420 assert!(enabled(&logger.directives, Level::Warn, ""));
421 assert!(enabled(&logger.directives, Level::Info, ""));
422 assert!(enabled(&logger.directives, Level::Debug, ""));
423 assert!(enabled(&logger.directives, Level::Trace, ""));
424 }
425
426 #[test]
427 fn parse_default_bare_level_trace_uc() {
428 let logger = Builder::new().parse("TRACE").build();
429 assert!(enabled(&logger.directives, Level::Error, ""));
430 assert!(enabled(&logger.directives, Level::Warn, ""));
431 assert!(enabled(&logger.directives, Level::Info, ""));
432 assert!(enabled(&logger.directives, Level::Debug, ""));
433 assert!(enabled(&logger.directives, Level::Trace, ""));
434 }
435
436 // In practice, the desired log level is typically specified by a token
437 // that is either all lowercase (e.g., 'trace') or all uppercase (.e.g,
438 // 'TRACE'), but this tests serves as a reminder that
439 // log::Level::from_str() ignores all case variants.
440 #[test]
441 fn parse_default_bare_level_debug_mixed() {
442 {
443 let logger = Builder::new().parse("Debug").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 let logger = Builder::new().parse("debuG").build();
452 assert!(enabled(&logger.directives, Level::Error, ""));
453 assert!(enabled(&logger.directives, Level::Warn, ""));
454 assert!(enabled(&logger.directives, Level::Info, ""));
455 assert!(enabled(&logger.directives, Level::Debug, ""));
456 assert!(!enabled(&logger.directives, Level::Trace, ""));
457 }
458 {
459 let logger = Builder::new().parse("deBug").build();
460 assert!(enabled(&logger.directives, Level::Error, ""));
461 assert!(enabled(&logger.directives, Level::Warn, ""));
462 assert!(enabled(&logger.directives, Level::Info, ""));
463 assert!(enabled(&logger.directives, Level::Debug, ""));
464 assert!(!enabled(&logger.directives, Level::Trace, ""));
465 }
466 {
467 let logger = Builder::new().parse("DeBuG").build(); // LaTeX flavor!
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
476 #[test]
477 fn try_parse_valid_filter() {
478 let logger = Builder::new()
479 .try_parse("info,crate1::mod1=warn")
480 .expect("valid filter returned error")
481 .build();
482 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
483 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
484 }
485
486 #[test]
487 fn try_parse_invalid_filter() {
488 let error = Builder::new().try_parse("info,crate1=invalid").unwrap_err();
489 assert_data_eq!(
490 error,
491 str!["error parsing logger filter: invalid logging spec 'invalid'"]
492 );
493 }
494
495 #[test]
496 fn match_full_path() {
497 let logger = make_logger_filter(vec![
498 Directive {
499 name: Some("crate2".to_owned()),
500 level: LevelFilter::Info,
501 },
502 Directive {
503 name: Some("crate1::mod1".to_owned()),
504 level: LevelFilter::Warn,
505 },
506 ]);
507 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
508 assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
509 assert!(enabled(&logger.directives, Level::Info, "crate2"));
510 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
511 }
512
513 #[test]
514 fn no_match() {
515 let logger = make_logger_filter(vec![
516 Directive {
517 name: Some("crate2".to_owned()),
518 level: LevelFilter::Info,
519 },
520 Directive {
521 name: Some("crate1::mod1".to_owned()),
522 level: LevelFilter::Warn,
523 },
524 ]);
525 assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
526 }
527
528 #[test]
529 fn match_beginning() {
530 let logger = make_logger_filter(vec![
531 Directive {
532 name: Some("crate2".to_owned()),
533 level: LevelFilter::Info,
534 },
535 Directive {
536 name: Some("crate1::mod1".to_owned()),
537 level: LevelFilter::Warn,
538 },
539 ]);
540 assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
541 }
542
543 #[test]
544 fn match_beginning_longest_match() {
545 let logger = make_logger_filter(vec![
546 Directive {
547 name: Some("crate2".to_owned()),
548 level: LevelFilter::Info,
549 },
550 Directive {
551 name: Some("crate2::mod".to_owned()),
552 level: LevelFilter::Debug,
553 },
554 Directive {
555 name: Some("crate1::mod1".to_owned()),
556 level: LevelFilter::Warn,
557 },
558 ]);
559 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
560 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
561 }
562
563 #[test]
564 fn match_default() {
565 let logger = make_logger_filter(vec![
566 Directive {
567 name: None,
568 level: LevelFilter::Info,
569 },
570 Directive {
571 name: Some("crate1::mod1".to_owned()),
572 level: LevelFilter::Warn,
573 },
574 ]);
575 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
576 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
577 }
578
579 #[test]
580 fn zero_level() {
581 let logger = make_logger_filter(vec![
582 Directive {
583 name: None,
584 level: LevelFilter::Info,
585 },
586 Directive {
587 name: Some("crate1::mod1".to_owned()),
588 level: LevelFilter::Off,
589 },
590 ]);
591 assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
592 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
593 }
594}
595