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}