1 //! A [filter] that enables or disables spans and events based on their [target] and [level]. 2 //! 3 //! See [`Targets`] for details. 4 //! 5 //! [target]: tracing_core::Metadata::target 6 //! [level]: tracing_core::Level 7 //! [filter]: crate::layer#filtering-with-layers 8 9 use crate::{ 10 filter::{ 11 directive::{DirectiveSet, ParseError, StaticDirective}, 12 LevelFilter, 13 }, 14 layer, 15 }; 16 #[cfg(not(feature = "std"))] 17 use alloc::string::String; 18 use core::{ 19 fmt, 20 iter::{Extend, FilterMap, FromIterator}, 21 slice, 22 str::FromStr, 23 }; 24 use tracing_core::{Interest, Level, Metadata, Subscriber}; 25 26 /// A filter that enables or disables spans and events based on their [target] 27 /// and [level]. 28 /// 29 /// Targets are typically equal to the Rust module path of the code where the 30 /// span or event was recorded, although they may be overridden. 31 /// 32 /// This type can be used for both [per-layer filtering][plf] (using its 33 /// [`Filter`] implementation) and [global filtering][global] (using its 34 /// [`Layer`] implementation). 35 /// 36 /// See the [documentation on filtering with layers][filtering] for details. 37 /// 38 /// # Filtering With `Targets` 39 /// 40 /// A `Targets` filter consists of one or more [target] prefixes, paired with 41 /// [`LevelFilter`]s. If a span or event's [target] begins with one of those 42 /// prefixes, and its [level] is at or below the [`LevelFilter`] enabled for 43 /// that prefix, then the span or event will be enabled. 44 /// 45 /// This is similar to the behavior implemented by the [`env_logger` crate] in 46 /// the `log` ecosystem. 47 /// 48 /// The [`EnvFilter`] type also provided by this crate is very similar to `Targets`, 49 /// but is capable of a more sophisticated form of filtering where events may 50 /// also be enabled or disabled based on the span they are recorded in. 51 /// `Targets` can be thought of as a lighter-weight form of [`EnvFilter`] that 52 /// can be used instead when this dynamic filtering is not required. 53 /// 54 /// # Examples 55 /// 56 /// A `Targets` filter can be constructed by programmatically adding targets and 57 /// levels to enable: 58 /// 59 /// ``` 60 /// use tracing_subscriber::{filter, prelude::*}; 61 /// use tracing_core::Level; 62 /// 63 /// let filter = filter::Targets::new() 64 /// // Enable the `INFO` level for anything in `my_crate` 65 /// .with_target("my_crate", Level::INFO) 66 /// // Enable the `DEBUG` level for a specific module. 67 /// .with_target("my_crate::interesting_module", Level::DEBUG); 68 /// 69 /// // Build a new subscriber with the `fmt` layer using the `Targets` 70 /// // filter we constructed above. 71 /// tracing_subscriber::registry() 72 /// .with(tracing_subscriber::fmt::layer()) 73 /// .with(filter) 74 /// .init(); 75 /// ``` 76 /// 77 /// [`LevelFilter::OFF`] can be used to disable a particular target: 78 /// ``` 79 /// use tracing_subscriber::filter::{Targets, LevelFilter}; 80 /// use tracing_core::Level; 81 /// 82 /// let filter = Targets::new() 83 /// .with_target("my_crate", Level::INFO) 84 /// // Disable all traces from `annoying_module`. 85 /// .with_target("my_crate::annoying_module", LevelFilter::OFF); 86 /// # drop(filter); 87 /// ``` 88 /// 89 /// Alternatively, `Targets` implements [`std::str::FromStr`], allowing it to be 90 /// parsed from a comma-delimited list of `target=level` pairs. For example: 91 /// 92 /// ```rust 93 /// # fn main() -> Result<(), Box<dyn std::error::Error>> { 94 /// use tracing_subscriber::filter; 95 /// use tracing_core::Level; 96 /// 97 /// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug" 98 /// .parse::<filter::Targets>()?; 99 /// 100 /// // The parsed filter is identical to a filter constructed using `with_target`: 101 /// assert_eq!( 102 /// filter, 103 /// filter::Targets::new() 104 /// .with_target("my_crate", Level::INFO) 105 /// .with_target("my_crate::interesting_module", Level::TRACE) 106 /// .with_target("other_crate", Level::DEBUG) 107 /// ); 108 /// # Ok(()) } 109 /// ``` 110 /// 111 /// This is particularly useful when the list of enabled targets is configurable 112 /// by the user at runtime. 113 /// 114 /// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a 115 /// [global filter][global]: 116 /// 117 /// ```rust 118 /// use tracing_subscriber::{ 119 /// fmt, 120 /// filter::{Targets, LevelFilter}, 121 /// prelude::*, 122 /// }; 123 /// use tracing_core::Level; 124 /// use std::{sync::Arc, fs::File}; 125 /// # fn docs() -> Result<(), Box<dyn std::error::Error>> { 126 /// 127 /// // A layer that logs events to stdout using the human-readable "pretty" 128 /// // format. 129 /// let stdout_log = fmt::layer().pretty(); 130 /// 131 /// // A layer that logs events to a file, using the JSON format. 132 /// let file = File::create("debug_log.json")?; 133 /// let debug_log = fmt::layer() 134 /// .with_writer(Arc::new(file)) 135 /// .json(); 136 /// 137 /// tracing_subscriber::registry() 138 /// // Only log INFO and above to stdout, unless the span or event 139 /// // has the `my_crate::cool_module` target prefix. 140 /// .with(stdout_log 141 /// .with_filter( 142 /// Targets::default() 143 /// .with_target("my_crate::cool_module", Level::DEBUG) 144 /// .with_default(Level::INFO) 145 /// ) 146 /// ) 147 /// // Log everything enabled by the global filter to `debug_log.json`. 148 /// .with(debug_log) 149 /// // Configure a global filter for the whole subscriber stack. This will 150 /// // control what spans and events are recorded by both the `debug_log` 151 /// // and the `stdout_log` layers, and `stdout_log` will *additionally* be 152 /// // filtered by its per-layer filter. 153 /// .with( 154 /// Targets::default() 155 /// .with_target("my_crate", Level::TRACE) 156 /// .with_target("other_crate", Level::INFO) 157 /// .with_target("other_crate::annoying_module", LevelFilter::OFF) 158 /// .with_target("third_crate", Level::DEBUG) 159 /// ).init(); 160 /// # Ok(()) } 161 ///``` 162 /// 163 /// [target]: tracing_core::Metadata::target 164 /// [level]: tracing_core::Level 165 /// [`Filter`]: crate::layer::Filter 166 /// [`Layer`]: crate::layer::Layer 167 /// [plf]: crate::layer#per-layer-filtering 168 /// [global]: crate::layer#global-filtering 169 /// [filtering]: crate::layer#filtering-with-layers 170 /// [`env_logger` crate]: https://docs.rs/env_logger/0.9.0/env_logger/index.html#enabling-logging 171 /// [`EnvFilter`]: crate::filter::EnvFilter 172 #[derive(Debug, Default, Clone, PartialEq)] 173 pub struct Targets(DirectiveSet<StaticDirective>); 174 175 impl Targets { 176 /// Returns a new `Targets` filter. 177 /// 178 /// This filter will enable no targets. Call [`with_target`] or [`with_targets`] 179 /// to add enabled targets, and [`with_default`] to change the default level 180 /// enabled for spans and events that didn't match any of the provided targets. 181 /// 182 /// [`with_target`]: Targets::with_target 183 /// [`with_targets`]: Targets::with_targets 184 /// [`with_default`]: Targets::with_default new() -> Self185 pub fn new() -> Self { 186 Self::default() 187 } 188 189 /// Enables spans and events with [target]s starting with the provided target 190 /// prefix if they are at or below the provided [`LevelFilter`]. 191 /// 192 /// # Examples 193 /// 194 /// ``` 195 /// use tracing_subscriber::filter; 196 /// use tracing_core::Level; 197 /// 198 /// let filter = filter::Targets::new() 199 /// // Enable the `INFO` level for anything in `my_crate` 200 /// .with_target("my_crate", Level::INFO) 201 /// // Enable the `DEBUG` level for a specific module. 202 /// .with_target("my_crate::interesting_module", Level::DEBUG); 203 /// # drop(filter); 204 /// ``` 205 /// 206 /// [`LevelFilter::OFF`] can be used to disable a particular target: 207 /// ``` 208 /// use tracing_subscriber::filter::{Targets, LevelFilter}; 209 /// use tracing_core::Level; 210 /// 211 /// let filter = Targets::new() 212 /// .with_target("my_crate", Level::INFO) 213 /// // Disable all traces from `annoying_module`. 214 /// .with_target("my_crate::interesting_module", LevelFilter::OFF); 215 /// # drop(filter); 216 /// ``` 217 /// 218 /// [target]: tracing_core::Metadata::target with_target(mut self, target: impl Into<String>, level: impl Into<LevelFilter>) -> Self219 pub fn with_target(mut self, target: impl Into<String>, level: impl Into<LevelFilter>) -> Self { 220 self.0.add(StaticDirective::new( 221 Some(target.into()), 222 Default::default(), 223 level.into(), 224 )); 225 self 226 } 227 /// Adds [target]s from an iterator of [target]-[`LevelFilter`] pairs to this filter. 228 /// 229 /// # Examples 230 /// 231 /// ``` 232 /// use tracing_subscriber::filter; 233 /// use tracing_core::Level; 234 /// 235 /// let filter = filter::Targets::new() 236 /// .with_targets(vec![ 237 /// ("my_crate", Level::INFO), 238 /// ("my_crate::some_module", Level::DEBUG), 239 /// ("my_crate::other_module::cool_stuff", Level::TRACE), 240 /// ("other_crate", Level::WARN) 241 /// ]); 242 /// # drop(filter); 243 /// ``` 244 /// 245 /// [`LevelFilter::OFF`] can be used to disable a particular target: 246 /// ``` 247 /// use tracing_subscriber::filter::{Targets, LevelFilter}; 248 /// use tracing_core::Level; 249 /// 250 /// let filter = Targets::new() 251 /// .with_target("my_crate", Level::INFO) 252 /// // Disable all traces from `annoying_module`. 253 /// .with_target("my_crate::interesting_module", LevelFilter::OFF); 254 /// # drop(filter); 255 /// ``` 256 /// 257 /// [target]: tracing_core::Metadata::target with_targets<T, L>(mut self, targets: impl IntoIterator<Item = (T, L)>) -> Self where String: From<T>, LevelFilter: From<L>,258 pub fn with_targets<T, L>(mut self, targets: impl IntoIterator<Item = (T, L)>) -> Self 259 where 260 String: From<T>, 261 LevelFilter: From<L>, 262 { 263 self.extend(targets); 264 self 265 } 266 267 /// Sets the default level to enable for spans and events whose targets did 268 /// not match any of the configured prefixes. 269 /// 270 /// By default, this is [`LevelFilter::OFF`]. This means that spans and 271 /// events will only be enabled if they match one of the configured target 272 /// prefixes. If this is changed to a different [`LevelFilter`], spans and 273 /// events with targets that did not match any of the configured prefixes 274 /// will be enabled if their level is at or below the provided level. with_default(mut self, level: impl Into<LevelFilter>) -> Self275 pub fn with_default(mut self, level: impl Into<LevelFilter>) -> Self { 276 self.0 277 .add(StaticDirective::new(None, Default::default(), level.into())); 278 self 279 } 280 281 /// Returns the default level for this filter, if one is set. 282 /// 283 /// The default level is used to filter any spans or events with targets 284 /// that do not match any of the configured set of prefixes. 285 /// 286 /// The default level can be set for a filter either by using 287 /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a 288 /// level without a target (e.g. `"trace"`). 289 /// 290 /// # Examples 291 /// 292 /// ``` 293 /// use tracing_subscriber::filter::{LevelFilter, Targets}; 294 /// 295 /// let filter = Targets::new().with_default(LevelFilter::INFO); 296 /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); 297 /// 298 /// let filter: Targets = "info".parse().unwrap(); 299 /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); 300 /// ``` 301 /// 302 /// The default level is `None` if no default is set: 303 /// 304 /// ``` 305 /// use tracing_subscriber::filter::Targets; 306 /// 307 /// let filter = Targets::new(); 308 /// assert_eq!(filter.default_level(), None); 309 /// 310 /// let filter: Targets = "my_crate=info".parse().unwrap(); 311 /// assert_eq!(filter.default_level(), None); 312 /// ``` 313 /// 314 /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is 315 /// used, but it could also be set explicitly which may be useful to distinguish (such as when 316 /// merging multiple `Targets`). 317 /// 318 /// ``` 319 /// use tracing_subscriber::filter::{LevelFilter, Targets}; 320 /// 321 /// let filter = Targets::new().with_default(LevelFilter::OFF); 322 /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); 323 /// 324 /// let filter: Targets = "off".parse().unwrap(); 325 /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); 326 /// ``` default_level(&self) -> Option<LevelFilter>327 pub fn default_level(&self) -> Option<LevelFilter> { 328 self.0.directives().find_map(|d| { 329 if d.target.is_none() { 330 Some(d.level) 331 } else { 332 None 333 } 334 }) 335 } 336 337 /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. 338 /// 339 /// The order of iteration is undefined. 340 /// 341 /// # Examples 342 /// 343 /// ``` 344 /// use tracing_subscriber::filter::{Targets, LevelFilter}; 345 /// use tracing_core::Level; 346 /// 347 /// let filter = Targets::new() 348 /// .with_target("my_crate", Level::INFO) 349 /// .with_target("my_crate::interesting_module", Level::DEBUG); 350 /// 351 /// let mut targets: Vec<_> = filter.iter().collect(); 352 /// targets.sort(); 353 /// 354 /// assert_eq!(targets, vec![ 355 /// ("my_crate", LevelFilter::INFO), 356 /// ("my_crate::interesting_module", LevelFilter::DEBUG), 357 /// ]); 358 /// ``` 359 /// 360 /// [target]: tracing_core::Metadata::target iter(&self) -> Iter<'_>361 pub fn iter(&self) -> Iter<'_> { 362 self.into_iter() 363 } 364 365 #[inline] interested(&self, metadata: &'static Metadata<'static>) -> Interest366 fn interested(&self, metadata: &'static Metadata<'static>) -> Interest { 367 if self.0.enabled(metadata) { 368 Interest::always() 369 } else { 370 Interest::never() 371 } 372 } 373 374 /// Returns whether a [target]-[`Level`] pair would be enabled 375 /// by this `Targets`. 376 /// 377 /// This method can be used with [`module_path!`] from `std` as the target 378 /// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`] 379 /// macros. 380 /// 381 /// # Examples 382 /// 383 /// ``` 384 /// use tracing_subscriber::filter::{Targets, LevelFilter}; 385 /// use tracing_core::Level; 386 /// 387 /// let filter = Targets::new() 388 /// .with_target("my_crate", Level::INFO) 389 /// .with_target("my_crate::interesting_module", Level::DEBUG); 390 /// 391 /// assert!(filter.would_enable("my_crate", &Level::INFO)); 392 /// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE)); 393 /// ``` 394 /// 395 /// [target]: tracing_core::Metadata::target 396 /// [`module_path!`]: std::module_path! would_enable(&self, target: &str, level: &Level) -> bool397 pub fn would_enable(&self, target: &str, level: &Level) -> bool { 398 // "Correct" to call because `Targets` only produces `StaticDirective`'s with NO 399 // fields 400 self.0.target_enabled(target, level) 401 } 402 } 403 404 impl<T, L> Extend<(T, L)> for Targets 405 where 406 T: Into<String>, 407 L: Into<LevelFilter>, 408 { extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I)409 fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) { 410 let iter = iter.into_iter().map(|(target, level)| { 411 StaticDirective::new(Some(target.into()), Default::default(), level.into()) 412 }); 413 self.0.extend(iter); 414 } 415 } 416 417 impl<T, L> FromIterator<(T, L)> for Targets 418 where 419 T: Into<String>, 420 L: Into<LevelFilter>, 421 { from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self422 fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self { 423 let mut this = Self::default(); 424 this.extend(iter); 425 this 426 } 427 } 428 429 impl FromStr for Targets { 430 type Err = ParseError; from_str(s: &str) -> Result<Self, Self::Err>431 fn from_str(s: &str) -> Result<Self, Self::Err> { 432 s.split(',') 433 .map(StaticDirective::from_str) 434 .collect::<Result<_, _>>() 435 .map(Self) 436 } 437 } 438 439 impl<S> layer::Layer<S> for Targets 440 where 441 S: Subscriber, 442 { enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool443 fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { 444 self.0.enabled(metadata) 445 } 446 register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest447 fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { 448 self.interested(metadata) 449 } 450 max_level_hint(&self) -> Option<LevelFilter>451 fn max_level_hint(&self) -> Option<LevelFilter> { 452 Some(self.0.max_level) 453 } 454 } 455 456 #[cfg(feature = "registry")] 457 #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] 458 impl<S> layer::Filter<S> for Targets { enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool459 fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { 460 self.0.enabled(metadata) 461 } 462 callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest463 fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { 464 self.interested(metadata) 465 } 466 max_level_hint(&self) -> Option<LevelFilter>467 fn max_level_hint(&self) -> Option<LevelFilter> { 468 Some(self.0.max_level) 469 } 470 } 471 472 impl IntoIterator for Targets { 473 type Item = (String, LevelFilter); 474 475 type IntoIter = IntoIter; 476 into_iter(self) -> Self::IntoIter477 fn into_iter(self) -> Self::IntoIter { 478 IntoIter::new(self) 479 } 480 } 481 482 impl<'a> IntoIterator for &'a Targets { 483 type Item = (&'a str, LevelFilter); 484 485 type IntoIter = Iter<'a>; 486 into_iter(self) -> Self::IntoIter487 fn into_iter(self) -> Self::IntoIter { 488 Iter::new(self) 489 } 490 } 491 492 impl fmt::Display for Targets { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 494 let mut directives = self.0.directives(); 495 if let Some(directive) = directives.next() { 496 write!(f, "{}", directive)?; 497 for directive in directives { 498 write!(f, ",{}", directive)?; 499 } 500 } 501 502 Ok(()) 503 } 504 } 505 506 /// An owning iterator over the [target]-[level] pairs of a `Targets` filter. 507 /// 508 /// This struct is created by the `IntoIterator` trait implementation of [`Targets`]. 509 /// 510 /// # Examples 511 /// 512 /// Merge the targets from one `Targets` with another: 513 /// 514 /// ``` 515 /// use tracing_subscriber::filter::Targets; 516 /// use tracing_core::Level; 517 /// 518 /// let mut filter = Targets::new().with_target("my_crate", Level::INFO); 519 /// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG); 520 /// 521 /// filter.extend(overrides); 522 /// # drop(filter); 523 /// ``` 524 /// 525 /// [target]: tracing_core::Metadata::target 526 /// [level]: tracing_core::Level 527 #[derive(Debug)] 528 pub struct IntoIter( 529 #[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing 530 FilterMap< 531 <DirectiveSet<StaticDirective> as IntoIterator>::IntoIter, 532 fn(StaticDirective) -> Option<(String, LevelFilter)>, 533 >, 534 ); 535 536 impl IntoIter { new(targets: Targets) -> Self537 fn new(targets: Targets) -> Self { 538 Self(targets.0.into_iter().filter_map(|directive| { 539 let level = directive.level; 540 directive.target.map(|target| (target, level)) 541 })) 542 } 543 } 544 545 impl Iterator for IntoIter { 546 type Item = (String, LevelFilter); 547 next(&mut self) -> Option<Self::Item>548 fn next(&mut self) -> Option<Self::Item> { 549 self.0.next() 550 } 551 size_hint(&self) -> (usize, Option<usize>)552 fn size_hint(&self) -> (usize, Option<usize>) { 553 self.0.size_hint() 554 } 555 } 556 557 /// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter. 558 /// 559 /// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator` 560 /// implementation for `&Targets`. 561 /// 562 /// [target]: tracing_core::Metadata::target 563 /// [level]: tracing_core::Level 564 /// [`iter`]: Targets::iter 565 #[derive(Debug)] 566 pub struct Iter<'a>( 567 FilterMap< 568 slice::Iter<'a, StaticDirective>, 569 fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>, 570 >, 571 ); 572 573 impl<'a> Iter<'a> { new(targets: &'a Targets) -> Self574 fn new(targets: &'a Targets) -> Self { 575 Self(targets.0.iter().filter_map(|directive| { 576 directive 577 .target 578 .as_deref() 579 .map(|target| (target, directive.level)) 580 })) 581 } 582 } 583 584 impl<'a> Iterator for Iter<'a> { 585 type Item = (&'a str, LevelFilter); 586 next(&mut self) -> Option<Self::Item>587 fn next(&mut self) -> Option<Self::Item> { 588 self.0.next() 589 } 590 size_hint(&self) -> (usize, Option<usize>)591 fn size_hint(&self) -> (usize, Option<usize>) { 592 self.0.size_hint() 593 } 594 } 595 596 #[cfg(test)] 597 mod tests { 598 use super::*; 599 600 feature! { 601 #![not(feature = "std")] 602 use alloc::{vec, vec::Vec, string::ToString}; 603 604 // `dbg!` is only available with `libstd`; just nop it out when testing 605 // with alloc only. 606 macro_rules! dbg { 607 ($x:expr) => { $x } 608 } 609 } 610 expect_parse(s: &str) -> Targets611 fn expect_parse(s: &str) -> Targets { 612 match dbg!(s).parse::<Targets>() { 613 Err(e) => panic!("string {:?} did not parse successfully: {}", s, e), 614 Ok(e) => e, 615 } 616 } 617 expect_parse_ralith(s: &str)618 fn expect_parse_ralith(s: &str) { 619 let dirs = expect_parse(s).0.into_vec(); 620 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); 621 assert_eq!(dirs[0].target, Some("server".to_string())); 622 assert_eq!(dirs[0].level, LevelFilter::DEBUG); 623 assert_eq!(dirs[0].field_names, Vec::<String>::new()); 624 625 assert_eq!(dirs[1].target, Some("common".to_string())); 626 assert_eq!(dirs[1].level, LevelFilter::INFO); 627 assert_eq!(dirs[1].field_names, Vec::<String>::new()); 628 } 629 expect_parse_level_directives(s: &str)630 fn expect_parse_level_directives(s: &str) { 631 let dirs = expect_parse(s).0.into_vec(); 632 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); 633 634 assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string())); 635 assert_eq!(dirs[0].level, LevelFilter::OFF); 636 assert_eq!(dirs[0].field_names, Vec::<String>::new()); 637 638 assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string())); 639 assert_eq!(dirs[1].level, LevelFilter::INFO); 640 assert_eq!(dirs[1].field_names, Vec::<String>::new()); 641 642 assert_eq!(dirs[2].target, Some("crate1::mod2".to_string())); 643 assert_eq!(dirs[2].level, LevelFilter::WARN); 644 assert_eq!(dirs[2].field_names, Vec::<String>::new()); 645 646 assert_eq!(dirs[3].target, Some("crate1::mod1".to_string())); 647 assert_eq!(dirs[3].level, LevelFilter::ERROR); 648 assert_eq!(dirs[3].field_names, Vec::<String>::new()); 649 650 assert_eq!(dirs[4].target, Some("crate3".to_string())); 651 assert_eq!(dirs[4].level, LevelFilter::TRACE); 652 assert_eq!(dirs[4].field_names, Vec::<String>::new()); 653 654 assert_eq!(dirs[5].target, Some("crate2".to_string())); 655 assert_eq!(dirs[5].level, LevelFilter::DEBUG); 656 assert_eq!(dirs[5].field_names, Vec::<String>::new()); 657 } 658 659 #[test] parse_ralith()660 fn parse_ralith() { 661 expect_parse_ralith("common=info,server=debug"); 662 } 663 664 #[test] parse_ralith_uc()665 fn parse_ralith_uc() { 666 expect_parse_ralith("common=INFO,server=DEBUG"); 667 } 668 669 #[test] parse_ralith_mixed()670 fn parse_ralith_mixed() { 671 expect_parse("common=iNfo,server=dEbUg"); 672 } 673 674 #[test] expect_parse_valid()675 fn expect_parse_valid() { 676 let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") 677 .0 678 .into_vec(); 679 assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); 680 assert_eq!(dirs[0].target, Some("crate1::mod2".to_string())); 681 assert_eq!(dirs[0].level, LevelFilter::TRACE); 682 assert_eq!(dirs[0].field_names, Vec::<String>::new()); 683 684 assert_eq!(dirs[1].target, Some("crate1::mod1".to_string())); 685 assert_eq!(dirs[1].level, LevelFilter::ERROR); 686 assert_eq!(dirs[1].field_names, Vec::<String>::new()); 687 688 assert_eq!(dirs[2].target, Some("crate3".to_string())); 689 assert_eq!(dirs[2].level, LevelFilter::OFF); 690 assert_eq!(dirs[2].field_names, Vec::<String>::new()); 691 692 assert_eq!(dirs[3].target, Some("crate2".to_string())); 693 assert_eq!(dirs[3].level, LevelFilter::DEBUG); 694 assert_eq!(dirs[3].field_names, Vec::<String>::new()); 695 } 696 697 #[test] parse_level_directives()698 fn parse_level_directives() { 699 expect_parse_level_directives( 700 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ 701 crate2=debug,crate3=trace,crate3::mod2::mod1=off", 702 ) 703 } 704 705 #[test] parse_uppercase_level_directives()706 fn parse_uppercase_level_directives() { 707 expect_parse_level_directives( 708 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ 709 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", 710 ) 711 } 712 713 #[test] parse_numeric_level_directives()714 fn parse_numeric_level_directives() { 715 expect_parse_level_directives( 716 "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ 717 crate3=5,crate3::mod2::mod1=0", 718 ) 719 } 720 721 #[test] targets_iter()722 fn targets_iter() { 723 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") 724 .with_default(LevelFilter::WARN); 725 726 let mut targets: Vec<_> = filter.iter().collect(); 727 targets.sort(); 728 729 assert_eq!( 730 targets, 731 vec![ 732 ("crate1::mod1", LevelFilter::ERROR), 733 ("crate1::mod2", LevelFilter::TRACE), 734 ("crate2", LevelFilter::DEBUG), 735 ("crate3", LevelFilter::OFF), 736 ] 737 ); 738 } 739 740 #[test] targets_into_iter()741 fn targets_into_iter() { 742 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") 743 .with_default(LevelFilter::WARN); 744 745 let mut targets: Vec<_> = filter.into_iter().collect(); 746 targets.sort(); 747 748 assert_eq!( 749 targets, 750 vec![ 751 ("crate1::mod1".to_string(), LevelFilter::ERROR), 752 ("crate1::mod2".to_string(), LevelFilter::TRACE), 753 ("crate2".to_string(), LevelFilter::DEBUG), 754 ("crate3".to_string(), LevelFilter::OFF), 755 ] 756 ); 757 } 758 759 #[test] targets_default_level()760 fn targets_default_level() { 761 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); 762 assert_eq!(filter.default_level(), None); 763 764 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") 765 .with_default(LevelFilter::OFF); 766 assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); 767 768 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") 769 .with_default(LevelFilter::OFF) 770 .with_default(LevelFilter::INFO); 771 assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); 772 } 773 774 #[test] 775 // `println!` is only available with `libstd`. 776 #[cfg(feature = "std")] size_of_filters()777 fn size_of_filters() { 778 fn print_sz(s: &str) { 779 let filter = s.parse::<Targets>().expect("filter should parse"); 780 println!( 781 "size_of_val({:?})\n -> {}B", 782 s, 783 std::mem::size_of_val(&filter) 784 ); 785 } 786 787 print_sz("info"); 788 789 print_sz("foo=debug"); 790 791 print_sz( 792 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ 793 crate2=debug,crate3=trace,crate3::mod2::mod1=off", 794 ); 795 } 796 797 /// Test that the `fmt::Display` implementation for `Targets` emits a string 798 /// that can itself be parsed as a `Targets`, and that the parsed `Targets` 799 /// is equivalent to the original one. 800 #[test] display_roundtrips()801 fn display_roundtrips() { 802 fn test_roundtrip(s: &str) { 803 let filter = expect_parse(s); 804 // we don't assert that the display output is equivalent to the 805 // original parsed filter string, because the `Display` impl always 806 // uses lowercase level names and doesn't use the 807 // target-without-level shorthand syntax. while they may not be 808 // textually equivalent, though, they should still *parse* to the 809 // same filter. 810 let formatted = filter.to_string(); 811 let filter2 = match dbg!(&formatted).parse::<Targets>() { 812 Ok(filter) => filter, 813 Err(e) => panic!( 814 "failed to parse formatted filter string {:?}: {}", 815 formatted, e 816 ), 817 }; 818 assert_eq!(filter, filter2); 819 } 820 821 test_roundtrip("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); 822 test_roundtrip( 823 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ 824 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", 825 ); 826 test_roundtrip( 827 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ 828 crate2=debug,crate3=trace,crate3::mod2::mod1=off", 829 ); 830 test_roundtrip("crate1::mod1,crate1::mod2,info"); 831 test_roundtrip("crate1"); 832 test_roundtrip("info"); 833 } 834 } 835