]> git.proxmox.com Git - rustc.git/blob - vendor/tracing-subscriber/src/filter/env/directive.rs
New upstream version 1.57.0+dfsg1
[rustc.git] / vendor / tracing-subscriber / src / filter / env / directive.rs
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 lazy_static::lazy_static;
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(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 {
39 pub(super) fn has_name(&self) -> bool {
40 self.in_span.is_some()
41 }
42
43 pub(super) fn has_fields(&self) -> bool {
44 !self.fields.is_empty()
45 }
46
47 pub(super) fn to_static(&self) -> Option<StaticDirective> {
48 if !self.is_static() {
49 return None;
50 }
51
52 // 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
63 fn is_static(&self) -> bool {
64 !self.has_name() && !self.fields.iter().any(field::Match::has_value)
65 }
66
67 pub(super) fn is_dynamic(&self) -> bool {
68 self.has_name() || self.has_fields()
69 }
70
71 pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option<field::CallsiteMatch> {
72 let fieldset = meta.fields();
73 let fields = self
74 .fields
75 .iter()
76 .filter_map(
77 |field::Match {
78 ref name,
79 ref value,
80 }| {
81 if let Some(field) = fieldset.field(name) {
82 let value = value.as_ref().cloned()?;
83 Some(Ok((field, value)))
84 } else {
85 Some(Err(()))
86 }
87 },
88 )
89 .collect::<Result<FieldMap<_>, ()>>()
90 .ok()?;
91 Some(field::CallsiteMatch {
92 fields,
93 level: self.level,
94 })
95 }
96
97 pub(super) fn make_tables(
98 directives: impl IntoIterator<Item = Directive>,
99 ) -> (Dynamics, Statics) {
100 // 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 }
111
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[..]) {
118 return false;
119 }
120 }
121
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() {
126 return false;
127 }
128 }
129
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() {
134 return false;
135 }
136 }
137
138 true
139 }
140
141 fn level(&self) -> &LevelFilter {
142 &self.level
143 }
144 }
145
146 impl FromStr for Directive {
147 type Err = ParseError;
148 fn from_str(from: &str) -> Result<Self, Self::Err> {
149 lazy_static! {
150 static ref DIRECTIVE_RE: Regex = Regex::new(
151 r"(?x)
152 ^(?P<global_level>(?i:trace|debug|info|warn|error|off|[0-5]))$ |
153 # ^^^.
154 # `note: we match log level names case-insensitively
155 ^
156 (?: # target name or span name
157 (?P<target>[\w:-]+)|(?P<span>\[[^\]]*\])
158 ){1,2}
159 (?: # level or nothing
160 =(?P<level>(?i:trace|debug|info|warn|error|off|[0-5]))?
161 # ^^^.
162 # `note: we match log level names case-insensitively
163 )?
164 $
165 "
166 )
167 .unwrap();
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.
173 Regex::new(r#"(?x)
174 (
175 # field name
176 [[:word:]][[[:word:]]\.]*
177 # value part (optional)
178 (?:=[^,]+)?
179 )
180 # trailing comma or EOS
181 (?:,\s?|$)
182 "#).unwrap();
183 }
184
185 let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
186
187 if let Some(level) = caps
188 .name("global_level")
189 .and_then(|s| s.as_str().parse().ok())
190 {
191 return Ok(Directive {
192 level,
193 ..Default::default()
194 });
195 }
196
197 let target = caps.name("target").and_then(|c| {
198 let s = c.as_str();
199 if s.parse::<LevelFilter>().is_ok() {
200 None
201 } else {
202 Some(s.to_owned())
203 }
204 });
205
206 let (in_span, fields) = caps
207 .name("span")
208 .and_then(|cap| {
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());
212 let fields = caps
213 .name("fields")
214 .map(|c| {
215 FIELD_FILTER_RE
216 .find_iter(c.as_str())
217 .map(|c| c.as_str().parse())
218 .collect::<Result<Vec<_>, _>>()
219 })
220 .unwrap_or_else(|| Ok(Vec::new()));
221 Some((span, fields))
222 })
223 .unwrap_or_else(|| (None, Ok(Vec::new())));
224
225 let level = caps
226 .name("level")
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);
230
231 Ok(Directive {
232 level,
233 target,
234 in_span,
235 fields: fields?,
236 })
237 }
238 }
239
240 impl Default for Directive {
241 fn default() -> Self {
242 Directive {
243 level: LevelFilter::OFF,
244 target: None,
245 in_span: None,
246 fields: Vec::new(),
247 }
248 }
249 }
250
251 impl PartialOrd for Directive {
252 fn partial_cmp(&self, other: &Directive) -> Option<Ordering> {
253 Some(self.cmp(other))
254 }
255 }
256
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.
262
263 // First, we compare based on whether a target is specified, and the
264 // lengths of those targets if both have targets.
265 let ordering = self
266 .target
267 .as_ref()
268 .map(String::len)
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
273 // directive.
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
278 // in the BTreeMap.
279 .then_with(|| {
280 self.target
281 .cmp(&other.target)
282 .then_with(|| self.in_span.cmp(&other.in_span))
283 .then_with(|| self.fields[..].cmp(&other.fields[..]))
284 })
285 .reverse();
286
287 #[cfg(debug_assertions)]
288 {
289 if ordering == Ordering::Equal {
290 debug_assert_eq!(
291 self.target, other.target,
292 "invariant violated: Ordering::Equal must imply a.target == b.target"
293 );
294 debug_assert_eq!(
295 self.in_span, other.in_span,
296 "invariant violated: Ordering::Equal must imply a.in_span == b.in_span"
297 );
298 debug_assert_eq!(
299 self.fields, other.fields,
300 "invariant violated: Ordering::Equal must imply a.fields == b.fields"
301 );
302 }
303 }
304
305 ordering
306 }
307 }
308
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)?;
314 wrote_any = true;
315 }
316
317 if self.in_span.is_some() || !self.fields.is_empty() {
318 f.write_str("[")?;
319
320 if let Some(ref span) = self.in_span {
321 fmt::Display::fmt(span, f)?;
322 }
323
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)?;
329 }
330 f.write_str("}")?;
331 }
332
333 f.write_str("]")?;
334 wrote_any = true;
335 }
336
337 if wrote_any {
338 f.write_str("=")?;
339 }
340
341 fmt::Display::fmt(&self.level, f)
342 }
343 }
344
345 impl From<LevelFilter> for Directive {
346 fn from(level: LevelFilter) -> Self {
347 Self {
348 level,
349 ..Self::default()
350 }
351 }
352 }
353
354 impl From<Level> for Directive {
355 fn from(level: Level) -> Self {
356 LevelFilter::from_level(level).into()
357 }
358 }
359
360 // === impl Dynamics ===
361
362 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)
367 .filter_map(|d| {
368 if let Some(f) = d.field_matcher(metadata) {
369 return Some(f);
370 }
371 match base_level {
372 Some(ref b) if d.level > *b => base_level = Some(d.level),
373 None => base_level = Some(d.level),
374 _ => {}
375 }
376 None
377 })
378 .collect();
379
380 if let Some(base_level) = base_level {
381 Some(CallsiteMatcher {
382 field_matches,
383 base_level,
384 })
385 } else if !field_matches.is_empty() {
386 Some(CallsiteMatcher {
387 field_matches,
388 base_level: base_level.unwrap_or(LevelFilter::OFF),
389 })
390 } else {
391 None
392 }
393 }
394
395 pub(crate) fn has_value_filters(&self) -> bool {
396 self.directives()
397 .any(|d| d.fields.iter().any(|f| f.value.is_some()))
398 }
399 }
400
401 // ===== impl DynamicMatch =====
402
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
407 .field_matches
408 .iter()
409 .map(|m| {
410 let m = m.to_span_match();
411 attrs.record(&mut m.visitor());
412 m
413 })
414 .collect();
415 SpanMatcher {
416 field_matches,
417 base_level: self.base_level,
418 }
419 }
420 }
421
422 impl SpanMatcher {
423 /// Returns the level currently enabled for this callsite.
424 pub(crate) fn level(&self) -> LevelFilter {
425 self.field_matches
426 .iter()
427 .filter_map(field::SpanMatch::filter)
428 .max()
429 .unwrap_or(self.base_level)
430 }
431
432 pub(crate) fn record_update(&self, record: &span::Record<'_>) {
433 for m in &self.field_matches {
434 record.record(&mut m.visitor())
435 }
436 }
437 }
438
439 #[cfg(test)]
440 mod test {
441 use super::*;
442
443 fn parse_directives(dirs: impl AsRef<str>) -> Vec<Directive> {
444 dirs.as_ref()
445 .split(',')
446 .filter_map(|s| s.parse().ok())
447 .collect()
448 }
449
450 fn expect_parse(dirs: impl AsRef<str>) -> Vec<Directive> {
451 dirs.as_ref()
452 .split(',')
453 .map(|s| {
454 s.parse()
455 .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err))
456 })
457 .collect()
458 }
459
460 #[test]
461 fn directive_ordering_by_target_len() {
462 // TODO(eliza): it would be nice to have a property-based test for this
463 // instead.
464 let mut dirs = expect_parse(
465 "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn",
466 );
467 dirs.sort_unstable();
468
469 let expected = vec![
470 "a_really_long_name_with_no_colons",
471 "foo::bar::baz",
472 "foo::bar",
473 "foo",
474 ];
475 let sorted = dirs
476 .iter()
477 .map(|d| d.target.as_ref().unwrap())
478 .collect::<Vec<_>>();
479
480 assert_eq!(expected, sorted);
481 }
482 #[test]
483 fn directive_ordering_by_span() {
484 // TODO(eliza): it would be nice to have a property-based test for this
485 // instead.
486 let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn");
487 dirs.sort_unstable();
488
489 let expected = vec!["baz::quux", "bar", "foo", "a"];
490 let sorted = dirs
491 .iter()
492 .map(|d| d.target.as_ref().unwrap())
493 .collect::<Vec<_>>();
494
495 assert_eq!(expected, sorted);
496 }
497
498 #[test]
499 fn directive_ordering_uses_lexicographic_when_equal() {
500 // TODO(eliza): it would be nice to have a property-based test for this
501 // instead.
502 let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info");
503 dirs.sort_unstable();
504
505 let expected = vec![
506 ("span", Some("b")),
507 ("span", Some("a")),
508 ("c", None),
509 ("b", None),
510 ("a", None),
511 ];
512 let sorted = dirs
513 .iter()
514 .map(|d| {
515 (
516 d.target.as_ref().unwrap().as_ref(),
517 d.in_span.as_ref().map(String::as_ref),
518 )
519 })
520 .collect::<Vec<_>>();
521
522 assert_eq!(expected, sorted);
523 }
524
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.
528 #[test]
529 #[ignore]
530 fn directive_ordering_by_field_num() {
531 // TODO(eliza): it would be nice to have a property-based test for this
532 // instead.
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"
535 );
536 dirs.sort_unstable();
537
538 let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"];
539 let sorted = dirs
540 .iter()
541 .map(|d| d.target.as_ref().unwrap())
542 .collect::<Vec<_>>();
543
544 assert_eq!(expected, sorted);
545 }
546
547 #[test]
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);
554
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);
558 }
559
560 #[test]
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);
567
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);
571 }
572
573 #[test]
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);
580
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);
584 }
585
586 #[test]
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);
593
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);
597
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);
601
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);
605 }
606
607 #[test]
608
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",
613 );
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);
618
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);
622
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);
626
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);
630
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);
634
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);
638 }
639
640 #[test]
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",
645 );
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);
650
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);
654
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);
658
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);
662
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);
666
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);
670 }
671
672 #[test]
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",
677 );
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);
682
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);
686
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);
690
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);
694
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);
698
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);
702 }
703
704 #[test]
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);
712 }
713
714 #[test]
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);
722 }
723
724 #[test]
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);
732 }
733
734 #[test]
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);
742 }
743
744 #[test]
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);
752
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);
756 }
757
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);
761 assert_eq!(
762 dirs.len(),
763 1,
764 "\ninput: \"{}\"; parsed: {:#?}",
765 directive_to_test,
766 dirs
767 );
768 assert_eq!(dirs[0].target, None);
769 assert_eq!(dirs[0].level, level_expected);
770 assert_eq!(dirs[0].in_span, None);
771 }
772
773 #[test]
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);
777 }
778
779 #[test]
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);
783 }
784
785 #[test]
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);
789 }
790
791 #[test]
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()));
798
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()));
802
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()));
806 }
807
808 #[test]
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);
815 }
816
817 #[test]
818 fn parse_directives_with_dash_in_span_name() {
819 // Reproduces https://github.com/tokio-rs/tracing/issues/1367
820
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()));
826 }
827
828 #[test]
829 fn parse_directives_with_special_characters_in_span_name() {
830 let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}";
831
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()));
837 }
838
839 #[test]
840 fn parse_directives_with_invalid_span_chars() {
841 let invalid_span_name = "]{";
842
843 let dirs = parse_directives(format!("target[{}]=info", invalid_span_name));
844 assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs);
845 }
846 }