1 | //! Filter combinators |
2 | use crate::layer::{Context, Filter}; |
3 | use std::{cmp, fmt, marker::PhantomData}; |
4 | use tracing_core::{ |
5 | span::{Attributes, Id, Record}, |
6 | subscriber::Interest, |
7 | LevelFilter, Metadata, |
8 | }; |
9 | |
10 | /// Combines two [`Filter`]s so that spans and events are enabled if and only if |
11 | /// *both* filters return `true`. |
12 | /// |
13 | /// This type is typically returned by the [`FilterExt::and`] method. See that |
14 | /// method's documentation for details. |
15 | /// |
16 | /// [`Filter`]: crate::layer::Filter |
17 | /// [`FilterExt::and`]: crate::filter::FilterExt::and |
18 | pub struct And<A, B, S> { |
19 | a: A, |
20 | b: B, |
21 | _s: PhantomData<fn(S)>, |
22 | } |
23 | |
24 | /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter |
25 | /// returns `true`. |
26 | /// |
27 | /// This type is typically returned by the [`FilterExt::or`] method. See that |
28 | /// method's documentation for details. |
29 | /// |
30 | /// [`Filter`]: crate::layer::Filter |
31 | /// [`FilterExt::or`]: crate::filter::FilterExt::or |
32 | pub struct Or<A, B, S> { |
33 | a: A, |
34 | b: B, |
35 | _s: PhantomData<fn(S)>, |
36 | } |
37 | |
38 | /// Inverts the result of a [`Filter`]. |
39 | /// |
40 | /// If the wrapped filter would enable a span or event, it will be disabled. If |
41 | /// it would disable a span or event, that span or event will be enabled. |
42 | /// |
43 | /// This type is typically returned by the [`FilterExt::not`] method. See that |
44 | /// method's documentation for details. |
45 | /// |
46 | /// [`Filter`]: crate::layer::Filter |
47 | /// [`FilterExt::not`]: crate::filter::FilterExt::not |
48 | pub struct Not<A, S> { |
49 | a: A, |
50 | _s: PhantomData<fn(S)>, |
51 | } |
52 | |
53 | // === impl And === |
54 | |
55 | impl<A, B, S> And<A, B, S> |
56 | where |
57 | A: Filter<S>, |
58 | B: Filter<S>, |
59 | { |
60 | /// Combines two [`Filter`]s so that spans and events are enabled if and only if |
61 | /// *both* filters return `true`. |
62 | /// |
63 | /// # Examples |
64 | /// |
65 | /// Enabling spans or events if they have both a particular target *and* are |
66 | /// above a certain level: |
67 | /// |
68 | /// ```ignore |
69 | /// use tracing_subscriber::{ |
70 | /// filter::{filter_fn, LevelFilter, combinator::And}, |
71 | /// prelude::*, |
72 | /// }; |
73 | /// |
74 | /// // Enables spans and events with targets starting with `interesting_target`: |
75 | /// let target_filter = filter_fn(|meta| { |
76 | /// meta.target().starts_with("interesting_target" ) |
77 | /// }); |
78 | /// |
79 | /// // Enables spans and events with levels `INFO` and below: |
80 | /// let level_filter = LevelFilter::INFO; |
81 | /// |
82 | /// // Combine the two filters together so that a span or event is only enabled |
83 | /// // if *both* filters would enable it: |
84 | /// let filter = And::new(level_filter, target_filter); |
85 | /// |
86 | /// tracing_subscriber::registry() |
87 | /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) |
88 | /// .init(); |
89 | /// |
90 | /// // This event will *not* be enabled: |
91 | /// tracing::info!("an event with an uninteresting target" ); |
92 | /// |
93 | /// // This event *will* be enabled: |
94 | /// tracing::info!(target: "interesting_target" , "a very interesting event" ); |
95 | /// |
96 | /// // This event will *not* be enabled: |
97 | /// tracing::debug!(target: "interesting_target" , "interesting debug event..." ); |
98 | /// ``` |
99 | /// |
100 | /// [`Filter`]: crate::layer::Filter |
101 | pub(crate) fn new(a: A, b: B) -> Self { |
102 | Self { |
103 | a, |
104 | b, |
105 | _s: PhantomData, |
106 | } |
107 | } |
108 | } |
109 | |
110 | impl<A, B, S> Filter<S> for And<A, B, S> |
111 | where |
112 | A: Filter<S>, |
113 | B: Filter<S>, |
114 | { |
115 | #[inline ] |
116 | fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { |
117 | self.a.enabled(meta, cx) && self.b.enabled(meta, cx) |
118 | } |
119 | |
120 | fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { |
121 | let a = self.a.callsite_enabled(meta); |
122 | if a.is_never() { |
123 | return a; |
124 | } |
125 | |
126 | let b = self.b.callsite_enabled(meta); |
127 | |
128 | if !b.is_always() { |
129 | return b; |
130 | } |
131 | |
132 | a |
133 | } |
134 | |
135 | fn max_level_hint(&self) -> Option<LevelFilter> { |
136 | // If either hint is `None`, return `None`. Otherwise, return the most restrictive. |
137 | cmp::min(self.a.max_level_hint(), self.b.max_level_hint()) |
138 | } |
139 | |
140 | #[inline ] |
141 | fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { |
142 | self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx) |
143 | } |
144 | |
145 | #[inline ] |
146 | fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { |
147 | self.a.on_new_span(attrs, id, ctx.clone()); |
148 | self.b.on_new_span(attrs, id, ctx) |
149 | } |
150 | |
151 | #[inline ] |
152 | fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { |
153 | self.a.on_record(id, values, ctx.clone()); |
154 | self.b.on_record(id, values, ctx); |
155 | } |
156 | |
157 | #[inline ] |
158 | fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { |
159 | self.a.on_enter(id, ctx.clone()); |
160 | self.b.on_enter(id, ctx); |
161 | } |
162 | |
163 | #[inline ] |
164 | fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { |
165 | self.a.on_exit(id, ctx.clone()); |
166 | self.b.on_exit(id, ctx); |
167 | } |
168 | |
169 | #[inline ] |
170 | fn on_close(&self, id: Id, ctx: Context<'_, S>) { |
171 | self.a.on_close(id.clone(), ctx.clone()); |
172 | self.b.on_close(id, ctx); |
173 | } |
174 | } |
175 | |
176 | impl<A, B, S> Clone for And<A, B, S> |
177 | where |
178 | A: Clone, |
179 | B: Clone, |
180 | { |
181 | fn clone(&self) -> Self { |
182 | Self { |
183 | a: self.a.clone(), |
184 | b: self.b.clone(), |
185 | _s: PhantomData, |
186 | } |
187 | } |
188 | } |
189 | |
190 | impl<A, B, S> fmt::Debug for And<A, B, S> |
191 | where |
192 | A: fmt::Debug, |
193 | B: fmt::Debug, |
194 | { |
195 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
196 | f&mut DebugStruct<'_, '_>.debug_struct("And" ) |
197 | .field("a" , &self.a) |
198 | .field(name:"b" , &self.b) |
199 | .finish() |
200 | } |
201 | } |
202 | |
203 | // === impl Or === |
204 | |
205 | impl<A, B, S> Or<A, B, S> |
206 | where |
207 | A: Filter<S>, |
208 | B: Filter<S>, |
209 | { |
210 | /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter |
211 | /// returns `true`. |
212 | /// |
213 | /// # Examples |
214 | /// |
215 | /// Enabling spans and events at the `INFO` level and above, and all spans |
216 | /// and events with a particular target: |
217 | /// |
218 | /// ```ignore |
219 | /// use tracing_subscriber::{ |
220 | /// filter::{filter_fn, LevelFilter, combinator::Or}, |
221 | /// prelude::*, |
222 | /// }; |
223 | /// |
224 | /// // Enables spans and events with targets starting with `interesting_target`: |
225 | /// let target_filter = filter_fn(|meta| { |
226 | /// meta.target().starts_with("interesting_target" ) |
227 | /// }); |
228 | /// |
229 | /// // Enables spans and events with levels `INFO` and below: |
230 | /// let level_filter = LevelFilter::INFO; |
231 | /// |
232 | /// // Combine the two filters together so that a span or event is enabled |
233 | /// // if it is at INFO or lower, or if it has a target starting with |
234 | /// // `interesting_target`. |
235 | /// let filter = Or::new(level_filter, target_filter); |
236 | /// |
237 | /// tracing_subscriber::registry() |
238 | /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) |
239 | /// .init(); |
240 | /// |
241 | /// // This event will *not* be enabled: |
242 | /// tracing::debug!("an uninteresting event" ); |
243 | /// |
244 | /// // This event *will* be enabled: |
245 | /// tracing::info!("an uninteresting INFO event" ); |
246 | /// |
247 | /// // This event *will* be enabled: |
248 | /// tracing::info!(target: "interesting_target" , "a very interesting event" ); |
249 | /// |
250 | /// // This event *will* be enabled: |
251 | /// tracing::debug!(target: "interesting_target" , "interesting debug event..." ); |
252 | /// ``` |
253 | /// |
254 | /// Enabling a higher level for a particular target by using `Or` in |
255 | /// conjunction with the [`And`] combinator: |
256 | /// |
257 | /// ```ignore |
258 | /// use tracing_subscriber::{ |
259 | /// filter::{filter_fn, LevelFilter, combinator}, |
260 | /// prelude::*, |
261 | /// }; |
262 | /// |
263 | /// // This filter will enable spans and events with targets beginning with |
264 | /// // `my_crate`: |
265 | /// let my_crate = filter_fn(|meta| { |
266 | /// meta.target().starts_with("my_crate" ) |
267 | /// }); |
268 | /// |
269 | /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter |
270 | /// // that will enable the `INFO` level and lower for spans and events with |
271 | /// // `my_crate` targets: |
272 | /// let filter = combinator::And::new(my_crate, LevelFilter::INFO); |
273 | /// |
274 | /// // If a span or event *doesn't* have a target beginning with |
275 | /// // `my_crate`, enable it if it has the `WARN` level or lower: |
276 | /// // let filter = combinator::Or::new(filter, LevelFilter::WARN); |
277 | /// |
278 | /// tracing_subscriber::registry() |
279 | /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) |
280 | /// .init(); |
281 | /// ``` |
282 | /// |
283 | /// [`Filter`]: crate::layer::Filter |
284 | pub(crate) fn new(a: A, b: B) -> Self { |
285 | Self { |
286 | a, |
287 | b, |
288 | _s: PhantomData, |
289 | } |
290 | } |
291 | } |
292 | |
293 | impl<A, B, S> Filter<S> for Or<A, B, S> |
294 | where |
295 | A: Filter<S>, |
296 | B: Filter<S>, |
297 | { |
298 | #[inline ] |
299 | fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { |
300 | self.a.enabled(meta, cx) || self.b.enabled(meta, cx) |
301 | } |
302 | |
303 | fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { |
304 | let a = self.a.callsite_enabled(meta); |
305 | let b = self.b.callsite_enabled(meta); |
306 | |
307 | // If either filter will always enable the span or event, return `always`. |
308 | if a.is_always() || b.is_always() { |
309 | return Interest::always(); |
310 | } |
311 | |
312 | // Okay, if either filter will sometimes enable the span or event, |
313 | // return `sometimes`. |
314 | if a.is_sometimes() || b.is_sometimes() { |
315 | return Interest::sometimes(); |
316 | } |
317 | |
318 | debug_assert!( |
319 | a.is_never() && b.is_never(), |
320 | "if neither filter was `always` or `sometimes`, both must be `never` (a= {:?}; b= {:?})" , |
321 | a, |
322 | b, |
323 | ); |
324 | Interest::never() |
325 | } |
326 | |
327 | fn max_level_hint(&self) -> Option<LevelFilter> { |
328 | // If either hint is `None`, return `None`. Otherwise, return the less restrictive. |
329 | Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?)) |
330 | } |
331 | |
332 | #[inline ] |
333 | fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { |
334 | self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx) |
335 | } |
336 | |
337 | #[inline ] |
338 | fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { |
339 | self.a.on_new_span(attrs, id, ctx.clone()); |
340 | self.b.on_new_span(attrs, id, ctx) |
341 | } |
342 | |
343 | #[inline ] |
344 | fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { |
345 | self.a.on_record(id, values, ctx.clone()); |
346 | self.b.on_record(id, values, ctx); |
347 | } |
348 | |
349 | #[inline ] |
350 | fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { |
351 | self.a.on_enter(id, ctx.clone()); |
352 | self.b.on_enter(id, ctx); |
353 | } |
354 | |
355 | #[inline ] |
356 | fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { |
357 | self.a.on_exit(id, ctx.clone()); |
358 | self.b.on_exit(id, ctx); |
359 | } |
360 | |
361 | #[inline ] |
362 | fn on_close(&self, id: Id, ctx: Context<'_, S>) { |
363 | self.a.on_close(id.clone(), ctx.clone()); |
364 | self.b.on_close(id, ctx); |
365 | } |
366 | } |
367 | |
368 | impl<A, B, S> Clone for Or<A, B, S> |
369 | where |
370 | A: Clone, |
371 | B: Clone, |
372 | { |
373 | fn clone(&self) -> Self { |
374 | Self { |
375 | a: self.a.clone(), |
376 | b: self.b.clone(), |
377 | _s: PhantomData, |
378 | } |
379 | } |
380 | } |
381 | |
382 | impl<A, B, S> fmt::Debug for Or<A, B, S> |
383 | where |
384 | A: fmt::Debug, |
385 | B: fmt::Debug, |
386 | { |
387 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
388 | f&mut DebugStruct<'_, '_>.debug_struct("Or" ) |
389 | .field("a" , &self.a) |
390 | .field(name:"b" , &self.b) |
391 | .finish() |
392 | } |
393 | } |
394 | |
395 | // === impl Not === |
396 | |
397 | impl<A, S> Not<A, S> |
398 | where |
399 | A: Filter<S>, |
400 | { |
401 | /// Inverts the result of a [`Filter`]. |
402 | /// |
403 | /// If the wrapped filter would enable a span or event, it will be disabled. If |
404 | /// it would disable a span or event, that span or event will be enabled. |
405 | /// |
406 | /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] |
407 | /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as |
408 | /// filters which do not implement filtering on event field values will return |
409 | /// the default `true` even for events that their [`enabled`] method disables. |
410 | /// |
411 | /// Consider a normal filter defined as: |
412 | /// |
413 | /// ```ignore (pseudo-code) |
414 | /// // for spans |
415 | /// match callsite_enabled() { |
416 | /// ALWAYS => on_span(), |
417 | /// SOMETIMES => if enabled() { on_span() }, |
418 | /// NEVER => (), |
419 | /// } |
420 | /// // for events |
421 | /// match callsite_enabled() { |
422 | /// ALWAYS => on_event(), |
423 | /// SOMETIMES => if enabled() && event_enabled() { on_event() }, |
424 | /// NEVER => (), |
425 | /// } |
426 | /// ``` |
427 | /// |
428 | /// and an inverted filter defined as: |
429 | /// |
430 | /// ```ignore (pseudo-code) |
431 | /// // for spans |
432 | /// match callsite_enabled() { |
433 | /// ALWAYS => (), |
434 | /// SOMETIMES => if !enabled() { on_span() }, |
435 | /// NEVER => on_span(), |
436 | /// } |
437 | /// // for events |
438 | /// match callsite_enabled() { |
439 | /// ALWAYS => (), |
440 | /// SOMETIMES => if !enabled() { on_event() }, |
441 | /// NEVER => on_event(), |
442 | /// } |
443 | /// ``` |
444 | /// |
445 | /// A proper inversion would do `!(enabled() && event_enabled())` (or |
446 | /// `!enabled() || !event_enabled()`), but because of the implicit `&&` |
447 | /// relation between `enabled` and `event_enabled`, it is difficult to |
448 | /// short circuit and not call the wrapped `event_enabled`. |
449 | /// |
450 | /// A combinator which remembers the result of `enabled` in order to call |
451 | /// `event_enabled` only when `enabled() == true` is possible, but requires |
452 | /// additional thread-local mutable state to support a very niche use case. |
453 | // |
454 | // Also, it'd mean the wrapped layer's `enabled()` always gets called and |
455 | // globally applied to events where it doesn't today, since we can't know |
456 | // what `event_enabled` will say until we have the event to call it with. |
457 | /// |
458 | /// [`Filter`]: crate::layer::Filter |
459 | /// [`enabled`]: crate::layer::Filter::enabled |
460 | /// [`event_enabled`]: crate::layer::Filter::event_enabled |
461 | /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled |
462 | pub(crate) fn new(a: A) -> Self { |
463 | Self { a, _s: PhantomData } |
464 | } |
465 | } |
466 | |
467 | impl<A, S> Filter<S> for Not<A, S> |
468 | where |
469 | A: Filter<S>, |
470 | { |
471 | #[inline ] |
472 | fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { |
473 | !self.a.enabled(meta, cx) |
474 | } |
475 | |
476 | fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { |
477 | match self.a.callsite_enabled(meta) { |
478 | i if i.is_always() => Interest::never(), |
479 | i if i.is_never() => Interest::always(), |
480 | _ => Interest::sometimes(), |
481 | } |
482 | } |
483 | |
484 | fn max_level_hint(&self) -> Option<LevelFilter> { |
485 | // TODO(eliza): figure this out??? |
486 | None |
487 | } |
488 | |
489 | #[inline ] |
490 | fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { |
491 | // Never disable based on event_enabled; we "disabled" it in `enabled`, |
492 | // so the `not` has already been applied and filtered this not out. |
493 | let _ = (event, cx); |
494 | true |
495 | } |
496 | |
497 | #[inline ] |
498 | fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { |
499 | self.a.on_new_span(attrs, id, ctx); |
500 | } |
501 | |
502 | #[inline ] |
503 | fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { |
504 | self.a.on_record(id, values, ctx.clone()); |
505 | } |
506 | |
507 | #[inline ] |
508 | fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { |
509 | self.a.on_enter(id, ctx); |
510 | } |
511 | |
512 | #[inline ] |
513 | fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { |
514 | self.a.on_exit(id, ctx); |
515 | } |
516 | |
517 | #[inline ] |
518 | fn on_close(&self, id: Id, ctx: Context<'_, S>) { |
519 | self.a.on_close(id, ctx); |
520 | } |
521 | } |
522 | |
523 | impl<A, S> Clone for Not<A, S> |
524 | where |
525 | A: Clone, |
526 | { |
527 | fn clone(&self) -> Self { |
528 | Self { |
529 | a: self.a.clone(), |
530 | _s: PhantomData, |
531 | } |
532 | } |
533 | } |
534 | |
535 | impl<A, S> fmt::Debug for Not<A, S> |
536 | where |
537 | A: fmt::Debug, |
538 | { |
539 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
540 | f.debug_tuple(name:"Not" ).field(&self.a).finish() |
541 | } |
542 | } |
543 | |