| 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 |
| 185 | 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 |
| 219 | 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 |
| 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. |
| 275 | 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 | /// ``` |
| 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 |
| 361 | pub fn iter(&self) -> Iter<'_> { |
| 362 | self.into_iter() |
| 363 | } |
| 364 | |
| 365 | #[inline ] |
| 366 | 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! |
| 397 | 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 | { |
| 409 | fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) { |
| 410 | let iter: impl Iterator = iter.into_iter().map(|(target: T, level: L)| { |
| 411 | StaticDirective::new(target:Some(target.into()), field_names: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 | { |
| 422 | fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self { |
| 423 | let mut this: Targets = Self::default(); |
| 424 | this.extend(iter); |
| 425 | this |
| 426 | } |
| 427 | } |
| 428 | |
| 429 | impl FromStr for Targets { |
| 430 | type Err = ParseError; |
| 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 | { |
| 443 | fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { |
| 444 | self.0.enabled(meta:metadata) |
| 445 | } |
| 446 | |
| 447 | fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { |
| 448 | self.interested(metadata) |
| 449 | } |
| 450 | |
| 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 { |
| 459 | fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { |
| 460 | self.0.enabled(meta:metadata) |
| 461 | } |
| 462 | |
| 463 | fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { |
| 464 | self.interested(metadata) |
| 465 | } |
| 466 | |
| 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 | |
| 477 | 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 | |
| 487 | fn into_iter(self) -> Self::IntoIter { |
| 488 | Iter::new(self) |
| 489 | } |
| 490 | } |
| 491 | |
| 492 | impl fmt::Display for Targets { |
| 493 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 494 | let mut directives: impl Iterator = self.0.directives(); |
| 495 | if let Some(directive: &StaticDirective) = directives.next() { |
| 496 | write!(f, " {}" , directive)?; |
| 497 | for directive: &StaticDirective 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 { |
| 537 | fn new(targets: Targets) -> Self { |
| 538 | Self(targets.0.into_iter().filter_map(|directive: StaticDirective| { |
| 539 | let level: LevelFilter = directive.level; |
| 540 | directive.target.map(|target: String| (target, level)) |
| 541 | })) |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | impl Iterator for IntoIter { |
| 546 | type Item = (String, LevelFilter); |
| 547 | |
| 548 | fn next(&mut self) -> Option<Self::Item> { |
| 549 | self.0.next() |
| 550 | } |
| 551 | |
| 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> { |
| 574 | fn new(targets: &'a Targets) -> Self { |
| 575 | Self(targets.0.iter().filter_map(|directive: &'a StaticDirective| { |
| 576 | directiveOption<&str> |
| 577 | .target |
| 578 | .as_deref() |
| 579 | .map(|target: &str| (target, directive.level)) |
| 580 | })) |
| 581 | } |
| 582 | } |
| 583 | |
| 584 | impl<'a> Iterator for Iter<'a> { |
| 585 | type Item = (&'a str, LevelFilter); |
| 586 | |
| 587 | fn next(&mut self) -> Option<Self::Item> { |
| 588 | self.0.next() |
| 589 | } |
| 590 | |
| 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 | |
| 611 | 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 | |
| 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 | |
| 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 ] |
| 660 | fn parse_ralith() { |
| 661 | expect_parse_ralith("common=info,server=debug" ); |
| 662 | } |
| 663 | |
| 664 | #[test ] |
| 665 | fn parse_ralith_uc() { |
| 666 | expect_parse_ralith("common=INFO,server=DEBUG" ); |
| 667 | } |
| 668 | |
| 669 | #[test ] |
| 670 | fn parse_ralith_mixed() { |
| 671 | expect_parse("common=iNfo,server=dEbUg" ); |
| 672 | } |
| 673 | |
| 674 | #[test ] |
| 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 ] |
| 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 ] |
| 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 ] |
| 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 ] |
| 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 ] |
| 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 ] |
| 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" )] |
| 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 ] |
| 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 | |