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