]>
Commit | Line | Data |
---|---|---|
c295e0f8 XL |
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 | }; | |
a2a8927a XL |
16 | #[cfg(not(feature = "std"))] |
17 | use alloc::string::String; | |
18 | use core::{ | |
c295e0f8 | 19 | iter::{Extend, FilterMap, FromIterator}, |
a2a8927a | 20 | slice, |
c295e0f8 XL |
21 | str::FromStr, |
22 | }; | |
5099ac24 | 23 | use tracing_core::{Interest, Level, Metadata, Subscriber}; |
c295e0f8 XL |
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 | |
04454e1e | 114 | /// [global filter][global]: |
c295e0f8 XL |
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}; | |
3c0e092e | 124 | /// # fn docs() -> Result<(), Box<dyn std::error::Error>> { |
c295e0f8 XL |
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 | } | |
5099ac24 FG |
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 | } | |
c295e0f8 XL |
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< | |
a2a8927a | 497 | slice::Iter<'a, StaticDirective>, |
c295e0f8 XL |
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 | ||
a2a8927a XL |
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 | ||
c295e0f8 XL |
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] | |
a2a8927a XL |
689 | // `println!` is only available with `libstd`. |
690 | #[cfg(feature = "std")] | |
c295e0f8 XL |
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 | } |