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
9use crate::{
10 filter::{
11 directive::{DirectiveSet, ParseError, StaticDirective},
12 LevelFilter,
13 },
14 layer,
15};
16#[cfg(not(feature = "std"))]
17use alloc::string::String;
18use core::{
19 fmt,
20 iter::{Extend, FilterMap, FromIterator},
21 slice,
22 str::FromStr,
23};
24use 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)]
173pub struct Targets(DirectiveSet<StaticDirective>);
174
175impl 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
404impl<T, L> Extend<(T, L)> for Targets
405where
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:level.into())
412 });
413 self.0.extend(iter);
414 }
415}
416
417impl<T, L> FromIterator<(T, L)> for Targets
418where
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
429impl 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
439impl<S> layer::Layer<S> for Targets
440where
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")))]
458impl<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
472impl 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
482impl<'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
492impl 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)]
528pub 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
536impl 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
545impl 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)]
566pub struct Iter<'a>(
567 FilterMap<
568 slice::Iter<'a, StaticDirective>,
569 fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>,
570 >,
571);
572
573impl<'a> Iter<'a> {
574 fn new(targets: &'a Targets) -> Self {
575 Self(targets.0.iter().filter_map(|directive: &StaticDirective| {
576 directiveOption<&str>
577 .target
578 .as_deref()
579 .map(|target: &str| (target, directive.level))
580 }))
581 }
582}
583
584impl<'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)]
597mod 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