1 pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective};
2 use crate::filter::{
3     directive::{DirectiveSet, Match},
4     env::{field, FieldMap},
5     level::LevelFilter,
6 };
7 use once_cell::sync::Lazy;
8 use regex::Regex;
9 use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr};
10 use tracing_core::{span, Level, Metadata};
11 
12 /// A single filtering directive.
13 // TODO(eliza): add a builder for programmatically constructing directives?
14 #[derive(Clone, Debug, Eq, PartialEq)]
15 #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))]
16 pub 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 
23 /// A set of dynamic filtering directives.
24 pub(super) type Dynamics = DirectiveSet<Directive>;
25 
26 /// A set of static filtering directives.
27 pub(super) type Statics = DirectiveSet<StaticDirective>;
28 
29 pub(crate) type CallsiteMatcher = MatchSet<field::CallsiteMatch>;
30 pub(crate) type SpanMatcher = MatchSet<field::SpanMatch>;
31 
32 #[derive(Debug, PartialEq, Eq)]
33 pub(crate) struct MatchSet<T> {
34     field_matches: FilterVec<T>,
35     base_level: LevelFilter,
36 }
37 
38 impl Directive {
has_name(&self) -> bool39     pub(super) fn has_name(&self) -> bool {
40         self.in_span.is_some()
41     }
42 
has_fields(&self) -> bool43     pub(super) fn has_fields(&self) -> bool {
44         !self.fields.is_empty()
45     }
46 
to_static(&self) -> Option<StaticDirective>47     pub(super) fn to_static(&self) -> Option<StaticDirective> {
48         if !self.is_static() {
49             return None;
50         }
51 
52         // TODO(eliza): these strings are all immutable; we should consider
53         // `Arc`ing them to make this more efficient...
54         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 
is_static(&self) -> bool63     fn is_static(&self) -> bool {
64         !self.has_name() && !self.fields.iter().any(field::Match::has_value)
65     }
66 
is_dynamic(&self) -> bool67     pub(super) fn is_dynamic(&self) -> bool {
68         self.has_name() || self.has_fields()
69     }
70 
field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch>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 
make_tables( directives: impl IntoIterator<Item = Directive>, ) -> (Dynamics, Statics)97     pub(super) fn make_tables(
98         directives: impl IntoIterator<Item = Directive>,
99     ) -> (Dynamics, Statics) {
100         // TODO(eliza): this could be made more efficient...
101         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 
deregexify(&mut self)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 
parse(from: &str, regex: bool) -> Result<Self, ParseError>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             // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
147             // characters. We should fix that.
148             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             // Setting the target without the level enables every level for that target
208             .unwrap_or(LevelFilter::TRACE);
209 
210         Ok(Self {
211             level,
212             target,
213             in_span,
214             fields: fields?,
215         })
216     }
217 }
218 
219 impl Match for Directive {
cares_about(&self, meta: &Metadata<'_>) -> bool220     fn cares_about(&self, meta: &Metadata<'_>) -> bool {
221         // Does this directive have a target filter, and does it match the
222         // metadata's target?
223         if let Some(ref target) = self.target {
224             if !meta.target().starts_with(&target[..]) {
225                 return false;
226             }
227         }
228 
229         // Do we have a name filter, and does it match the metadata's name?
230         // TODO(eliza): put name globbing here?
231         if let Some(ref name) = self.in_span {
232             if name != meta.name() {
233                 return false;
234             }
235         }
236 
237         // Does the metadata define all the fields that this directive cares about?
238         let actual_fields = meta.fields();
239         for expected_field in &self.fields {
240             // Does the actual field set (from the metadata) contain this field?
241             if actual_fields.field(&expected_field.name).is_none() {
242                 return false;
243             }
244         }
245 
246         true
247     }
248 
level(&self) -> &LevelFilter249     fn level(&self) -> &LevelFilter {
250         &self.level
251     }
252 }
253 
254 impl FromStr for Directive {
255     type Err = ParseError;
from_str(from: &str) -> Result<Self, Self::Err>256     fn from_str(from: &str) -> Result<Self, Self::Err> {
257         Directive::parse(from, true)
258     }
259 }
260 
261 impl Default for Directive {
default() -> Self262     fn default() -> Self {
263         Directive {
264             level: LevelFilter::OFF,
265             target: None,
266             in_span: None,
267             fields: Vec::new(),
268         }
269     }
270 }
271 
272 impl PartialOrd for Directive {
partial_cmp(&self, other: &Directive) -> Option<Ordering>273     fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
274         Some(self.cmp(other))
275     }
276 }
277 
278 impl Ord for Directive {
cmp(&self, other: &Directive) -> Ordering279     fn cmp(&self, other: &Directive) -> Ordering {
280         // We attempt to order directives by how "specific" they are. This
281         // ensures that we try the most specific directives first when
282         // attempting to match a piece of metadata.
283 
284         // First, we compare based on whether a target is specified, and the
285         // lengths of those targets if both have targets.
286         let ordering = self
287             .target
288             .as_ref()
289             .map(String::len)
290             .cmp(&other.target.as_ref().map(String::len))
291             // Next compare based on the presence of span names.
292             .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some()))
293             // Then we compare how many fields are defined by each
294             // directive.
295             .then_with(|| self.fields.len().cmp(&other.fields.len()))
296             // Finally, we fall back to lexicographical ordering if the directives are
297             // equally specific. Although this is no longer semantically important,
298             // we need to define a total ordering to determine the directive's place
299             // in the BTreeMap.
300             .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 
330 impl fmt::Display for Directive {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result331     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 
366 impl From<LevelFilter> for Directive {
from(level: LevelFilter) -> Self367     fn from(level: LevelFilter) -> Self {
368         Self {
369             level,
370             ..Self::default()
371         }
372     }
373 }
374 
375 impl From<Level> for Directive {
from(level: Level) -> Self376     fn from(level: Level) -> Self {
377         LevelFilter::from_level(level).into()
378     }
379 }
380 
381 // === impl Dynamics ===
382 
383 impl Dynamics {
matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher>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 
has_value_filters(&self) -> bool416     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 
422 // ===== impl DynamicMatch =====
423 
424 impl CallsiteMatcher {
425     /// Create a new `SpanMatch` for a given instance of the matched callsite.
to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher426     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 
443 impl SpanMatcher {
444     /// Returns the level currently enabled for this callsite.
level(&self) -> LevelFilter445     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 
record_update(&self, record: &span::Record<'_>)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)]
461 mod test {
462     use super::*;
463 
parse_directives(dirs: impl AsRef<str>) -> Vec<Directive>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 
expect_parse(dirs: impl AsRef<str>) -> Vec<Directive>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]
directive_ordering_by_target_len()482     fn directive_ordering_by_target_len() {
483         // TODO(eliza): it would be nice to have a property-based test for this
484         // instead.
485         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]
directive_ordering_by_span()504     fn directive_ordering_by_span() {
505         // TODO(eliza): it would be nice to have a property-based test for this
506         // instead.
507         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]
directive_ordering_uses_lexicographic_when_equal()520     fn directive_ordering_uses_lexicographic_when_equal() {
521         // TODO(eliza): it would be nice to have a property-based test for this
522         // instead.
523         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     // TODO: this test requires the parser to support directives with multiple
547     // fields, which it currently can't handle. We should enable this test when
548     // that's implemented.
549     #[test]
550     #[ignore]
directive_ordering_by_field_num()551     fn directive_ordering_by_field_num() {
552         // TODO(eliza): it would be nice to have a property-based test for this
553         // instead.
554         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]
parse_directives_ralith()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]
parse_directives_ralith_uc()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]
parse_directives_ralith_mixed()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]
parse_directives_valid()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 
parse_level_directives()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]
parse_uppercase_level_directives()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]
parse_numeric_level_directives()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]
parse_directives_invalid_crate()726     fn parse_directives_invalid_crate() {
727         // test parse_directives with multiple = in specification
728         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]
parse_directives_invalid_level()736     fn parse_directives_invalid_level() {
737         // test parse_directives with 'noNumber' as log level
738         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]
parse_directives_string_level()746     fn parse_directives_string_level() {
747         // test parse_directives with 'warn' as log level
748         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]
parse_directives_empty_level()756     fn parse_directives_empty_level() {
757         // test parse_directives with '' as log level
758         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]
parse_directives_global()766     fn parse_directives_global() {
767         // test parse_directives with no crate
768         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     // helper function for tests below
test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter)780     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]
parse_directives_global_bare_warn_lc()795     fn parse_directives_global_bare_warn_lc() {
796         // test parse_directives with no crate, in isolation, all lowercase
797         test_parse_bare_level("warn", LevelFilter::WARN);
798     }
799 
800     #[test]
parse_directives_global_bare_warn_uc()801     fn parse_directives_global_bare_warn_uc() {
802         // test parse_directives with no crate, in isolation, all uppercase
803         test_parse_bare_level("WARN", LevelFilter::WARN);
804     }
805 
806     #[test]
parse_directives_global_bare_warn_mixed()807     fn parse_directives_global_bare_warn_mixed() {
808         // test parse_directives with no crate, in isolation, mixed case
809         test_parse_bare_level("wArN", LevelFilter::WARN);
810     }
811 
812     #[test]
parse_directives_valid_with_spans()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]
parse_directives_with_dash_in_target_name()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]
parse_directives_with_dash_in_span_name()839     fn parse_directives_with_dash_in_span_name() {
840         // Reproduces https://github.com/tokio-rs/tracing/issues/1367
841 
842         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]
parse_directives_with_special_characters_in_span_name()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]
parse_directives_with_invalid_span_chars()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 }
868