1 pub(crate) use crate::filter
::directive
::{FilterVec, ParseError, StaticDirective}
;
3 directive
::{DirectiveSet, Match}
,
4 env
::{field, FieldMap}
,
7 use lazy_static
::lazy_static
;
9 use std
::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr}
;
10 use tracing_core
::{span, Level, Metadata}
;
12 /// A single filtering directive.
13 // TODO(eliza): add a builder for programmatically constructing directives?
14 #[derive(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
,
23 /// A set of dynamic filtering directives.
24 pub(super) type Dynamics
= DirectiveSet
<Directive
>;
26 /// A set of static filtering directives.
27 pub(super) type Statics
= DirectiveSet
<StaticDirective
>;
29 pub(crate) type CallsiteMatcher
= MatchSet
<field
::CallsiteMatch
>;
30 pub(crate) type SpanMatcher
= MatchSet
<field
::SpanMatch
>;
32 #[derive(Debug, PartialEq, Eq)]
33 pub(crate) struct MatchSet
<T
> {
34 field_matches
: FilterVec
<T
>,
35 base_level
: LevelFilter
,
39 pub(super) fn has_name(&self) -> bool
{
40 self.in_span
.is_some()
43 pub(super) fn has_fields(&self) -> bool
{
44 !self.fields
.is_empty()
47 pub(super) fn to_static(&self) -> Option
<StaticDirective
> {
48 if !self.is_static() {
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();
56 Some(StaticDirective
::new(
63 fn is_static(&self) -> bool
{
64 !self.has_name() && !self.fields
.iter().any(field
::Match
::has_value
)
67 pub(super) fn is_dynamic(&self) -> bool
{
68 self.has_name() || self.has_fields()
71 pub(crate) fn field_matcher(&self, meta
: &Metadata
<'_
>) -> Option
<field
::CallsiteMatch
> {
72 let fieldset
= meta
.fields();
81 if let Some(field
) = fieldset
.field(name
) {
82 let value
= value
.as_ref().cloned()?
;
83 Some(Ok((field
, value
)))
89 .collect
::<Result
<FieldMap
<_
>, ()>>()
91 Some(field
::CallsiteMatch
{
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
);
105 .filter_map(|d
| d
.to_static())
106 .chain(dyns
.iter().filter_map(Directive
::to_static
))
108 (Dynamics
::from_iter(dyns
), statics
)
112 impl Match
for Directive
{
113 fn cares_about(&self, meta
: &Metadata
<'_
>) -> bool
{
114 // Does this directive have a target filter, and does it match the
115 // metadata's target?
116 if let Some(ref target
) = self.target
{
117 if !meta
.target().starts_with(&target
[..]) {
122 // Do we have a name filter, and does it match the metadata's name?
123 // TODO(eliza): put name globbing here?
124 if let Some(ref name
) = self.in_span
{
125 if name
!= meta
.name() {
130 // Does the metadata define all the fields that this directive cares about?
131 let fields
= meta
.fields();
132 for field
in &self.fields
{
133 if fields
.field(&field
.name
).is_none() {
141 fn level(&self) -> &LevelFilter
{
146 impl FromStr
for Directive
{
147 type Err
= ParseError
;
148 fn from_str(from
: &str) -> Result
<Self, Self::Err
> {
150 static ref DIRECTIVE_RE
: Regex
= Regex
::new(
152 ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
154 # `note: we match log level names case-insensitively
156 (?: # target name or span name
157 (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
159 (?: # level or nothing
160 =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
162 # `note: we match log level names case-insensitively
168 static ref SPAN_PART_RE
: Regex
=
169 Regex
::new(r
#"(?P<name>[^\]\{]+)?(?:\{(?P<fields>[^\}]*)\})?"#).unwrap();
170 static ref FIELD_FILTER_RE
: Regex
=
171 // TODO(eliza): this doesn't _currently_ handle value matchers that include comma
172 // characters. We should fix that.
176 [[:word:]][[[:word:]]\.]*
177 # value part (optional)
180 # trailing comma or EOS
185 let caps
= DIRECTIVE_RE
.captures(from
).ok_or_else(ParseError
::new
)?
;
187 if let Some(level
) = caps
188 .name("global_level")
189 .and_then(|s
| s
.as_str().parse().ok())
191 return Ok(Directive
{
197 let target
= caps
.name("target").and_then(|c
| {
199 if s
.parse
::<LevelFilter
>().is_ok() {
206 let (in_span
, fields
) = caps
209 let cap
= cap
.as_str().trim_matches(|c
| c
== '
['
|| c
== '
]'
);
210 let caps
= SPAN_PART_RE
.captures(cap
)?
;
211 let span
= caps
.name("name").map(|c
| c
.as_str().to_owned());
216 .find_iter(c
.as_str())
217 .map(|c
| c
.as_str().parse())
218 .collect
::<Result
<Vec
<_
>, _
>>()
220 .unwrap_or_else(|| Ok(Vec
::new()));
223 .unwrap_or_else(|| (None
, Ok(Vec
::new())));
227 .and_then(|l
| l
.as_str().parse().ok())
228 // Setting the target without the level enables every level for that target
229 .unwrap_or(LevelFilter
::TRACE
);
240 impl Default
for Directive
{
241 fn default() -> Self {
243 level
: LevelFilter
::OFF
,
251 impl PartialOrd
for Directive
{
252 fn partial_cmp(&self, other
: &Directive
) -> Option
<Ordering
> {
253 Some(self.cmp(other
))
257 impl Ord
for Directive
{
258 fn cmp(&self, other
: &Directive
) -> Ordering
{
259 // We attempt to order directives by how "specific" they are. This
260 // ensures that we try the most specific directives first when
261 // attempting to match a piece of metadata.
263 // First, we compare based on whether a target is specified, and the
264 // lengths of those targets if both have targets.
269 .cmp(&other
.target
.as_ref().map(String
::len
))
270 // Next compare based on the presence of span names.
271 .then_with(|| self.in_span
.is_some().cmp(&other
.in_span
.is_some()))
272 // Then we compare how many fields are defined by each
274 .then_with(|| self.fields
.len().cmp(&other
.fields
.len()))
275 // Finally, we fall back to lexicographical ordering if the directives are
276 // equally specific. Although this is no longer semantically important,
277 // we need to define a total ordering to determine the directive's place
282 .then_with(|| self.in_span
.cmp(&other
.in_span
))
283 .then_with(|| self.fields
[..].cmp(&other
.fields
[..]))
287 #[cfg(debug_assertions)]
289 if ordering
== Ordering
::Equal
{
291 self.target
, other
.target
,
292 "invariant violated: Ordering::Equal must imply a.target == b.target"
295 self.in_span
, other
.in_span
,
296 "invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
299 self.fields
, other
.fields
,
300 "invariant violated: Ordering::Equal must imply a.fields == b.fields"
309 impl fmt
::Display
for Directive
{
310 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
311 let mut wrote_any
= false;
312 if let Some(ref target
) = self.target
{
313 fmt
::Display
::fmt(target
, f
)?
;
317 if self.in_span
.is_some() || !self.fields
.is_empty() {
320 if let Some(ref span
) = self.in_span
{
321 fmt
::Display
::fmt(span
, f
)?
;
324 let mut fields
= self.fields
.iter();
325 if let Some(field
) = fields
.next() {
326 write
!(f
, "{{{}", field
)?
;
327 for field
in fields
{
328 write
!(f
, ",{}", field
)?
;
341 fmt
::Display
::fmt(&self.level
, f
)
345 impl From
<LevelFilter
> for Directive
{
346 fn from(level
: LevelFilter
) -> Self {
354 impl From
<Level
> for Directive
{
355 fn from(level
: Level
) -> Self {
356 LevelFilter
::from_level(level
).into()
360 // === impl Dynamics ===
363 pub(crate) fn matcher(&self, metadata
: &Metadata
<'_
>) -> Option
<CallsiteMatcher
> {
364 let mut base_level
= None
;
365 let field_matches
= self
366 .directives_for(metadata
)
368 if let Some(f
) = d
.field_matcher(metadata
) {
372 Some(ref b
) if d
.level
> *b
=> base_level
= Some(d
.level
),
373 None
=> base_level
= Some(d
.level
),
380 if let Some(base_level
) = base_level
{
381 Some(CallsiteMatcher
{
385 } else if !field_matches
.is_empty() {
386 Some(CallsiteMatcher
{
388 base_level
: base_level
.unwrap_or(LevelFilter
::OFF
),
395 pub(crate) fn has_value_filters(&self) -> bool
{
397 .any(|d
| d
.fields
.iter().any(|f
| f
.value
.is_some()))
401 // ===== impl DynamicMatch =====
403 impl CallsiteMatcher
{
404 /// Create a new `SpanMatch` for a given instance of the matched callsite.
405 pub(crate) fn to_span_match(&self, attrs
: &span
::Attributes
<'_
>) -> SpanMatcher
{
406 let field_matches
= self
410 let m
= m
.to_span_match();
411 attrs
.record(&mut m
.visitor());
417 base_level
: self.base_level
,
423 /// Returns the level currently enabled for this callsite.
424 pub(crate) fn level(&self) -> LevelFilter
{
427 .filter_map(field
::SpanMatch
::filter
)
429 .unwrap_or(self.base_level
)
432 pub(crate) fn record_update(&self, record
: &span
::Record
<'_
>) {
433 for m
in &self.field_matches
{
434 record
.record(&mut m
.visitor())
443 fn parse_directives(dirs
: impl AsRef
<str>) -> Vec
<Directive
> {
446 .filter_map(|s
| s
.parse().ok())
450 fn expect_parse(dirs
: impl AsRef
<str>) -> Vec
<Directive
> {
455 .unwrap_or_else(|err
| panic
!("directive '{:?}' should parse: {}", s
, err
))
461 fn directive_ordering_by_target_len() {
462 // TODO(eliza): it would be nice to have a property-based test for this
464 let mut dirs
= expect_parse(
465 "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
467 dirs
.sort_unstable();
470 "a_really_long_name_with_no_colons",
477 .map(|d
| d
.target
.as_ref().unwrap())
478 .collect
::<Vec
<_
>>();
480 assert_eq
!(expected
, sorted
);
483 fn directive_ordering_by_span() {
484 // TODO(eliza): it would be nice to have a property-based test for this
486 let mut dirs
= expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
487 dirs
.sort_unstable();
489 let expected
= vec
!["baz::quux", "bar", "foo", "a"];
492 .map(|d
| d
.target
.as_ref().unwrap())
493 .collect
::<Vec
<_
>>();
495 assert_eq
!(expected
, sorted
);
499 fn directive_ordering_uses_lexicographic_when_equal() {
500 // TODO(eliza): it would be nice to have a property-based test for this
502 let mut dirs
= expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
503 dirs
.sort_unstable();
516 d
.target
.as_ref().unwrap().as_ref(),
517 d
.in_span
.as_ref().map(String
::as_ref
),
520 .collect
::<Vec
<_
>>();
522 assert_eq
!(expected
, sorted
);
525 // TODO: this test requires the parser to support directives with multiple
526 // fields, which it currently can't handle. We should enable this test when
527 // that's implemented.
530 fn directive_ordering_by_field_num() {
531 // TODO(eliza): it would be nice to have a property-based test for this
533 let mut dirs
= expect_parse(
534 "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info"
536 dirs
.sort_unstable();
538 let expected
= vec
!["baz::quux", "bar", "foo", "c", "b", "a"];
541 .map(|d
| d
.target
.as_ref().unwrap())
542 .collect
::<Vec
<_
>>();
544 assert_eq
!(expected
, sorted
);
548 fn parse_directives_ralith() {
549 let dirs
= parse_directives("common=trace,server=trace");
550 assert_eq
!(dirs
.len(), 2, "\nparsed: {:#?}", dirs
);
551 assert_eq
!(dirs
[0].target
, Some("common".to_string()));
552 assert_eq
!(dirs
[0].level
, LevelFilter
::TRACE
);
553 assert_eq
!(dirs
[0].in_span
, None
);
555 assert_eq
!(dirs
[1].target
, Some("server".to_string()));
556 assert_eq
!(dirs
[1].level
, LevelFilter
::TRACE
);
557 assert_eq
!(dirs
[1].in_span
, None
);
561 fn parse_directives_ralith_uc() {
562 let dirs
= parse_directives("common=INFO,server=DEBUG");
563 assert_eq
!(dirs
.len(), 2, "\nparsed: {:#?}", dirs
);
564 assert_eq
!(dirs
[0].target
, Some("common".to_string()));
565 assert_eq
!(dirs
[0].level
, LevelFilter
::INFO
);
566 assert_eq
!(dirs
[0].in_span
, None
);
568 assert_eq
!(dirs
[1].target
, Some("server".to_string()));
569 assert_eq
!(dirs
[1].level
, LevelFilter
::DEBUG
);
570 assert_eq
!(dirs
[1].in_span
, None
);
574 fn parse_directives_ralith_mixed() {
575 let dirs
= parse_directives("common=iNfo,server=dEbUg");
576 assert_eq
!(dirs
.len(), 2, "\nparsed: {:#?}", dirs
);
577 assert_eq
!(dirs
[0].target
, Some("common".to_string()));
578 assert_eq
!(dirs
[0].level
, LevelFilter
::INFO
);
579 assert_eq
!(dirs
[0].in_span
, None
);
581 assert_eq
!(dirs
[1].target
, Some("server".to_string()));
582 assert_eq
!(dirs
[1].level
, LevelFilter
::DEBUG
);
583 assert_eq
!(dirs
[1].in_span
, None
);
587 fn parse_directives_valid() {
588 let dirs
= parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off");
589 assert_eq
!(dirs
.len(), 4, "\nparsed: {:#?}", dirs
);
590 assert_eq
!(dirs
[0].target
, Some("crate1::mod1".to_string()));
591 assert_eq
!(dirs
[0].level
, LevelFilter
::ERROR
);
592 assert_eq
!(dirs
[0].in_span
, None
);
594 assert_eq
!(dirs
[1].target
, Some("crate1::mod2".to_string()));
595 assert_eq
!(dirs
[1].level
, LevelFilter
::TRACE
);
596 assert_eq
!(dirs
[1].in_span
, None
);
598 assert_eq
!(dirs
[2].target
, Some("crate2".to_string()));
599 assert_eq
!(dirs
[2].level
, LevelFilter
::DEBUG
);
600 assert_eq
!(dirs
[2].in_span
, None
);
602 assert_eq
!(dirs
[3].target
, Some("crate3".to_string()));
603 assert_eq
!(dirs
[3].level
, LevelFilter
::OFF
);
604 assert_eq
!(dirs
[3].in_span
, None
);
609 fn parse_level_directives() {
610 let dirs
= parse_directives(
611 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
612 crate2=debug,crate3=trace,crate3::mod2::mod1=off",
614 assert_eq
!(dirs
.len(), 6, "\nparsed: {:#?}", dirs
);
615 assert_eq
!(dirs
[0].target
, Some("crate1::mod1".to_string()));
616 assert_eq
!(dirs
[0].level
, LevelFilter
::ERROR
);
617 assert_eq
!(dirs
[0].in_span
, None
);
619 assert_eq
!(dirs
[1].target
, Some("crate1::mod2".to_string()));
620 assert_eq
!(dirs
[1].level
, LevelFilter
::WARN
);
621 assert_eq
!(dirs
[1].in_span
, None
);
623 assert_eq
!(dirs
[2].target
, Some("crate1::mod2::mod3".to_string()));
624 assert_eq
!(dirs
[2].level
, LevelFilter
::INFO
);
625 assert_eq
!(dirs
[2].in_span
, None
);
627 assert_eq
!(dirs
[3].target
, Some("crate2".to_string()));
628 assert_eq
!(dirs
[3].level
, LevelFilter
::DEBUG
);
629 assert_eq
!(dirs
[3].in_span
, None
);
631 assert_eq
!(dirs
[4].target
, Some("crate3".to_string()));
632 assert_eq
!(dirs
[4].level
, LevelFilter
::TRACE
);
633 assert_eq
!(dirs
[4].in_span
, None
);
635 assert_eq
!(dirs
[5].target
, Some("crate3::mod2::mod1".to_string()));
636 assert_eq
!(dirs
[5].level
, LevelFilter
::OFF
);
637 assert_eq
!(dirs
[5].in_span
, None
);
641 fn parse_uppercase_level_directives() {
642 let dirs
= parse_directives(
643 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
644 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
646 assert_eq
!(dirs
.len(), 6, "\nparsed: {:#?}", dirs
);
647 assert_eq
!(dirs
[0].target
, Some("crate1::mod1".to_string()));
648 assert_eq
!(dirs
[0].level
, LevelFilter
::ERROR
);
649 assert_eq
!(dirs
[0].in_span
, None
);
651 assert_eq
!(dirs
[1].target
, Some("crate1::mod2".to_string()));
652 assert_eq
!(dirs
[1].level
, LevelFilter
::WARN
);
653 assert_eq
!(dirs
[1].in_span
, None
);
655 assert_eq
!(dirs
[2].target
, Some("crate1::mod2::mod3".to_string()));
656 assert_eq
!(dirs
[2].level
, LevelFilter
::INFO
);
657 assert_eq
!(dirs
[2].in_span
, None
);
659 assert_eq
!(dirs
[3].target
, Some("crate2".to_string()));
660 assert_eq
!(dirs
[3].level
, LevelFilter
::DEBUG
);
661 assert_eq
!(dirs
[3].in_span
, None
);
663 assert_eq
!(dirs
[4].target
, Some("crate3".to_string()));
664 assert_eq
!(dirs
[4].level
, LevelFilter
::TRACE
);
665 assert_eq
!(dirs
[4].in_span
, None
);
667 assert_eq
!(dirs
[5].target
, Some("crate3::mod2::mod1".to_string()));
668 assert_eq
!(dirs
[5].level
, LevelFilter
::OFF
);
669 assert_eq
!(dirs
[5].in_span
, None
);
673 fn parse_numeric_level_directives() {
674 let dirs
= parse_directives(
675 "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
676 crate3=5,crate3::mod2::mod1=0",
678 assert_eq
!(dirs
.len(), 6, "\nparsed: {:#?}", dirs
);
679 assert_eq
!(dirs
[0].target
, Some("crate1::mod1".to_string()));
680 assert_eq
!(dirs
[0].level
, LevelFilter
::ERROR
);
681 assert_eq
!(dirs
[0].in_span
, None
);
683 assert_eq
!(dirs
[1].target
, Some("crate1::mod2".to_string()));
684 assert_eq
!(dirs
[1].level
, LevelFilter
::WARN
);
685 assert_eq
!(dirs
[1].in_span
, None
);
687 assert_eq
!(dirs
[2].target
, Some("crate1::mod2::mod3".to_string()));
688 assert_eq
!(dirs
[2].level
, LevelFilter
::INFO
);
689 assert_eq
!(dirs
[2].in_span
, None
);
691 assert_eq
!(dirs
[3].target
, Some("crate2".to_string()));
692 assert_eq
!(dirs
[3].level
, LevelFilter
::DEBUG
);
693 assert_eq
!(dirs
[3].in_span
, None
);
695 assert_eq
!(dirs
[4].target
, Some("crate3".to_string()));
696 assert_eq
!(dirs
[4].level
, LevelFilter
::TRACE
);
697 assert_eq
!(dirs
[4].in_span
, None
);
699 assert_eq
!(dirs
[5].target
, Some("crate3::mod2::mod1".to_string()));
700 assert_eq
!(dirs
[5].level
, LevelFilter
::OFF
);
701 assert_eq
!(dirs
[5].in_span
, None
);
705 fn parse_directives_invalid_crate() {
706 // test parse_directives with multiple = in specification
707 let dirs
= parse_directives("crate1::mod1=warn=info,crate2=debug");
708 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
709 assert_eq
!(dirs
[0].target
, Some("crate2".to_string()));
710 assert_eq
!(dirs
[0].level
, LevelFilter
::DEBUG
);
711 assert_eq
!(dirs
[0].in_span
, None
);
715 fn parse_directives_invalid_level() {
716 // test parse_directives with 'noNumber' as log level
717 let dirs
= parse_directives("crate1::mod1=noNumber,crate2=debug");
718 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
719 assert_eq
!(dirs
[0].target
, Some("crate2".to_string()));
720 assert_eq
!(dirs
[0].level
, LevelFilter
::DEBUG
);
721 assert_eq
!(dirs
[0].in_span
, None
);
725 fn parse_directives_string_level() {
726 // test parse_directives with 'warn' as log level
727 let dirs
= parse_directives("crate1::mod1=wrong,crate2=warn");
728 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
729 assert_eq
!(dirs
[0].target
, Some("crate2".to_string()));
730 assert_eq
!(dirs
[0].level
, LevelFilter
::WARN
);
731 assert_eq
!(dirs
[0].in_span
, None
);
735 fn parse_directives_empty_level() {
736 // test parse_directives with '' as log level
737 let dirs
= parse_directives("crate1::mod1=wrong,crate2=");
738 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
739 assert_eq
!(dirs
[0].target
, Some("crate2".to_string()));
740 assert_eq
!(dirs
[0].level
, LevelFilter
::TRACE
);
741 assert_eq
!(dirs
[0].in_span
, None
);
745 fn parse_directives_global() {
746 // test parse_directives with no crate
747 let dirs
= parse_directives("warn,crate2=debug");
748 assert_eq
!(dirs
.len(), 2, "\nparsed: {:#?}", dirs
);
749 assert_eq
!(dirs
[0].target
, None
);
750 assert_eq
!(dirs
[0].level
, LevelFilter
::WARN
);
751 assert_eq
!(dirs
[1].in_span
, None
);
753 assert_eq
!(dirs
[1].target
, Some("crate2".to_string()));
754 assert_eq
!(dirs
[1].level
, LevelFilter
::DEBUG
);
755 assert_eq
!(dirs
[1].in_span
, None
);
758 // helper function for tests below
759 fn test_parse_bare_level(directive_to_test
: &str, level_expected
: LevelFilter
) {
760 let dirs
= parse_directives(directive_to_test
);
764 "\ninput: \"{}\"; parsed: {:#?}",
768 assert_eq
!(dirs
[0].target
, None
);
769 assert_eq
!(dirs
[0].level
, level_expected
);
770 assert_eq
!(dirs
[0].in_span
, None
);
774 fn parse_directives_global_bare_warn_lc() {
775 // test parse_directives with no crate, in isolation, all lowercase
776 test_parse_bare_level("warn", LevelFilter
::WARN
);
780 fn parse_directives_global_bare_warn_uc() {
781 // test parse_directives with no crate, in isolation, all uppercase
782 test_parse_bare_level("WARN", LevelFilter
::WARN
);
786 fn parse_directives_global_bare_warn_mixed() {
787 // test parse_directives with no crate, in isolation, mixed case
788 test_parse_bare_level("wArN", LevelFilter
::WARN
);
792 fn parse_directives_valid_with_spans() {
793 let dirs
= parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug");
794 assert_eq
!(dirs
.len(), 3, "\nparsed: {:#?}", dirs
);
795 assert_eq
!(dirs
[0].target
, Some("crate1::mod1".to_string()));
796 assert_eq
!(dirs
[0].level
, LevelFilter
::ERROR
);
797 assert_eq
!(dirs
[0].in_span
, Some("foo".to_string()));
799 assert_eq
!(dirs
[1].target
, Some("crate1::mod2".to_string()));
800 assert_eq
!(dirs
[1].level
, LevelFilter
::TRACE
);
801 assert_eq
!(dirs
[1].in_span
, Some("bar".to_string()));
803 assert_eq
!(dirs
[2].target
, Some("crate2".to_string()));
804 assert_eq
!(dirs
[2].level
, LevelFilter
::DEBUG
);
805 assert_eq
!(dirs
[2].in_span
, Some("baz".to_string()));
809 fn parse_directives_with_dash_in_target_name() {
810 let dirs
= parse_directives("target-name=info");
811 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
812 assert_eq
!(dirs
[0].target
, Some("target-name".to_string()));
813 assert_eq
!(dirs
[0].level
, LevelFilter
::INFO
);
814 assert_eq
!(dirs
[0].in_span
, None
);
818 fn parse_directives_with_dash_in_span_name() {
819 // Reproduces https://github.com/tokio-rs/tracing/issues/1367
821 let dirs
= parse_directives("target[span-name]=info");
822 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
823 assert_eq
!(dirs
[0].target
, Some("target".to_string()));
824 assert_eq
!(dirs
[0].level
, LevelFilter
::INFO
);
825 assert_eq
!(dirs
[0].in_span
, Some("span-name".to_string()));
829 fn parse_directives_with_special_characters_in_span_name() {
830 let span_name
= "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
832 let dirs
= parse_directives(format
!("target[{}]=info", span_name
));
833 assert_eq
!(dirs
.len(), 1, "\nparsed: {:#?}", dirs
);
834 assert_eq
!(dirs
[0].target
, Some("target".to_string()));
835 assert_eq
!(dirs
[0].level
, LevelFilter
::INFO
);
836 assert_eq
!(dirs
[0].in_span
, Some(span_name
.to_string()));
840 fn parse_directives_with_invalid_span_chars() {
841 let invalid_span_name
= "]{";
843 let dirs
= parse_directives(format
!("target[{}]=info", invalid_span_name
));
844 assert_eq
!(dirs
.len(), 0, "\nparsed: {:#?}", dirs
);