• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(|| Regex::new(
124             r"(?x)
125             ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
126                 #                 ^^^.
127                 #                     `note: we match log level names case-insensitively
128             ^
129             (?: # target name or span name
130                 (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
131             ){1,2}
132             (?: # level or nothing
133                 =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
134                     #          ^^^.
135                     #              `note: we match log level names case-insensitively
136             )?
137             $
138             "
139         )
140         .unwrap());
141         static SPAN_PART_RE: Lazy<Regex> =
142             Lazy::new(|| Regex::new(r#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap());
143         static FIELD_FILTER_RE: Lazy<Regex> =
144             // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
145             // characters. We should fix that.
146             Lazy::new(|| Regex::new(r#"(?x)
147                 (
148                     # field name
149                     [[:word:]][[[:word:]]\.]*
150                     # value part (optional)
151                     (?:=[^,]+)?
152                 )
153                 # trailing comma or EOS
154                 (?:,\s?|$)
155             "#).unwrap());
156 
157         let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
158 
159         if let Some(level) = caps
160             .name("global_level")
161             .and_then(|s| s.as_str().parse().ok())
162         {
163             return Ok(Directive {
164                 level,
165                 ..Default::default()
166             });
167         }
168 
169         let target = caps.name("target").and_then(|c| {
170             let s = c.as_str();
171             if s.parse::<LevelFilter>().is_ok() {
172                 None
173             } else {
174                 Some(s.to_owned())
175             }
176         });
177 
178         let (in_span, fields) = caps
179             .name("span")
180             .and_then(|cap| {
181                 let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']');
182                 let caps = SPAN_PART_RE.captures(cap)?;
183                 let span = caps.name("name").map(|c| c.as_str().to_owned());
184                 let fields = caps
185                     .name("fields")
186                     .map(|c| {
187                         FIELD_FILTER_RE
188                             .find_iter(c.as_str())
189                             .map(|c| field::Match::parse(c.as_str(), regex))
190                             .collect::<Result<Vec<_>, _>>()
191                     })
192                     .unwrap_or_else(|| Ok(Vec::new()));
193                 Some((span, fields))
194             })
195             .unwrap_or_else(|| (None, Ok(Vec::new())));
196 
197         let level = caps
198             .name("level")
199             .and_then(|l| l.as_str().parse().ok())
200             // Setting the target without the level enables every level for that target
201             .unwrap_or(LevelFilter::TRACE);
202 
203         Ok(Self {
204             level,
205             target,
206             in_span,
207             fields: fields?,
208         })
209     }
210 }
211 
212 impl Match for Directive {
cares_about(&self, meta: &Metadata<'_>) -> bool213     fn cares_about(&self, meta: &Metadata<'_>) -> bool {
214         // Does this directive have a target filter, and does it match the
215         // metadata's target?
216         if let Some(ref target) = self.target {
217             if !meta.target().starts_with(&target[..]) {
218                 return false;
219             }
220         }
221 
222         // Do we have a name filter, and does it match the metadata's name?
223         // TODO(eliza): put name globbing here?
224         if let Some(ref name) = self.in_span {
225             if name != meta.name() {
226                 return false;
227             }
228         }
229 
230         // Does the metadata define all the fields that this directive cares about?
231         let actual_fields = meta.fields();
232         for expected_field in &self.fields {
233             // Does the actual field set (from the metadata) contain this field?
234             if actual_fields.field(&expected_field.name).is_none() {
235                 return false;
236             }
237         }
238 
239         true
240     }
241 
level(&self) -> &LevelFilter242     fn level(&self) -> &LevelFilter {
243         &self.level
244     }
245 }
246 
247 impl FromStr for Directive {
248     type Err = ParseError;
from_str(from: &str) -> Result<Self, Self::Err>249     fn from_str(from: &str) -> Result<Self, Self::Err> {
250         Directive::parse(from, true)
251     }
252 }
253 
254 impl Default for Directive {
default() -> Self255     fn default() -> Self {
256         Directive {
257             level: LevelFilter::OFF,
258             target: None,
259             in_span: None,
260             fields: Vec::new(),
261         }
262     }
263 }
264 
265 impl PartialOrd for Directive {
partial_cmp(&self, other: &Directive) -> Option<Ordering>266     fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
267         Some(self.cmp(other))
268     }
269 }
270 
271 impl Ord for Directive {
cmp(&self, other: &Directive) -> Ordering272     fn cmp(&self, other: &Directive) -> Ordering {
273         // We attempt to order directives by how "specific" they are. This
274         // ensures that we try the most specific directives first when
275         // attempting to match a piece of metadata.
276 
277         // First, we compare based on whether a target is specified, and the
278         // lengths of those targets if both have targets.
279         let ordering = self
280             .target
281             .as_ref()
282             .map(String::len)
283             .cmp(&other.target.as_ref().map(String::len))
284             // Next compare based on the presence of span names.
285             .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some()))
286             // Then we compare how many fields are defined by each
287             // directive.
288             .then_with(|| self.fields.len().cmp(&other.fields.len()))
289             // Finally, we fall back to lexicographical ordering if the directives are
290             // equally specific. Although this is no longer semantically important,
291             // we need to define a total ordering to determine the directive's place
292             // in the BTreeMap.
293             .then_with(|| {
294                 self.target
295                     .cmp(&other.target)
296                     .then_with(|| self.in_span.cmp(&other.in_span))
297                     .then_with(|| self.fields[..].cmp(&other.fields[..]))
298             })
299             .reverse();
300 
301         #[cfg(debug_assertions)]
302         {
303             if ordering == Ordering::Equal {
304                 debug_assert_eq!(
305                     self.target, other.target,
306                     "invariant violated: Ordering::Equal must imply a.target == b.target"
307                 );
308                 debug_assert_eq!(
309                     self.in_span, other.in_span,
310                     "invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
311                 );
312                 debug_assert_eq!(
313                     self.fields, other.fields,
314                     "invariant violated: Ordering::Equal must imply a.fields == b.fields"
315                 );
316             }
317         }
318 
319         ordering
320     }
321 }
322 
323 impl fmt::Display for Directive {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result324     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325         let mut wrote_any = false;
326         if let Some(ref target) = self.target {
327             fmt::Display::fmt(target, f)?;
328             wrote_any = true;
329         }
330 
331         if self.in_span.is_some() || !self.fields.is_empty() {
332             f.write_str("[")?;
333 
334             if let Some(ref span) = self.in_span {
335                 fmt::Display::fmt(span, f)?;
336             }
337 
338             let mut fields = self.fields.iter();
339             if let Some(field) = fields.next() {
340                 write!(f, "{{{}", field)?;
341                 for field in fields {
342                     write!(f, ",{}", field)?;
343                 }
344                 f.write_str("}")?;
345             }
346 
347             f.write_str("]")?;
348             wrote_any = true;
349         }
350 
351         if wrote_any {
352             f.write_str("=")?;
353         }
354 
355         fmt::Display::fmt(&self.level, f)
356     }
357 }
358 
359 impl From<LevelFilter> for Directive {
from(level: LevelFilter) -> Self360     fn from(level: LevelFilter) -> Self {
361         Self {
362             level,
363             ..Self::default()
364         }
365     }
366 }
367 
368 impl From<Level> for Directive {
from(level: Level) -> Self369     fn from(level: Level) -> Self {
370         LevelFilter::from_level(level).into()
371     }
372 }
373 
374 // === impl Dynamics ===
375 
376 impl Dynamics {
matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher>377     pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option<CallsiteMatcher> {
378         let mut base_level = None;
379         let field_matches = self
380             .directives_for(metadata)
381             .filter_map(|d| {
382                 if let Some(f) = d.field_matcher(metadata) {
383                     return Some(f);
384                 }
385                 match base_level {
386                     Some(ref b) if d.level > *b => base_level = Some(d.level),
387                     None => base_level = Some(d.level),
388                     _ => {}
389                 }
390                 None
391             })
392             .collect();
393 
394         if let Some(base_level) = base_level {
395             Some(CallsiteMatcher {
396                 field_matches,
397                 base_level,
398             })
399         } else if !field_matches.is_empty() {
400             Some(CallsiteMatcher {
401                 field_matches,
402                 base_level: base_level.unwrap_or(LevelFilter::OFF),
403             })
404         } else {
405             None
406         }
407     }
408 
has_value_filters(&self) -> bool409     pub(crate) fn has_value_filters(&self) -> bool {
410         self.directives()
411             .any(|d| d.fields.iter().any(|f| f.value.is_some()))
412     }
413 }
414 
415 // ===== impl DynamicMatch =====
416 
417 impl CallsiteMatcher {
418     /// Create a new `SpanMatch` for a given instance of the matched callsite.
to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher419     pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher {
420         let field_matches = self
421             .field_matches
422             .iter()
423             .map(|m| {
424                 let m = m.to_span_match();
425                 attrs.record(&mut m.visitor());
426                 m
427             })
428             .collect();
429         SpanMatcher {
430             field_matches,
431             base_level: self.base_level,
432         }
433     }
434 }
435 
436 impl SpanMatcher {
437     /// Returns the level currently enabled for this callsite.
level(&self) -> LevelFilter438     pub(crate) fn level(&self) -> LevelFilter {
439         self.field_matches
440             .iter()
441             .filter_map(field::SpanMatch::filter)
442             .max()
443             .unwrap_or(self.base_level)
444     }
445 
record_update(&self, record: &span::Record<'_>)446     pub(crate) fn record_update(&self, record: &span::Record<'_>) {
447         for m in &self.field_matches {
448             record.record(&mut m.visitor())
449         }
450     }
451 }
452 
453 #[cfg(test)]
454 mod test {
455     use super::*;
456 
parse_directives(dirs: impl AsRef<str>) -> Vec<Directive>457     fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> {
458         dirs.as_ref()
459             .split(',')
460             .filter_map(|s| s.parse().ok())
461             .collect()
462     }
463 
expect_parse(dirs: impl AsRef<str>) -> Vec<Directive>464     fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> {
465         dirs.as_ref()
466             .split(',')
467             .map(|s| {
468                 s.parse()
469                     .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err))
470             })
471             .collect()
472     }
473 
474     #[test]
directive_ordering_by_target_len()475     fn directive_ordering_by_target_len() {
476         // TODO(eliza): it would be nice to have a property-based test for this
477         // instead.
478         let mut dirs = expect_parse(
479             "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
480         );
481         dirs.sort_unstable();
482 
483         let expected = vec![
484             "a_really_long_name_with_no_colons",
485             "foo::bar::baz",
486             "foo::bar",
487             "foo",
488         ];
489         let sorted = dirs
490             .iter()
491             .map(|d| d.target.as_ref().unwrap())
492             .collect::<Vec<_>>();
493 
494         assert_eq!(expected, sorted);
495     }
496     #[test]
directive_ordering_by_span()497     fn directive_ordering_by_span() {
498         // TODO(eliza): it would be nice to have a property-based test for this
499         // instead.
500         let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
501         dirs.sort_unstable();
502 
503         let expected = vec!["baz::quux", "bar", "foo", "a"];
504         let sorted = dirs
505             .iter()
506             .map(|d| d.target.as_ref().unwrap())
507             .collect::<Vec<_>>();
508 
509         assert_eq!(expected, sorted);
510     }
511 
512     #[test]
directive_ordering_uses_lexicographic_when_equal()513     fn directive_ordering_uses_lexicographic_when_equal() {
514         // TODO(eliza): it would be nice to have a property-based test for this
515         // instead.
516         let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
517         dirs.sort_unstable();
518 
519         let expected = vec![
520             ("span", Some("b")),
521             ("span", Some("a")),
522             ("c", None),
523             ("b", None),
524             ("a", None),
525         ];
526         let sorted = dirs
527             .iter()
528             .map(|d| {
529                 (
530                     d.target.as_ref().unwrap().as_ref(),
531                     d.in_span.as_ref().map(String::as_ref),
532                 )
533             })
534             .collect::<Vec<_>>();
535 
536         assert_eq!(expected, sorted);
537     }
538 
539     // TODO: this test requires the parser to support directives with multiple
540     // fields, which it currently can't handle. We should enable this test when
541     // that's implemented.
542     #[test]
543     #[ignore]
directive_ordering_by_field_num()544     fn directive_ordering_by_field_num() {
545         // TODO(eliza): it would be nice to have a property-based test for this
546         // instead.
547         let mut dirs = expect_parse(
548             "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info"
549         );
550         dirs.sort_unstable();
551 
552         let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"];
553         let sorted = dirs
554             .iter()
555             .map(|d| d.target.as_ref().unwrap())
556             .collect::<Vec<_>>();
557 
558         assert_eq!(expected, sorted);
559     }
560 
561     #[test]
parse_directives_ralith()562     fn parse_directives_ralith() {
563         let dirs = parse_directives("common=trace,server=trace");
564         assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
565         assert_eq!(dirs[0].target, Some("common".to_string()));
566         assert_eq!(dirs[0].level, LevelFilter::TRACE);
567         assert_eq!(dirs[0].in_span, None);
568 
569         assert_eq!(dirs[1].target, Some("server".to_string()));
570         assert_eq!(dirs[1].level, LevelFilter::TRACE);
571         assert_eq!(dirs[1].in_span, None);
572     }
573 
574     #[test]
parse_directives_ralith_uc()575     fn parse_directives_ralith_uc() {
576         let dirs = parse_directives("common=INFO,server=DEBUG");
577         assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
578         assert_eq!(dirs[0].target, Some("common".to_string()));
579         assert_eq!(dirs[0].level, LevelFilter::INFO);
580         assert_eq!(dirs[0].in_span, None);
581 
582         assert_eq!(dirs[1].target, Some("server".to_string()));
583         assert_eq!(dirs[1].level, LevelFilter::DEBUG);
584         assert_eq!(dirs[1].in_span, None);
585     }
586 
587     #[test]
parse_directives_ralith_mixed()588     fn parse_directives_ralith_mixed() {
589         let dirs = parse_directives("common=iNfo,server=dEbUg");
590         assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
591         assert_eq!(dirs[0].target, Some("common".to_string()));
592         assert_eq!(dirs[0].level, LevelFilter::INFO);
593         assert_eq!(dirs[0].in_span, None);
594 
595         assert_eq!(dirs[1].target, Some("server".to_string()));
596         assert_eq!(dirs[1].level, LevelFilter::DEBUG);
597         assert_eq!(dirs[1].in_span, None);
598     }
599 
600     #[test]
parse_directives_valid()601     fn parse_directives_valid() {
602         let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
603         assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
604         assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
605         assert_eq!(dirs[0].level, LevelFilter::ERROR);
606         assert_eq!(dirs[0].in_span, None);
607 
608         assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
609         assert_eq!(dirs[1].level, LevelFilter::TRACE);
610         assert_eq!(dirs[1].in_span, None);
611 
612         assert_eq!(dirs[2].target, Some("crate2".to_string()));
613         assert_eq!(dirs[2].level, LevelFilter::DEBUG);
614         assert_eq!(dirs[2].in_span, None);
615 
616         assert_eq!(dirs[3].target, Some("crate3".to_string()));
617         assert_eq!(dirs[3].level, LevelFilter::OFF);
618         assert_eq!(dirs[3].in_span, None);
619     }
620 
621     #[test]
622 
parse_level_directives()623     fn parse_level_directives() {
624         let dirs = parse_directives(
625             "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
626              crate2=debug,crate3=trace,crate3::mod2::mod1=off",
627         );
628         assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
629         assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
630         assert_eq!(dirs[0].level, LevelFilter::ERROR);
631         assert_eq!(dirs[0].in_span, None);
632 
633         assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
634         assert_eq!(dirs[1].level, LevelFilter::WARN);
635         assert_eq!(dirs[1].in_span, None);
636 
637         assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
638         assert_eq!(dirs[2].level, LevelFilter::INFO);
639         assert_eq!(dirs[2].in_span, None);
640 
641         assert_eq!(dirs[3].target, Some("crate2".to_string()));
642         assert_eq!(dirs[3].level, LevelFilter::DEBUG);
643         assert_eq!(dirs[3].in_span, None);
644 
645         assert_eq!(dirs[4].target, Some("crate3".to_string()));
646         assert_eq!(dirs[4].level, LevelFilter::TRACE);
647         assert_eq!(dirs[4].in_span, None);
648 
649         assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
650         assert_eq!(dirs[5].level, LevelFilter::OFF);
651         assert_eq!(dirs[5].in_span, None);
652     }
653 
654     #[test]
parse_uppercase_level_directives()655     fn parse_uppercase_level_directives() {
656         let dirs = parse_directives(
657             "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
658              crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
659         );
660         assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
661         assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
662         assert_eq!(dirs[0].level, LevelFilter::ERROR);
663         assert_eq!(dirs[0].in_span, None);
664 
665         assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
666         assert_eq!(dirs[1].level, LevelFilter::WARN);
667         assert_eq!(dirs[1].in_span, None);
668 
669         assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
670         assert_eq!(dirs[2].level, LevelFilter::INFO);
671         assert_eq!(dirs[2].in_span, None);
672 
673         assert_eq!(dirs[3].target, Some("crate2".to_string()));
674         assert_eq!(dirs[3].level, LevelFilter::DEBUG);
675         assert_eq!(dirs[3].in_span, None);
676 
677         assert_eq!(dirs[4].target, Some("crate3".to_string()));
678         assert_eq!(dirs[4].level, LevelFilter::TRACE);
679         assert_eq!(dirs[4].in_span, None);
680 
681         assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
682         assert_eq!(dirs[5].level, LevelFilter::OFF);
683         assert_eq!(dirs[5].in_span, None);
684     }
685 
686     #[test]
parse_numeric_level_directives()687     fn parse_numeric_level_directives() {
688         let dirs = parse_directives(
689             "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
690              crate3=5,crate3::mod2::mod1=0",
691         );
692         assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
693         assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
694         assert_eq!(dirs[0].level, LevelFilter::ERROR);
695         assert_eq!(dirs[0].in_span, None);
696 
697         assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
698         assert_eq!(dirs[1].level, LevelFilter::WARN);
699         assert_eq!(dirs[1].in_span, None);
700 
701         assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string()));
702         assert_eq!(dirs[2].level, LevelFilter::INFO);
703         assert_eq!(dirs[2].in_span, None);
704 
705         assert_eq!(dirs[3].target, Some("crate2".to_string()));
706         assert_eq!(dirs[3].level, LevelFilter::DEBUG);
707         assert_eq!(dirs[3].in_span, None);
708 
709         assert_eq!(dirs[4].target, Some("crate3".to_string()));
710         assert_eq!(dirs[4].level, LevelFilter::TRACE);
711         assert_eq!(dirs[4].in_span, None);
712 
713         assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string()));
714         assert_eq!(dirs[5].level, LevelFilter::OFF);
715         assert_eq!(dirs[5].in_span, None);
716     }
717 
718     #[test]
parse_directives_invalid_crate()719     fn parse_directives_invalid_crate() {
720         // test parse_directives with multiple = in specification
721         let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug");
722         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
723         assert_eq!(dirs[0].target, Some("crate2".to_string()));
724         assert_eq!(dirs[0].level, LevelFilter::DEBUG);
725         assert_eq!(dirs[0].in_span, None);
726     }
727 
728     #[test]
parse_directives_invalid_level()729     fn parse_directives_invalid_level() {
730         // test parse_directives with 'noNumber' as log level
731         let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug");
732         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
733         assert_eq!(dirs[0].target, Some("crate2".to_string()));
734         assert_eq!(dirs[0].level, LevelFilter::DEBUG);
735         assert_eq!(dirs[0].in_span, None);
736     }
737 
738     #[test]
parse_directives_string_level()739     fn parse_directives_string_level() {
740         // test parse_directives with 'warn' as log level
741         let dirs = parse_directives("crate1::mod1=wrong,crate2=warn");
742         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
743         assert_eq!(dirs[0].target, Some("crate2".to_string()));
744         assert_eq!(dirs[0].level, LevelFilter::WARN);
745         assert_eq!(dirs[0].in_span, None);
746     }
747 
748     #[test]
parse_directives_empty_level()749     fn parse_directives_empty_level() {
750         // test parse_directives with '' as log level
751         let dirs = parse_directives("crate1::mod1=wrong,crate2=");
752         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
753         assert_eq!(dirs[0].target, Some("crate2".to_string()));
754         assert_eq!(dirs[0].level, LevelFilter::TRACE);
755         assert_eq!(dirs[0].in_span, None);
756     }
757 
758     #[test]
parse_directives_global()759     fn parse_directives_global() {
760         // test parse_directives with no crate
761         let dirs = parse_directives("warn,crate2=debug");
762         assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
763         assert_eq!(dirs[0].target, None);
764         assert_eq!(dirs[0].level, LevelFilter::WARN);
765         assert_eq!(dirs[1].in_span, None);
766 
767         assert_eq!(dirs[1].target, Some("crate2".to_string()));
768         assert_eq!(dirs[1].level, LevelFilter::DEBUG);
769         assert_eq!(dirs[1].in_span, None);
770     }
771 
772     // helper function for tests below
test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter)773     fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) {
774         let dirs = parse_directives(directive_to_test);
775         assert_eq!(
776             dirs.len(),
777             1,
778             "\ninput: \"{}\"; parsed: {:#?}",
779             directive_to_test,
780             dirs
781         );
782         assert_eq!(dirs[0].target, None);
783         assert_eq!(dirs[0].level, level_expected);
784         assert_eq!(dirs[0].in_span, None);
785     }
786 
787     #[test]
parse_directives_global_bare_warn_lc()788     fn parse_directives_global_bare_warn_lc() {
789         // test parse_directives with no crate, in isolation, all lowercase
790         test_parse_bare_level("warn", LevelFilter::WARN);
791     }
792 
793     #[test]
parse_directives_global_bare_warn_uc()794     fn parse_directives_global_bare_warn_uc() {
795         // test parse_directives with no crate, in isolation, all uppercase
796         test_parse_bare_level("WARN", LevelFilter::WARN);
797     }
798 
799     #[test]
parse_directives_global_bare_warn_mixed()800     fn parse_directives_global_bare_warn_mixed() {
801         // test parse_directives with no crate, in isolation, mixed case
802         test_parse_bare_level("wArN", LevelFilter::WARN);
803     }
804 
805     #[test]
parse_directives_valid_with_spans()806     fn parse_directives_valid_with_spans() {
807         let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug");
808         assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs);
809         assert_eq!(dirs[0].target, Some("crate1::mod1".to_string()));
810         assert_eq!(dirs[0].level, LevelFilter::ERROR);
811         assert_eq!(dirs[0].in_span, Some("foo".to_string()));
812 
813         assert_eq!(dirs[1].target, Some("crate1::mod2".to_string()));
814         assert_eq!(dirs[1].level, LevelFilter::TRACE);
815         assert_eq!(dirs[1].in_span, Some("bar".to_string()));
816 
817         assert_eq!(dirs[2].target, Some("crate2".to_string()));
818         assert_eq!(dirs[2].level, LevelFilter::DEBUG);
819         assert_eq!(dirs[2].in_span, Some("baz".to_string()));
820     }
821 
822     #[test]
parse_directives_with_dash_in_target_name()823     fn parse_directives_with_dash_in_target_name() {
824         let dirs = parse_directives("target-name=info");
825         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
826         assert_eq!(dirs[0].target, Some("target-name".to_string()));
827         assert_eq!(dirs[0].level, LevelFilter::INFO);
828         assert_eq!(dirs[0].in_span, None);
829     }
830 
831     #[test]
parse_directives_with_dash_in_span_name()832     fn parse_directives_with_dash_in_span_name() {
833         // Reproduces https://github.com/tokio-rs/tracing/issues/1367
834 
835         let dirs = parse_directives("target[span-name]=info");
836         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
837         assert_eq!(dirs[0].target, Some("target".to_string()));
838         assert_eq!(dirs[0].level, LevelFilter::INFO);
839         assert_eq!(dirs[0].in_span, Some("span-name".to_string()));
840     }
841 
842     #[test]
parse_directives_with_special_characters_in_span_name()843     fn parse_directives_with_special_characters_in_span_name() {
844         let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
845 
846         let dirs = parse_directives(format!("target[{}]=info", span_name));
847         assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs);
848         assert_eq!(dirs[0].target, Some("target".to_string()));
849         assert_eq!(dirs[0].level, LevelFilter::INFO);
850         assert_eq!(dirs[0].in_span, Some(span_name.to_string()));
851     }
852 
853     #[test]
parse_directives_with_invalid_span_chars()854     fn parse_directives_with_invalid_span_chars() {
855         let invalid_span_name = "]{";
856 
857         let dirs = parse_directives(format!("target[{}]=info", invalid_span_name));
858         assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs);
859     }
860 }
861