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