• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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