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