]> git.proxmox.com Git - rustc.git/blob - vendor/tracing-subscriber/src/filter/targets.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / vendor / tracing-subscriber / src / 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
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 iter::{Extend, FilterMap, FromIterator},
20 slice,
21 str::FromStr,
22 };
23 use 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)]
172 pub struct Targets(DirectiveSet<StaticDirective>);
173
174 impl 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 an iterator over the [target]-[`LevelFilter`] pairs in this filter.
281 ///
282 /// The order of iteration is undefined.
283 ///
284 /// # Examples
285 ///
286 /// ```
287 /// use tracing_subscriber::filter::{Targets, LevelFilter};
288 /// use tracing_core::Level;
289 ///
290 /// let filter = Targets::new()
291 /// .with_target("my_crate", Level::INFO)
292 /// .with_target("my_crate::interesting_module", Level::DEBUG);
293 ///
294 /// let mut targets: Vec<_> = filter.iter().collect();
295 /// targets.sort();
296 ///
297 /// assert_eq!(targets, vec![
298 /// ("my_crate", LevelFilter::INFO),
299 /// ("my_crate::interesting_module", LevelFilter::DEBUG),
300 /// ]);
301 /// ```
302 ///
303 /// [target]: tracing_core::Metadata::target
304 pub fn iter(&self) -> Iter<'_> {
305 self.into_iter()
306 }
307
308 #[inline]
309 fn interested(&self, metadata: &'static Metadata<'static>) -> Interest {
310 if self.0.enabled(metadata) {
311 Interest::always()
312 } else {
313 Interest::never()
314 }
315 }
316
317 /// Returns whether a [target]-[`Level`] pair would be enabled
318 /// by this `Targets`.
319 ///
320 /// This method can be used with [`module_path!`] from `std` as the target
321 /// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`]
322 /// macros.
323 ///
324 /// # Examples
325 ///
326 /// ```
327 /// use tracing_subscriber::filter::{Targets, LevelFilter};
328 /// use tracing_core::Level;
329 ///
330 /// let filter = Targets::new()
331 /// .with_target("my_crate", Level::INFO)
332 /// .with_target("my_crate::interesting_module", Level::DEBUG);
333 ///
334 /// assert!(filter.would_enable("my_crate", &Level::INFO));
335 /// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE));
336 /// ```
337 ///
338 /// [target]: tracing_core::Metadata::target
339 /// [`module_path!`]: std::module_path!
340 pub fn would_enable(&self, target: &str, level: &Level) -> bool {
341 // "Correct" to call because `Targets` only produces `StaticDirective`'s with NO
342 // fields
343 self.0.target_enabled(target, level)
344 }
345 }
346
347 impl<T, L> Extend<(T, L)> for Targets
348 where
349 T: Into<String>,
350 L: Into<LevelFilter>,
351 {
352 fn extend<I: IntoIterator<Item = (T, L)>>(&mut self, iter: I) {
353 let iter = iter.into_iter().map(|(target, level)| {
354 StaticDirective::new(Some(target.into()), Default::default(), level.into())
355 });
356 self.0.extend(iter);
357 }
358 }
359
360 impl<T, L> FromIterator<(T, L)> for Targets
361 where
362 T: Into<String>,
363 L: Into<LevelFilter>,
364 {
365 fn from_iter<I: IntoIterator<Item = (T, L)>>(iter: I) -> Self {
366 let mut this = Self::default();
367 this.extend(iter);
368 this
369 }
370 }
371
372 impl FromStr for Targets {
373 type Err = ParseError;
374 fn from_str(s: &str) -> Result<Self, Self::Err> {
375 s.split(',')
376 .map(StaticDirective::from_str)
377 .collect::<Result<_, _>>()
378 .map(Self)
379 }
380 }
381
382 impl<S> layer::Layer<S> for Targets
383 where
384 S: Subscriber,
385 {
386 fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool {
387 self.0.enabled(metadata)
388 }
389
390 fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
391 self.interested(metadata)
392 }
393
394 fn max_level_hint(&self) -> Option<LevelFilter> {
395 Some(self.0.max_level)
396 }
397 }
398
399 #[cfg(feature = "registry")]
400 #[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
401 impl<S> layer::Filter<S> for Targets {
402 fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool {
403 self.0.enabled(metadata)
404 }
405
406 fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
407 self.interested(metadata)
408 }
409
410 fn max_level_hint(&self) -> Option<LevelFilter> {
411 Some(self.0.max_level)
412 }
413 }
414
415 impl IntoIterator for Targets {
416 type Item = (String, LevelFilter);
417
418 type IntoIter = IntoIter;
419
420 fn into_iter(self) -> Self::IntoIter {
421 IntoIter::new(self)
422 }
423 }
424
425 impl<'a> IntoIterator for &'a Targets {
426 type Item = (&'a str, LevelFilter);
427
428 type IntoIter = Iter<'a>;
429
430 fn into_iter(self) -> Self::IntoIter {
431 Iter::new(self)
432 }
433 }
434
435 /// An owning iterator over the [target]-[level] pairs of a `Targets` filter.
436 ///
437 /// This struct is created by the `IntoIterator` trait implementation of [`Targets`].
438 ///
439 /// # Examples
440 ///
441 /// Merge the targets from one `Targets` with another:
442 ///
443 /// ```
444 /// use tracing_subscriber::filter::Targets;
445 /// use tracing_core::Level;
446 ///
447 /// let mut filter = Targets::new().with_target("my_crate", Level::INFO);
448 /// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG);
449 ///
450 /// filter.extend(overrides);
451 /// # drop(filter);
452 /// ```
453 ///
454 /// [target]: tracing_core::Metadata::target
455 /// [level]: tracing_core::Level
456 #[derive(Debug)]
457 pub struct IntoIter(
458 #[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing
459 FilterMap<
460 <DirectiveSet<StaticDirective> as IntoIterator>::IntoIter,
461 fn(StaticDirective) -> Option<(String, LevelFilter)>,
462 >,
463 );
464
465 impl IntoIter {
466 fn new(targets: Targets) -> Self {
467 Self(targets.0.into_iter().filter_map(|directive| {
468 let level = directive.level;
469 directive.target.map(|target| (target, level))
470 }))
471 }
472 }
473
474 impl Iterator for IntoIter {
475 type Item = (String, LevelFilter);
476
477 fn next(&mut self) -> Option<Self::Item> {
478 self.0.next()
479 }
480
481 fn size_hint(&self) -> (usize, Option<usize>) {
482 self.0.size_hint()
483 }
484 }
485
486 /// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter.
487 ///
488 /// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator`
489 /// implementation for `&Targets`.
490 ///
491 /// [target]: tracing_core::Metadata::target
492 /// [level]: tracing_core::Level
493 /// [`iter`]: Targets::iter
494 #[derive(Debug)]
495 pub struct Iter<'a>(
496 FilterMap<
497 slice::Iter<'a, StaticDirective>,
498 fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>,
499 >,
500 );
501
502 impl<'a> Iter<'a> {
503 fn new(targets: &'a Targets) -> Self {
504 Self(targets.0.iter().filter_map(|directive| {
505 directive
506 .target
507 .as_deref()
508 .map(|target| (target, directive.level))
509 }))
510 }
511 }
512
513 impl<'a> Iterator for Iter<'a> {
514 type Item = (&'a str, LevelFilter);
515
516 fn next(&mut self) -> Option<Self::Item> {
517 self.0.next()
518 }
519
520 fn size_hint(&self) -> (usize, Option<usize>) {
521 self.0.size_hint()
522 }
523 }
524
525 #[cfg(test)]
526 mod tests {
527 use super::*;
528
529 feature! {
530 #![not(feature = "std")]
531 use alloc::{vec, vec::Vec, string::ToString};
532
533 // `dbg!` is only available with `libstd`; just nop it out when testing
534 // with alloc only.
535 macro_rules! dbg {
536 ($x:expr) => { $x }
537 }
538 }
539
540 fn expect_parse(s: &str) -> Targets {
541 match dbg!(s).parse::<Targets>() {
542 Err(e) => panic!("string {:?} did not parse successfully: {}", s, e),
543 Ok(e) => e,
544 }
545 }
546
547 fn expect_parse_ralith(s: &str) {
548 let dirs = expect_parse(s).0.into_vec();
549 assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs);
550 assert_eq!(dirs[0].target, Some("server".to_string()));
551 assert_eq!(dirs[0].level, LevelFilter::DEBUG);
552 assert_eq!(dirs[0].field_names, Vec::<String>::new());
553
554 assert_eq!(dirs[1].target, Some("common".to_string()));
555 assert_eq!(dirs[1].level, LevelFilter::INFO);
556 assert_eq!(dirs[1].field_names, Vec::<String>::new());
557 }
558
559 fn expect_parse_level_directives(s: &str) {
560 let dirs = expect_parse(s).0.into_vec();
561 assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs);
562
563 assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string()));
564 assert_eq!(dirs[0].level, LevelFilter::OFF);
565 assert_eq!(dirs[0].field_names, Vec::<String>::new());
566
567 assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string()));
568 assert_eq!(dirs[1].level, LevelFilter::INFO);
569 assert_eq!(dirs[1].field_names, Vec::<String>::new());
570
571 assert_eq!(dirs[2].target, Some("crate1::mod2".to_string()));
572 assert_eq!(dirs[2].level, LevelFilter::WARN);
573 assert_eq!(dirs[2].field_names, Vec::<String>::new());
574
575 assert_eq!(dirs[3].target, Some("crate1::mod1".to_string()));
576 assert_eq!(dirs[3].level, LevelFilter::ERROR);
577 assert_eq!(dirs[3].field_names, Vec::<String>::new());
578
579 assert_eq!(dirs[4].target, Some("crate3".to_string()));
580 assert_eq!(dirs[4].level, LevelFilter::TRACE);
581 assert_eq!(dirs[4].field_names, Vec::<String>::new());
582
583 assert_eq!(dirs[5].target, Some("crate2".to_string()));
584 assert_eq!(dirs[5].level, LevelFilter::DEBUG);
585 assert_eq!(dirs[5].field_names, Vec::<String>::new());
586 }
587
588 #[test]
589 fn parse_ralith() {
590 expect_parse_ralith("common=info,server=debug");
591 }
592
593 #[test]
594 fn parse_ralith_uc() {
595 expect_parse_ralith("common=INFO,server=DEBUG");
596 }
597
598 #[test]
599 fn parse_ralith_mixed() {
600 expect_parse("common=iNfo,server=dEbUg");
601 }
602
603 #[test]
604 fn expect_parse_valid() {
605 let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
606 .0
607 .into_vec();
608 assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs);
609 assert_eq!(dirs[0].target, Some("crate1::mod2".to_string()));
610 assert_eq!(dirs[0].level, LevelFilter::TRACE);
611 assert_eq!(dirs[0].field_names, Vec::<String>::new());
612
613 assert_eq!(dirs[1].target, Some("crate1::mod1".to_string()));
614 assert_eq!(dirs[1].level, LevelFilter::ERROR);
615 assert_eq!(dirs[1].field_names, Vec::<String>::new());
616
617 assert_eq!(dirs[2].target, Some("crate3".to_string()));
618 assert_eq!(dirs[2].level, LevelFilter::OFF);
619 assert_eq!(dirs[2].field_names, Vec::<String>::new());
620
621 assert_eq!(dirs[3].target, Some("crate2".to_string()));
622 assert_eq!(dirs[3].level, LevelFilter::DEBUG);
623 assert_eq!(dirs[3].field_names, Vec::<String>::new());
624 }
625
626 #[test]
627 fn parse_level_directives() {
628 expect_parse_level_directives(
629 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
630 crate2=debug,crate3=trace,crate3::mod2::mod1=off",
631 )
632 }
633
634 #[test]
635 fn parse_uppercase_level_directives() {
636 expect_parse_level_directives(
637 "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\
638 crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF",
639 )
640 }
641
642 #[test]
643 fn parse_numeric_level_directives() {
644 expect_parse_level_directives(
645 "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\
646 crate3=5,crate3::mod2::mod1=0",
647 )
648 }
649
650 #[test]
651 fn targets_iter() {
652 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
653 .with_default(LevelFilter::WARN);
654
655 let mut targets: Vec<_> = filter.iter().collect();
656 targets.sort();
657
658 assert_eq!(
659 targets,
660 vec![
661 ("crate1::mod1", LevelFilter::ERROR),
662 ("crate1::mod2", LevelFilter::TRACE),
663 ("crate2", LevelFilter::DEBUG),
664 ("crate3", LevelFilter::OFF),
665 ]
666 );
667 }
668
669 #[test]
670 fn targets_into_iter() {
671 let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off")
672 .with_default(LevelFilter::WARN);
673
674 let mut targets: Vec<_> = filter.into_iter().collect();
675 targets.sort();
676
677 assert_eq!(
678 targets,
679 vec![
680 ("crate1::mod1".to_string(), LevelFilter::ERROR),
681 ("crate1::mod2".to_string(), LevelFilter::TRACE),
682 ("crate2".to_string(), LevelFilter::DEBUG),
683 ("crate3".to_string(), LevelFilter::OFF),
684 ]
685 );
686 }
687
688 #[test]
689 // `println!` is only available with `libstd`.
690 #[cfg(feature = "std")]
691 fn size_of_filters() {
692 fn print_sz(s: &str) {
693 let filter = s.parse::<Targets>().expect("filter should parse");
694 println!(
695 "size_of_val({:?})\n -> {}B",
696 s,
697 std::mem::size_of_val(&filter)
698 );
699 }
700
701 print_sz("info");
702
703 print_sz("foo=debug");
704
705 print_sz(
706 "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\
707 crate2=debug,crate3=trace,crate3::mod2::mod1=off",
708 );
709 }
710 }