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