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