tracing_subscriber/filter/
targets.rs

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 = 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
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 = 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(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(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 = 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)]
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| {
539            let level = directive.level;
540            directive.target.map(|target| (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| {
576            directive
577                .target
578                .as_deref()
579                .map(|target| (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}