🛈 Note: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem.

For the release documentation, please see docs.rs, instead.

tracing_subscriber/filter/env/
directive.rs

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