1pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective};
2use crate::filter::{
3 directive::{DirectiveSet, Match},
4 env::{field, FieldMap},
5 level::LevelFilter,
6};
7use once_cell::sync::Lazy;
8use regex::Regex;
9use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
10use tracing_core::{span, Level, Metadata};
11
12#[derive(Clone, Debug, Eq, PartialEq)]
15#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
16pub struct Directive {
17 in_span: Option<String>,
18 fields: Vec<field::Match>,
19 pub(crate) target: Option<String>,
20 pub(crate) level: LevelFilter,
21}
22
23pub(super) type Dynamics = DirectiveSet<Directive>;
25
26pub(super) type Statics = DirectiveSet<StaticDirective>;
28
29pub(crate) type CallsiteMatcher = MatchSet<field::CallsiteMatch>;
30pub(crate) type SpanMatcher = MatchSet<field::SpanMatch>;
31
32#[derive(Debug, PartialEq, Eq)]
33pub(crate) struct MatchSet<T> {
34 field_matches: FilterVec<T>,
35 base_level: LevelFilter,
36}
37
38impl Directive {
39 pub(super) fn has_name(&self) -> bool {
40 self.in_span.is_some()
41 }
42
43 pub(super) fn has_fields(&self) -> bool {
44 !self.fields.is_empty()
45 }
46
47 pub(super) fn to_static(&self) -> Option<StaticDirective> {
48 if !self.is_static() {
49 return None;
50 }
51
52 let field_names = self.fields.iter().map(field::Match::name).collect();
55
56 Some(StaticDirective::new(
57 self.target.clone(),
58 field_names,
59 self.level,
60 ))
61 }
62
63 fn is_static(&self) -> bool {
64 !self.has_name() && !self.fields.iter().any(field::Match::has_value)
65 }
66
67 pub(super) fn is_dynamic(&self) -> bool {
68 self.has_name() || self.has_fields()
69 }
70
71 pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch> {
72 let fieldset = meta.fields();
73 let fields = self
74 .fields
75 .iter()
76 .filter_map(
77 |field::Match {
78 ref name,
79 ref value,
80 }| {
81 if let Some(field) = fieldset.field(name) {
82 let value = value.as_ref().cloned()?;
83 Some(Ok((field, value)))
84 } else {
85 Some(Err(()))
86 }
87 },
88 )
89 .collect::<Result<FieldMap<_>, ()>>()
90 .ok()?;
91 Some(field::CallsiteMatch {
92 fields,
93 level: self.level,
94 })
95 }
96
97 pub(super) fn make_tables(
98 directives: impl IntoIterator<Item = Directive>,
99 ) -> (Dynamics, Statics) {
100 let (dyns, stats): (Vec<Directive>, Vec<Directive>) =
102 directives.into_iter().partition(Directive::is_dynamic);
103 let statics = stats
104 .into_iter()
105 .filter_map(|d| d.to_static())
106 .chain(dyns.iter().filter_map(Directive::to_static))
107 .collect();
108 (Dynamics::from_iter(dyns), statics)
109 }
110
111 pub(super) fn deregexify(&mut self) {
112 for field in &mut self.fields {
113 field.value = match field.value.take() {
114 Some(field::ValueMatch::Pat(pat)) => {
115 Some(field::ValueMatch::Debug(pat.into_debug_match()))
116 }
117 x => x,
118 }
119 }
120 }
121
122 pub(super) fn parse(from: &str, regex: bool) -> Result<Self, ParseError> {
123 static DIRECTIVE_RE: Lazy<Regex> = Lazy::new(|| {
124 Regex::new(
125 r"(?x)
126 ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
127 # ^^^.
128 # `note: we match log level names case-insensitively
129 ^
130 (?: # target name or span name
131 (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
132 ){1,2}
133 (?: # level or nothing
134 =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
135 # ^^^.
136 # `note: we match log level names case-insensitively
137 )?
138 $
139 ",
140 )
141 .unwrap()
142 });
143 static SPAN_PART_RE: Lazy<Regex> =
144 Lazy::new(|| Regex::new(r"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?").unwrap());
145 static FIELD_FILTER_RE: Lazy<Regex> =
146 Lazy::new(|| {
149 Regex::new(
150 r"(?x)
151 (
152 # field name
153 [[:word:]][[[:word:]]\.]*
154 # value part (optional)
155 (?:=[^,]+)?
156 )
157 # trailing comma or EOS
158 (?:,\s?|$)
159 ",
160 )
161 .unwrap()
162 });
163
164 let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
165
166 if let Some(level) = caps
167 .name("global_level")
168 .and_then(|s| s.as_str().parse().ok())
169 {
170 return Ok(Directive {
171 level,
172 ..Default::default()
173 });
174 }
175
176 let target = caps.name("target").and_then(|c| {
177 let s = c.as_str();
178 if s.parse::<LevelFilter>().is_ok() {
179 None
180 } else {
181 Some(s.to_owned())
182 }
183 });
184
185 let (in_span, fields) = caps
186 .name("span")
187 .and_then(|cap| {
188 let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']');
189 let caps = SPAN_PART_RE.captures(cap)?;
190 let span = caps.name("name").map(|c| c.as_str().to_owned());
191 let fields = caps
192 .name("fields")
193 .map(|c| {
194 FIELD_FILTER_RE
195 .find_iter(c.as_str())
196 .map(|c| field::Match::parse(c.as_str(), regex))
197 .collect::<Result<Vec<_>, _>>()
198 })
199 .unwrap_or_else(|| Ok(Vec::new()));
200 Some((span, fields))
201 })
202 .unwrap_or_else(|| (None, Ok(Vec::new())));
203
204 let level = caps
205 .name("level")
206 .and_then(|l| l.as_str().parse().ok())
207 .unwrap_or(LevelFilter::TRACE);
209
210 Ok(Self {
211 level,
212 target,
213 in_span,
214 fields: fields?,
215 })
216 }
217}
218
219impl Match for Directive {
220 fn cares_about(&self, meta: &Metadata<'_>) -> bool {
221 if let Some(ref target) = self.target {
224 if !meta.target().starts_with(&target[..]) {
225 return false;
226 }
227 }
228
229 if let Some(ref name) = self.in_span {
232 if name != meta.name() {
233 return false;
234 }
235 }
236
237 let actual_fields = meta.fields();
239 for expected_field in &self.fields {
240 if actual_fields.field(&expected_field.name).is_none() {
242 return false;
243 }
244 }
245
246 true
247 }
248
249 fn level(&self) -> &LevelFilter {
250 &self.level
251 }
252}
253
254impl FromStr for Directive {
255 type Err = ParseError;
256 fn from_str(from: &str) -> Result<Self, Self::Err> {
257 Directive::parse(from, true)
258 }
259}
260
261impl Default for Directive {
262 fn default() -> Self {
263 Directive {
264 level: LevelFilter::OFF,
265 target: None,
266 in_span: None,
267 fields: Vec::new(),
268 }
269 }
270}
271
272impl PartialOrd for Directive {
273 fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
274 Some(self.cmp(other))
275 }
276}
277
278impl Ord for Directive {
279 fn cmp(&self, other: &Directive) -> Ordering {
280 let ordering = self
287 .target
288 .as_ref()
289 .map(String::len)
290 .cmp(&other.target.as_ref().map(String::len))
291 .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some()))
293 .then_with(|| self.fields.len().cmp(&other.fields.len()))
296 .then_with(|| {
301 self.target
302 .cmp(&other.target)
303 .then_with(|| self.in_span.cmp(&other.in_span))
304 .then_with(|| self.fields[..].cmp(&other.fields[..]))
305 })
306 .reverse();
307
308 #[cfg(debug_assertions)]
309 {
310 if ordering == Ordering::Equal {
311 debug_assert_eq!(
312 self.target, other.target,
313 "invariant violated: Ordering::Equal must imply a.target == b.target"
314 );
315 debug_assert_eq!(
316 self.in_span, other.in_span,
317 "invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
318 );
319 debug_assert_eq!(
320 self.fields, other.fields,
321 "invariant violated: Ordering::Equal must imply a.fields == b.fields"
322 );
323 }
324 }
325
326 ordering
327 }
328}
329
330impl fmt::Display for Directive {
331 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332 let mut wrote_any = false;
333 if let Some(ref target) = self.target {
334 fmt::Display::fmt(target, f)?;
335 wrote_any = true;
336 }
337
338 if self.in_span.is_some() || !self.fields.is_empty() {
339 f.write_str("[")?;
340
341 if let Some(ref span) = self.in_span {
342 fmt::Display::fmt(span, f)?;
343 }
344
345 let mut fields = self.fields.iter();
346 if let Some(field) = fields.next() {
347 write!(f, "{{{}", field)?;
348 for field in fields {
349 write!(f, ",{}", field)?;
350 }
351 f.write_str("}")?;
352 }
353
354 f.write_str("]")?;
355 wrote_any = true;
356 }
357
358 if wrote_any {
359 f.write_str("=")?;
360 }
361
362 fmt::Display::fmt(&self.level, f)
363 }
364}
365
366impl From<LevelFilter> for Directive {
367 fn from(level: LevelFilter) -> Self {
368 Self {
369 level,
370 ..Self::default()
371 }
372 }
373}
374
375impl From<Level> for Directive {
376 fn from(level: Level) -> Self {
377 LevelFilter::from_level(level).into()
378 }
379}
380
381impl Dynamics {
384 pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher> {
385 let mut base_level = None;
386 let field_matches = self
387 .directives_for(metadata)
388 .filter_map(|d| {
389 if let Some(f) = d.field_matcher(metadata) {
390 return Some(f);
391 }
392 match base_level {
393 Some(ref b) if d.level > *b => base_level = Some(d.level),
394 None => base_level = Some(d.level),
395 _ => {}
396 }
397 None
398 })
399 .collect();
400
401 if let Some(base_level) = base_level {
402 Some(CallsiteMatcher {
403 field_matches,
404 base_level,
405 })
406 } else if !field_matches.is_empty() {
407 Some(CallsiteMatcher {
408 field_matches,
409 base_level: base_level.unwrap_or(LevelFilter::OFF),
410 })
411 } else {
412 None
413 }
414 }
415
416 pub(crate) fn has_value_filters(&self) -> bool {
417 self.directives()
418 .any(|d| d.fields.iter().any(|f| f.value.is_some()))
419 }
420}
421
422impl CallsiteMatcher {
425 pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher {
427 let field_matches = self
428 .field_matches
429 .iter()
430 .map(|m| {
431 let m = m.to_span_match();
432 attrs.record(&mut m.visitor());
433 m
434 })
435 .collect();
436 SpanMatcher {
437 field_matches,
438 base_level: self.base_level,
439 }
440 }
441}
442
443impl SpanMatcher {
444 pub(crate) fn level(&self) -> LevelFilter {
446 self.field_matches
447 .iter()
448 .filter_map(field::SpanMatch::filter)
449 .max()
450 .unwrap_or(self.base_level)
451 }
452
453 pub(crate) fn record_update(&self, record: &span::Record<'_>) {
454 for m in &self.field_matches {
455 record.record(&mut m.visitor())
456 }
457 }
458}
459
460#[cfg(test)]
461mod test {
462 use super::*;
463
464 fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> {
465 dirs.as_ref()
466 .split(',')
467 .filter_map(|s| s.parse().ok())
468 .collect()
469 }
470
471 fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> {
472 dirs.as_ref()
473 .split(',')
474 .map(|s| {
475 s.parse()
476 .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err))
477 })
478 .collect()
479 }
480
481 #[test]
482 fn directive_ordering_by_target_len() {
483 let mut dirs = expect_parse(
486 "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
487 );
488 dirs.sort_unstable();
489
490 let expected = vec![
491 "a_really_long_name_with_no_colons",
492 "foo::bar::baz",
493 "foo::bar",
494 "foo",
495 ];
496 let sorted = dirs
497 .iter()
498 .map(|d| d.target.as_ref().unwrap())
499 .collect::<Vec<_>>();
500
501 assert_eq!(expected, sorted);
502 }
503 #[test]
504 fn directive_ordering_by_span() {
505 let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
508 dirs.sort_unstable();
509
510 let expected = vec!["baz::quux", "bar", "foo", "a"];
511 let sorted = dirs
512 .iter()
513 .map(|d| d.target.as_ref().unwrap())
514 .collect::<Vec<_>>();
515
516 assert_eq!(expected, sorted);
517 }
518
519 #[test]
520 fn directive_ordering_uses_lexicographic_when_equal() {
521 let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
524 dirs.sort_unstable();
525
526 let expected = vec![
527 ("span", Some("b")),
528 ("span", Some("a")),
529 ("c", None),
530 ("b", None),
531 ("a", None),
532 ];
533 let sorted = dirs
534 .iter()
535 .map(|d| {
536 (
537 d.target.as_ref().unwrap().as_ref(),
538 d.in_span.as_ref().map(String::as_ref),
539 )
540 })
541 .collect::<Vec<_>>();
542
543 assert_eq!(expected, sorted);
544 }
545
546 #[test]
550 #[ignore]
551 fn directive_ordering_by_field_num() {
552 let mut dirs = expect_parse(
555 "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info"
556 );
557 dirs.sort_unstable();
558
559 let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"];
560 let sorted = dirs
561 .iter()
562 .map(|d| d.target.as_ref().unwrap())
563 .collect::<Vec<_>>();
564
565 assert_eq!(expected, sorted);
566 }
567
568 #[test]
569 fn parse_directives_ralith() {
570 let dirs = parse_directives("common=trace,server=trace");
571 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
572 assert_eq!(dirs[0].target, Some("common".to_string()));
573 assert_eq!(dirs[0].level, LevelFilter::TRACE);
574 assert_eq!(dirs[0].in_span, None);
575
576 assert_eq!(dirs[1].target, Some("server".to_string()));
577 assert_eq!(dirs[1].level, LevelFilter::TRACE);
578 assert_eq!(dirs[1].in_span, None);
579 }
580
581 #[test]
582 fn parse_directives_ralith_uc() {
583 let dirs = parse_directives("common=INFO,server=DEBUG");
584 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
585 assert_eq!(dirs[0].target, Some("common".to_string()));
586 assert_eq!(dirs[0].level, LevelFilter::INFO);
587 assert_eq!(dirs[0].in_span, None);
588
589 assert_eq!(dirs[1].target, Some("server".to_string()));
590 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
591 assert_eq!(dirs[1].in_span, None);
592 }
593
594 #[test]
595 fn parse_directives_ralith_mixed() {
596 let dirs = parse_directives("common=iNfo,server=dEbUg");
597 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
598 assert_eq!(dirs[0].target, Some("common".to_string()));
599 assert_eq!(dirs[0].level, LevelFilter::INFO);
600 assert_eq!(dirs[0].in_span, None);
601
602 assert_eq!(dirs[1].target, Some("server".to_string()));
603 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
604 assert_eq!(dirs[1].in_span, None);
605 }
606
607 #[test]
608 fn parse_directives_valid() {
609 let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
610 assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
611 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
612 assert_eq!(dirs[0].level, LevelFilter::ERROR);
613 assert_eq!(dirs[0].in_span, None);
614
615 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
616 assert_eq!(dirs[1].level, LevelFilter::TRACE);
617 assert_eq!(dirs[1].in_span, None);
618
619 assert_eq!(dirs[2].target, Some("crate2".to_string()));
620 assert_eq!(dirs[2].level, LevelFilter::DEBUG);
621 assert_eq!(dirs[2].in_span, None);
622
623 assert_eq!(dirs[3].target, Some("crate3".to_string()));
624 assert_eq!(dirs[3].level, LevelFilter::OFF);
625 assert_eq!(dirs[3].in_span, None);
626 }
627
628 #[test]
629
630 fn parse_level_directives() {
631 let dirs = parse_directives(
632 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
633 crate2=debug,crate3=trace,crate3::mod2::mod1=off",
634 );
635 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
636 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
637 assert_eq!(dirs[0].level, LevelFilter::ERROR);
638 assert_eq!(dirs[0].in_span, None);
639
640 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
641 assert_eq!(dirs[1].level, LevelFilter::WARN);
642 assert_eq!(dirs[1].in_span, None);
643
644 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
645 assert_eq!(dirs[2].level, LevelFilter::INFO);
646 assert_eq!(dirs[2].in_span, None);
647
648 assert_eq!(dirs[3].target, Some("crate2".to_string()));
649 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
650 assert_eq!(dirs[3].in_span, None);
651
652 assert_eq!(dirs[4].target, Some("crate3".to_string()));
653 assert_eq!(dirs[4].level, LevelFilter::TRACE);
654 assert_eq!(dirs[4].in_span, None);
655
656 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
657 assert_eq!(dirs[5].level, LevelFilter::OFF);
658 assert_eq!(dirs[5].in_span, None);
659 }
660
661 #[test]
662 fn parse_uppercase_level_directives() {
663 let dirs = parse_directives(
664 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
665 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
666 );
667 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
668 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
669 assert_eq!(dirs[0].level, LevelFilter::ERROR);
670 assert_eq!(dirs[0].in_span, None);
671
672 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
673 assert_eq!(dirs[1].level, LevelFilter::WARN);
674 assert_eq!(dirs[1].in_span, None);
675
676 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
677 assert_eq!(dirs[2].level, LevelFilter::INFO);
678 assert_eq!(dirs[2].in_span, None);
679
680 assert_eq!(dirs[3].target, Some("crate2".to_string()));
681 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
682 assert_eq!(dirs[3].in_span, None);
683
684 assert_eq!(dirs[4].target, Some("crate3".to_string()));
685 assert_eq!(dirs[4].level, LevelFilter::TRACE);
686 assert_eq!(dirs[4].in_span, None);
687
688 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
689 assert_eq!(dirs[5].level, LevelFilter::OFF);
690 assert_eq!(dirs[5].in_span, None);
691 }
692
693 #[test]
694 fn parse_numeric_level_directives() {
695 let dirs = parse_directives(
696 "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
697 crate3=5,crate3::mod2::mod1=0",
698 );
699 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
700 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
701 assert_eq!(dirs[0].level, LevelFilter::ERROR);
702 assert_eq!(dirs[0].in_span, None);
703
704 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
705 assert_eq!(dirs[1].level, LevelFilter::WARN);
706 assert_eq!(dirs[1].in_span, None);
707
708 assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
709 assert_eq!(dirs[2].level, LevelFilter::INFO);
710 assert_eq!(dirs[2].in_span, None);
711
712 assert_eq!(dirs[3].target, Some("crate2".to_string()));
713 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
714 assert_eq!(dirs[3].in_span, None);
715
716 assert_eq!(dirs[4].target, Some("crate3".to_string()));
717 assert_eq!(dirs[4].level, LevelFilter::TRACE);
718 assert_eq!(dirs[4].in_span, None);
719
720 assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
721 assert_eq!(dirs[5].level, LevelFilter::OFF);
722 assert_eq!(dirs[5].in_span, None);
723 }
724
725 #[test]
726 fn parse_directives_invalid_crate() {
727 let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug");
729 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
730 assert_eq!(dirs[0].target, Some("crate2".to_string()));
731 assert_eq!(dirs[0].level, LevelFilter::DEBUG);
732 assert_eq!(dirs[0].in_span, None);
733 }
734
735 #[test]
736 fn parse_directives_invalid_level() {
737 let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug");
739 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
740 assert_eq!(dirs[0].target, Some("crate2".to_string()));
741 assert_eq!(dirs[0].level, LevelFilter::DEBUG);
742 assert_eq!(dirs[0].in_span, None);
743 }
744
745 #[test]
746 fn parse_directives_string_level() {
747 let dirs = parse_directives("crate1::mod1=wrong,crate2=warn");
749 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
750 assert_eq!(dirs[0].target, Some("crate2".to_string()));
751 assert_eq!(dirs[0].level, LevelFilter::WARN);
752 assert_eq!(dirs[0].in_span, None);
753 }
754
755 #[test]
756 fn parse_directives_empty_level() {
757 let dirs = parse_directives("crate1::mod1=wrong,crate2=");
759 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
760 assert_eq!(dirs[0].target, Some("crate2".to_string()));
761 assert_eq!(dirs[0].level, LevelFilter::TRACE);
762 assert_eq!(dirs[0].in_span, None);
763 }
764
765 #[test]
766 fn parse_directives_global() {
767 let dirs = parse_directives("warn,crate2=debug");
769 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
770 assert_eq!(dirs[0].target, None);
771 assert_eq!(dirs[0].level, LevelFilter::WARN);
772 assert_eq!(dirs[1].in_span, None);
773
774 assert_eq!(dirs[1].target, Some("crate2".to_string()));
775 assert_eq!(dirs[1].level, LevelFilter::DEBUG);
776 assert_eq!(dirs[1].in_span, None);
777 }
778
779 fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) {
781 let dirs = parse_directives(directive_to_test);
782 assert_eq!(
783 dirs.len(),
784 1,
785 "\ninput: \"{}\"; parsed: {:#?}",
786 directive_to_test,
787 dirs
788 );
789 assert_eq!(dirs[0].target, None);
790 assert_eq!(dirs[0].level, level_expected);
791 assert_eq!(dirs[0].in_span, None);
792 }
793
794 #[test]
795 fn parse_directives_global_bare_warn_lc() {
796 test_parse_bare_level("warn", LevelFilter::WARN);
798 }
799
800 #[test]
801 fn parse_directives_global_bare_warn_uc() {
802 test_parse_bare_level("WARN", LevelFilter::WARN);
804 }
805
806 #[test]
807 fn parse_directives_global_bare_warn_mixed() {
808 test_parse_bare_level("wArN", LevelFilter::WARN);
810 }
811
812 #[test]
813 fn parse_directives_valid_with_spans() {
814 let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug");
815 assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs);
816 assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
817 assert_eq!(dirs[0].level, LevelFilter::ERROR);
818 assert_eq!(dirs[0].in_span, Some("foo".to_string()));
819
820 assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
821 assert_eq!(dirs[1].level, LevelFilter::TRACE);
822 assert_eq!(dirs[1].in_span, Some("bar".to_string()));
823
824 assert_eq!(dirs[2].target, Some("crate2".to_string()));
825 assert_eq!(dirs[2].level, LevelFilter::DEBUG);
826 assert_eq!(dirs[2].in_span, Some("baz".to_string()));
827 }
828
829 #[test]
830 fn parse_directives_with_dash_in_target_name() {
831 let dirs = parse_directives("target-name=info");
832 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
833 assert_eq!(dirs[0].target, Some("target-name".to_string()));
834 assert_eq!(dirs[0].level, LevelFilter::INFO);
835 assert_eq!(dirs[0].in_span, None);
836 }
837
838 #[test]
839 fn parse_directives_with_dash_in_span_name() {
840 let dirs = parse_directives("target[span-name]=info");
843 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
844 assert_eq!(dirs[0].target, Some("target".to_string()));
845 assert_eq!(dirs[0].level, LevelFilter::INFO);
846 assert_eq!(dirs[0].in_span, Some("span-name".to_string()));
847 }
848
849 #[test]
850 fn parse_directives_with_special_characters_in_span_name() {
851 let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
852
853 let dirs = parse_directives(format!("target[{}]=info", span_name));
854 assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
855 assert_eq!(dirs[0].target, Some("target".to_string()));
856 assert_eq!(dirs[0].level, LevelFilter::INFO);
857 assert_eq!(dirs[0].in_span, Some(span_name.to_string()));
858 }
859
860 #[test]
861 fn parse_directives_with_invalid_span_chars() {
862 let invalid_span_name = "]{";
863
864 let dirs = parse_directives(format!("target[{}]=info", invalid_span_name));
865 assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs);
866 }
867}