1use std::{env, fmt, mem};
2
3use log::{LevelFilter, Metadata, Record};
4
5use crate::filter::{
6 Directive, DirectiveKind, FilterOp, KvFilter, ParseError, enabled, kv_filter::KvFilterOp,
7 parse_spec, parser::ParseResult,
8};
9
10pub struct Builder {
31 directives: Vec<Directive>,
32 kv_filters: Vec<KvFilter>,
33 filter: Option<FilterOp>,
34 built: bool,
35}
36
37impl Builder {
38 pub fn new() -> Builder {
40 Builder {
41 directives: Vec::new(),
42 kv_filters: Vec::new(),
43 filter: None,
44 built: false,
45 }
46 }
47
48 pub fn from_env(env: &str) -> Builder {
50 let mut builder = Builder::new();
51
52 if let Ok(s) = env::var(env) {
53 builder.parse(&s);
54 }
55
56 builder
57 }
58
59 fn insert_directive(&mut self, directive: Directive) {
61 for d in self.directives.iter_mut() {
62 if d.negated != directive.negated {
63 continue;
64 }
65 match &d.kind {
66 DirectiveKind::Any if matches!(&directive.kind, DirectiveKind::Any) => {
67 d.level = directive.level;
68 return;
69 }
70 DirectiveKind::Module { module } if matches!(&directive.kind, DirectiveKind::Module { module: m } if m == module) =>
71 {
72 d.level = directive.level;
73 return;
74 }
75 DirectiveKind::Component { component } if matches!(&directive.kind, DirectiveKind::Component { component: c } if c == component) =>
76 {
77 d.level = directive.level;
78 return;
79 }
80 DirectiveKind::Topic { component, topic } if matches!(&directive.kind, DirectiveKind::Topic { component: c, topic: t } if c == component && t == topic) =>
81 {
82 d.level = directive.level;
83 return;
84 }
85 _ => continue,
86 }
87 }
88 self.directives.push(directive);
89 }
90
91 pub fn filter_module(&mut self, module: &str, level: LevelFilter) -> &mut Self {
93 self.insert_directive(Directive {
94 kind: DirectiveKind::Module {
95 module: module.to_string(),
96 },
97 level,
98 negated: false,
99 });
100 self
101 }
102
103 pub fn filter_level(&mut self, level: LevelFilter) -> &mut Self {
105 self.insert_directive(Directive {
106 kind: DirectiveKind::Any,
107 level,
108 negated: false,
109 });
110 self
111 }
112
113 pub fn filter_component(
115 &mut self,
116 component: &str,
117 level: LevelFilter,
118 negated: bool,
119 ) -> &mut Self {
120 self.insert_directive(Directive {
121 kind: DirectiveKind::Component {
122 component: component.to_string(),
123 },
124 level,
125 negated,
126 });
127 self
128 }
129
130 pub fn filter_topic(
132 &mut self,
133 component: &str,
134 topic: &str,
135 level: LevelFilter,
136 negated: bool,
137 ) -> &mut Self {
138 self.insert_directive(Directive {
139 kind: DirectiveKind::Topic {
140 component: component.to_string(),
141 topic: FilterOp::new(topic).expect("invalid topic filter"),
142 },
143 level,
144 negated,
145 });
146 self
147 }
148
149 pub fn filter_key_value(&mut self, key: &str, value: &str, negated: bool) -> &mut Self {
151 for kv_filter in self.kv_filters.iter_mut() {
152 if kv_filter.key != key {
153 continue;
154 }
155
156 kv_filter.insert_value_filter(value, negated);
157 return self;
158 }
159
160 self.kv_filters.push(KvFilter {
161 key: key.to_owned(),
162 patterns: vec![KvFilterOp {
163 value: FilterOp::new(value).expect("invalid value filter"),
164 negated,
165 }],
166 });
167 self
168 }
169
170 pub fn parse(&mut self, filters: &str) -> &mut Self {
176 #![allow(clippy::print_stderr)] let ParseResult {
179 directives,
180 filter,
181 errors,
182 } = parse_spec(filters);
183
184 for error in errors {
185 eprintln!("warning: {error}, ignoring it");
186 }
187
188 self.filter = filter;
189
190 for directive in directives {
191 self.insert_directive(directive);
192 }
193 self
194 }
195
196 pub fn try_parse(&mut self, filters: &str) -> Result<&mut Self, ParseError> {
202 let (directives, filter) = parse_spec(filters).ok()?;
203
204 self.filter = filter;
205
206 for directive in directives {
207 self.insert_directive(directive);
208 }
209 Ok(self)
210 }
211
212 pub fn build(&mut self) -> Filter {
214 assert!(!self.built, "attempt to re-use consumed builder");
215 self.built = true;
216
217 let mut directives = Vec::new();
218 if self.directives.is_empty() {
219 directives.push(Directive {
221 kind: DirectiveKind::Any,
222 level: LevelFilter::Error,
223 negated: false,
224 });
225 } else {
226 directives = mem::take(&mut self.directives);
228 directives.sort();
230 }
231
232 Filter {
233 directives,
234 kv_filters: mem::take(&mut self.kv_filters),
235 filter: mem::take(&mut self.filter),
236 }
237 }
238}
239
240impl Default for Builder {
241 fn default() -> Self {
242 Builder::new()
243 }
244}
245
246impl fmt::Debug for Builder {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 if self.built {
249 f.debug_struct("Filter").field("built", &true).finish()
250 } else {
251 f.debug_struct("Filter")
252 .field("filter", &self.filter)
253 .field("kv_filters", &self.kv_filters)
254 .field("directives", &self.directives)
255 .finish()
256 }
257 }
258}
259
260#[derive(Clone)]
268pub struct Filter {
269 directives: Vec<Directive>,
270 kv_filters: Vec<KvFilter>,
271 filter: Option<FilterOp>,
272}
273
274impl Filter {
275 pub fn filter(&self) -> LevelFilter {
292 self.directives.iter().map(|d| d.level).max().unwrap_or(LevelFilter::Off)
293 }
294
295 pub fn matches(&self, record: &Record<'_>) -> bool {
297 let level = record.metadata().level();
298 let target = record.metadata().target();
299
300 let is_enabled = enabled(&self.directives, level, target);
301 if !is_enabled {
302 return false;
303 }
304
305 let mut was_matched = None;
309 let kv = record.key_values();
310 for kv_filter in self.kv_filters.iter() {
311 match kv_filter.matches(kv) {
312 None => continue,
313 Some(false) => return false,
314 Some(true) => {
315 was_matched = Some(true);
316 }
317 }
318 }
319
320 if was_matched.is_some_and(|matched| !matched) {
321 return false;
322 }
323
324 if let Some(filter) = self.filter.as_ref()
325 && !filter.is_match(&record.args().to_string())
326 {
327 return false;
328 }
329
330 true
331 }
332
333 pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
335 let level = metadata.level();
336 let target = metadata.target();
337
338 enabled(&self.directives, level, target)
339 }
340}
341
342impl fmt::Debug for Filter {
343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 f.debug_struct("Filter")
345 .field("filter", &self.filter)
346 .field("directives", &self.directives)
347 .finish()
348 }
349}
350
351#[cfg(test)]
352mod tests {
353 use log::{Level, LevelFilter};
354 use snapbox::{assert_data_eq, str};
355
356 use super::{Builder, Directive, DirectiveKind, Filter, enabled};
357
358 fn make_logger_filter(dirs: Vec<Directive>) -> Filter {
359 let mut logger = Builder::new().build();
360 logger.directives = dirs;
361 logger
362 }
363
364 #[test]
365 fn filter_info() {
366 let logger = Builder::new().filter_level(LevelFilter::Info).build();
367 assert!(enabled(&logger.directives, Level::Info, "crate1"));
368 assert!(!enabled(&logger.directives, Level::Debug, "crate1"));
369 }
370
371 #[test]
372 fn filter_beginning_longest_match() {
373 let logger = Builder::new()
374 .filter_module("crate2", LevelFilter::Info)
375 .filter_module("crate2::mod", LevelFilter::Debug)
376 .filter_module("crate1::mod1", LevelFilter::Warn)
377 .build();
378 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
379 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
380 }
381
382 #[test]
395 fn ensure_tests_cover_level_universe() {
396 let level_universe: Level = Level::Trace; match level_universe {
398 Level::Error | Level::Warn | Level::Info | Level::Debug | Level::Trace => (),
399 }
400 }
401
402 #[test]
403 fn parse_default() {
404 let logger = Builder::new().parse("info,crate1::mod1=warn").build();
405 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
406 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
407 }
408
409 #[test]
410 fn parse_default_bare_level_off_lc() {
411 let logger = Builder::new().parse("off").build();
412 assert!(!enabled(&logger.directives, Level::Error, ""));
413 assert!(!enabled(&logger.directives, Level::Warn, ""));
414 assert!(!enabled(&logger.directives, Level::Info, ""));
415 assert!(!enabled(&logger.directives, Level::Debug, ""));
416 assert!(!enabled(&logger.directives, Level::Trace, ""));
417 }
418
419 #[test]
420 fn parse_default_bare_level_off_uc() {
421 let logger = Builder::new().parse("OFF").build();
422 assert!(!enabled(&logger.directives, Level::Error, ""));
423 assert!(!enabled(&logger.directives, Level::Warn, ""));
424 assert!(!enabled(&logger.directives, Level::Info, ""));
425 assert!(!enabled(&logger.directives, Level::Debug, ""));
426 assert!(!enabled(&logger.directives, Level::Trace, ""));
427 }
428
429 #[test]
430 fn parse_default_bare_level_error_lc() {
431 let logger = Builder::new().parse("error").build();
432 assert!(enabled(&logger.directives, Level::Error, ""));
433 assert!(!enabled(&logger.directives, Level::Warn, ""));
434 assert!(!enabled(&logger.directives, Level::Info, ""));
435 assert!(!enabled(&logger.directives, Level::Debug, ""));
436 assert!(!enabled(&logger.directives, Level::Trace, ""));
437 }
438
439 #[test]
440 fn parse_default_bare_level_error_uc() {
441 let logger = Builder::new().parse("ERROR").build();
442 assert!(enabled(&logger.directives, Level::Error, ""));
443 assert!(!enabled(&logger.directives, Level::Warn, ""));
444 assert!(!enabled(&logger.directives, Level::Info, ""));
445 assert!(!enabled(&logger.directives, Level::Debug, ""));
446 assert!(!enabled(&logger.directives, Level::Trace, ""));
447 }
448
449 #[test]
450 fn parse_default_bare_level_warn_lc() {
451 let logger = Builder::new().parse("warn").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 #[test]
460 fn parse_default_bare_level_warn_uc() {
461 let logger = Builder::new().parse("WARN").build();
462 assert!(enabled(&logger.directives, Level::Error, ""));
463 assert!(enabled(&logger.directives, Level::Warn, ""));
464 assert!(!enabled(&logger.directives, Level::Info, ""));
465 assert!(!enabled(&logger.directives, Level::Debug, ""));
466 assert!(!enabled(&logger.directives, Level::Trace, ""));
467 }
468
469 #[test]
470 fn parse_default_bare_level_info_lc() {
471 let logger = Builder::new().parse("info").build();
472 assert!(enabled(&logger.directives, Level::Error, ""));
473 assert!(enabled(&logger.directives, Level::Warn, ""));
474 assert!(enabled(&logger.directives, Level::Info, ""));
475 assert!(!enabled(&logger.directives, Level::Debug, ""));
476 assert!(!enabled(&logger.directives, Level::Trace, ""));
477 }
478
479 #[test]
480 fn parse_default_bare_level_info_uc() {
481 let logger = Builder::new().parse("INFO").build();
482 assert!(enabled(&logger.directives, Level::Error, ""));
483 assert!(enabled(&logger.directives, Level::Warn, ""));
484 assert!(enabled(&logger.directives, Level::Info, ""));
485 assert!(!enabled(&logger.directives, Level::Debug, ""));
486 assert!(!enabled(&logger.directives, Level::Trace, ""));
487 }
488
489 #[test]
490 fn parse_default_bare_level_debug_lc() {
491 let logger = Builder::new().parse("debug").build();
492 assert!(enabled(&logger.directives, Level::Error, ""));
493 assert!(enabled(&logger.directives, Level::Warn, ""));
494 assert!(enabled(&logger.directives, Level::Info, ""));
495 assert!(enabled(&logger.directives, Level::Debug, ""));
496 assert!(!enabled(&logger.directives, Level::Trace, ""));
497 }
498
499 #[test]
500 fn parse_default_bare_level_debug_uc() {
501 let logger = Builder::new().parse("DEBUG").build();
502 assert!(enabled(&logger.directives, Level::Error, ""));
503 assert!(enabled(&logger.directives, Level::Warn, ""));
504 assert!(enabled(&logger.directives, Level::Info, ""));
505 assert!(enabled(&logger.directives, Level::Debug, ""));
506 assert!(!enabled(&logger.directives, Level::Trace, ""));
507 }
508
509 #[test]
510 fn parse_default_bare_level_trace_lc() {
511 let logger = Builder::new().parse("trace").build();
512 assert!(enabled(&logger.directives, Level::Error, ""));
513 assert!(enabled(&logger.directives, Level::Warn, ""));
514 assert!(enabled(&logger.directives, Level::Info, ""));
515 assert!(enabled(&logger.directives, Level::Debug, ""));
516 assert!(enabled(&logger.directives, Level::Trace, ""));
517 }
518
519 #[test]
520 fn parse_default_bare_level_trace_uc() {
521 let logger = Builder::new().parse("TRACE").build();
522 assert!(enabled(&logger.directives, Level::Error, ""));
523 assert!(enabled(&logger.directives, Level::Warn, ""));
524 assert!(enabled(&logger.directives, Level::Info, ""));
525 assert!(enabled(&logger.directives, Level::Debug, ""));
526 assert!(enabled(&logger.directives, Level::Trace, ""));
527 }
528
529 #[test]
534 fn parse_default_bare_level_debug_mixed() {
535 {
536 let logger = Builder::new().parse("Debug").build();
537 assert!(enabled(&logger.directives, Level::Error, ""));
538 assert!(enabled(&logger.directives, Level::Warn, ""));
539 assert!(enabled(&logger.directives, Level::Info, ""));
540 assert!(enabled(&logger.directives, Level::Debug, ""));
541 assert!(!enabled(&logger.directives, Level::Trace, ""));
542 }
543 {
544 let logger = Builder::new().parse("debuG").build();
545 assert!(enabled(&logger.directives, Level::Error, ""));
546 assert!(enabled(&logger.directives, Level::Warn, ""));
547 assert!(enabled(&logger.directives, Level::Info, ""));
548 assert!(enabled(&logger.directives, Level::Debug, ""));
549 assert!(!enabled(&logger.directives, Level::Trace, ""));
550 }
551 {
552 let logger = Builder::new().parse("deBug").build();
553 assert!(enabled(&logger.directives, Level::Error, ""));
554 assert!(enabled(&logger.directives, Level::Warn, ""));
555 assert!(enabled(&logger.directives, Level::Info, ""));
556 assert!(enabled(&logger.directives, Level::Debug, ""));
557 assert!(!enabled(&logger.directives, Level::Trace, ""));
558 }
559 {
560 let logger = Builder::new().parse("DeBuG").build(); assert!(enabled(&logger.directives, Level::Error, ""));
562 assert!(enabled(&logger.directives, Level::Warn, ""));
563 assert!(enabled(&logger.directives, Level::Info, ""));
564 assert!(enabled(&logger.directives, Level::Debug, ""));
565 assert!(!enabled(&logger.directives, Level::Trace, ""));
566 }
567 }
568
569 #[test]
570 fn try_parse_valid_filter() {
571 let logger = Builder::new()
572 .try_parse("info,crate1::mod1=warn")
573 .expect("valid filter returned error")
574 .build();
575 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
576 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
577 }
578
579 #[test]
580 fn try_parse_invalid_filter() {
581 let error = Builder::new().try_parse("info,crate1=invalid").unwrap_err();
582 assert_data_eq!(
583 error,
584 str![
585 "error parsing logger filter: invalid logging spec 'crate1=invalid': attempted to \
586 convert a string that doesn't match an existing log level"
587 ]
588 );
589 }
590
591 #[test]
592 fn match_full_path() {
593 let logger = make_logger_filter(vec![
594 Directive {
595 kind: DirectiveKind::Module {
596 module: "crate2".to_owned(),
597 },
598 level: LevelFilter::Info,
599 negated: false,
600 },
601 Directive {
602 kind: DirectiveKind::Module {
603 module: "crate1::mod1".to_owned(),
604 },
605 level: LevelFilter::Warn,
606 negated: false,
607 },
608 ]);
609 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
610 assert!(!enabled(&logger.directives, Level::Info, "crate1::mod1"));
611 assert!(enabled(&logger.directives, Level::Info, "crate2"));
612 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
613 }
614
615 #[test]
616 fn no_match() {
617 let logger = make_logger_filter(vec![
618 Directive {
619 kind: DirectiveKind::Module {
620 module: "crate2".to_owned(),
621 },
622 level: LevelFilter::Info,
623 negated: false,
624 },
625 Directive {
626 kind: DirectiveKind::Module {
627 module: "crate1::mod1".to_owned(),
628 },
629 level: LevelFilter::Warn,
630 negated: false,
631 },
632 ]);
633 assert!(!enabled(&logger.directives, Level::Warn, "crate3"));
634 }
635
636 #[test]
637 fn match_beginning() {
638 let logger = make_logger_filter(vec![
639 Directive {
640 kind: DirectiveKind::Module {
641 module: "crate2".to_owned(),
642 },
643 level: LevelFilter::Info,
644 negated: false,
645 },
646 Directive {
647 kind: DirectiveKind::Module {
648 module: "crate1::mod1".to_owned(),
649 },
650 level: LevelFilter::Warn,
651 negated: false,
652 },
653 ]);
654 assert!(enabled(&logger.directives, Level::Info, "crate2::mod1"));
655 }
656
657 #[test]
658 fn match_beginning_longest_match() {
659 let logger = make_logger_filter(vec![
660 Directive {
661 kind: DirectiveKind::Module {
662 module: "crate2".to_owned(),
663 },
664 level: LevelFilter::Info,
665 negated: false,
666 },
667 Directive {
668 kind: DirectiveKind::Module {
669 module: "crate2::mod".to_owned(),
670 },
671 level: LevelFilter::Debug,
672 negated: false,
673 },
674 Directive {
675 kind: DirectiveKind::Module {
676 module: "crate1::mod1".to_owned(),
677 },
678 level: LevelFilter::Warn,
679 negated: false,
680 },
681 ]);
682 assert!(enabled(&logger.directives, Level::Debug, "crate2::mod1"));
683 assert!(!enabled(&logger.directives, Level::Debug, "crate2"));
684 }
685
686 #[test]
687 fn match_default() {
688 let logger = make_logger_filter(vec![
689 Directive {
690 kind: DirectiveKind::Any,
691 level: LevelFilter::Info,
692 negated: false,
693 },
694 Directive {
695 kind: DirectiveKind::Module {
696 module: "crate1::mod1".to_owned(),
697 },
698 level: LevelFilter::Warn,
699 negated: false,
700 },
701 ]);
702 assert!(enabled(&logger.directives, Level::Warn, "crate1::mod1"));
703 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
704 }
705
706 #[test]
707 fn zero_level() {
708 let logger = make_logger_filter(vec![
709 Directive {
710 kind: DirectiveKind::Any,
711 level: LevelFilter::Info,
712 negated: false,
713 },
714 Directive {
715 kind: DirectiveKind::Module {
716 module: "crate1::mod1".to_owned(),
717 },
718 level: LevelFilter::Off,
719 negated: false,
720 },
721 ]);
722 assert!(!enabled(&logger.directives, Level::Error, "crate1::mod1"));
723 assert!(enabled(&logger.directives, Level::Info, "crate2::mod2"));
724 }
725}