Skip to content

Commit 9284e10

Browse files
guswynnhawkw
authored andcommitted
subscriber: implement Filter for reload::Layer (#2159)
## Motivation The `reload` layer doesn't (and can't) implement downcasting correctly, which breaks certain layers like the opentelemetry one. ## Solution Most uses of the `reload` module (including mine) are just to change the filter. Therefore, this PR implements `Filter` for `reload::Layer` to allow users to not need to wrap the whole layer trait. Another advantage of this is that the common-case critical sections are shortened.
1 parent 240cd50 commit 9284e10

File tree

2 files changed

+136
-20
lines changed

2 files changed

+136
-20
lines changed

tracing-subscriber/src/reload.rs

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Wrapper for a `Layer` to allow it to be dynamically reloaded.
22
//!
33
//! This module provides a [`Layer` type] which wraps another type implementing
4-
//! the [`Layer` trait], allowing the wrapped type to be replaced with another
5-
//! instance of that type at runtime.
4+
//! the [`Layer`][layer-trait] or [`Filter`] traits, allowing the wrapped type
5+
//! to be replaced with another instance of that type at runtime.
66
//!
77
//! This can be used in cases where a subset of `Subscriber` functionality
88
//! should be dynamically reconfigured, such as when filtering directives may
@@ -52,8 +52,17 @@
5252
//! info!("This will be logged");
5353
//! ```
5454
//!
55+
//! ## Note
56+
//!
57+
//! The [`Layer`][layer-trait] implementation is unable to implement downcasting functionality,
58+
//! so certain `Layer`s will fail to reload if wrapped in a `reload::Layer`.
59+
//!
60+
//! If you only want to be able to dynamically change the
61+
//! [`Filter`] on your layer, prefer wrapping that [`Filter`] in the `reload::Layer`.
62+
//!
5563
//! [`Layer` type]: Layer
56-
//! [`Layer` trait]: super::layer::Layer
64+
//! [layer-trait]: super::layer::Layer
65+
//! [`Filter`]: super::layer::Filter
5766
use crate::layer;
5867
use crate::sync::RwLock;
5968

@@ -68,7 +77,11 @@ use tracing_core::{
6877
Event, Metadata,
6978
};
7079

71-
/// Wraps a `Layer`, allowing it to be reloaded dynamically at runtime.
80+
/// Wraps a [`Layer`] or [`Filter`], allowing it to be reloaded dynamically at
81+
/// runtime.
82+
///
83+
/// [`Filter`]: crate::layer::Filter
84+
/// [`Layer`]: crate::Layer
7285
#[derive(Debug)]
7386
pub struct Layer<L, S> {
7487
// TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may
@@ -160,13 +173,56 @@ where
160173
}
161174
}
162175

163-
impl<L, S> Layer<L, S>
176+
#[cfg(all(feature = "registry", feature = "std"))]
177+
#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
178+
impl<F, S> crate::layer::Filter<S> for Layer<F, S>
164179
where
165-
L: crate::Layer<S> + 'static,
180+
F: crate::layer::Filter<S> + 'static,
166181
S: Subscriber,
167182
{
168-
/// Wraps the given `Layer`, returning a `Layer` and a `Handle` that allows
183+
#[inline]
184+
fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
185+
try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata)
186+
}
187+
188+
#[inline]
189+
fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool {
190+
try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
191+
}
192+
193+
#[inline]
194+
fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
195+
try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
196+
}
197+
198+
#[inline]
199+
fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
200+
try_lock!(self.inner.read()).on_record(span, values, ctx)
201+
}
202+
203+
#[inline]
204+
fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
205+
try_lock!(self.inner.read()).on_enter(id, ctx)
206+
}
207+
208+
#[inline]
209+
fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
210+
try_lock!(self.inner.read()).on_exit(id, ctx)
211+
}
212+
213+
#[inline]
214+
fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
215+
try_lock!(self.inner.read()).on_close(id, ctx)
216+
}
217+
}
218+
219+
impl<L, S> Layer<L, S> {
220+
/// Wraps the given `Subscribe` or `Filter`,
221+
/// returning a subscriber or filter and a `Handle` that allows
169222
/// the inner type to be modified at runtime.
223+
///
224+
/// [`Filter`]: crate::subscribe::Filter
225+
/// [`Subscribe`]: crate::Subscribe
170226
pub fn new(inner: L) -> (Self, Handle<L, S>) {
171227
let this = Self {
172228
inner: Arc::new(RwLock::new(inner)),
@@ -187,20 +243,21 @@ where
187243

188244
// ===== impl Handle =====
189245

190-
impl<L, S> Handle<L, S>
191-
where
192-
L: crate::Layer<S> + 'static,
193-
S: Subscriber,
194-
{
195-
/// Replace the current layer with the provided `new_layer`.
246+
impl<L, S> Handle<L, S> {
247+
/// Replace the current subscriber or filter with the provided `new_value`.
248+
///
249+
/// [`Handle::reload`] cannot be used with the [`Filtered`] `Layer`; use
250+
/// [`Handle::modify`] instead (see [this issue] for additional details).
251+
///
252+
/// However, if the _only_ the [`Filter`] needs to be modified,
253+
/// use `reload::Layer` to wrap the `Filter` directly.
196254
///
197-
/// **Warning:** The [`Filtered`](crate::filter::Filtered) type currently can't be changed
198-
/// at runtime via the [`Handle::reload`] method.
199-
/// Use the [`Handle::modify`] method to change the filter instead.
200-
/// (see <https://github.com/tokio-rs/tracing/issues/1629>)
201-
pub fn reload(&self, new_layer: impl Into<L>) -> Result<(), Error> {
202-
self.modify(|layer| {
203-
*layer = new_layer.into();
255+
/// [`Filtered`]: crate::filter::Filtered
256+
/// [`Filter`]: crate::layer::Filter
257+
/// [this issue]: https://github.com/tokio-rs/tracing/issues/1629
258+
pub fn reload(&self, new_value: impl Into<L>) -> Result<(), Error> {
259+
self.modify(|object| {
260+
*object = new_value.into();
204261
})
205262
}
206263

tracing-subscriber/tests/reload.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ impl Subscriber for NopSubscriber {
2929
fn exit(&self, _: &Id) {}
3030
}
3131

32+
pub struct NopSubscriber;
33+
impl<S: Collect> tracing_subscriber::Subscribe<S> for NopSubscriber {
34+
fn register_callsite(&self, _m: &Metadata<'_>) -> Interest {
35+
Interest::sometimes()
36+
}
37+
38+
fn enabled(&self, _m: &Metadata<'_>, _: subscribe::Context<'_, S>) -> bool {
39+
true
40+
}
41+
}
42+
3243
#[test]
3344
fn reload_handle() {
3445
static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
@@ -79,3 +90,51 @@ fn reload_handle() {
7990
assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1);
8091
})
8192
}
93+
94+
#[test]
95+
fn reload_filter() {
96+
static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
97+
static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0);
98+
99+
enum Filter {
100+
One,
101+
Two,
102+
}
103+
104+
impl<S: Collect> tracing_subscriber::subscribe::Filter<S> for Filter {
105+
fn enabled(&self, m: &Metadata<'_>, _: &subscribe::Context<'_, S>) -> bool {
106+
println!("ENABLED: {:?}", m);
107+
match self {
108+
Filter::One => FILTER1_CALLS.fetch_add(1, Ordering::SeqCst),
109+
Filter::Two => FILTER2_CALLS.fetch_add(1, Ordering::SeqCst),
110+
};
111+
true
112+
}
113+
}
114+
fn event() {
115+
tracing::trace!("my event");
116+
}
117+
118+
let (filter, handle) = Subscriber::new(Filter::One);
119+
120+
let dispatcher = tracing_core::dispatch::Dispatch::new(
121+
tracing_subscriber::registry().with(NopSubscriber.with_filter(filter)),
122+
);
123+
124+
tracing_core::dispatch::with_default(&dispatcher, || {
125+
assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 0);
126+
assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0);
127+
128+
event();
129+
130+
assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1);
131+
assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0);
132+
133+
handle.reload(Filter::Two).expect("should reload");
134+
135+
event();
136+
137+
assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1);
138+
assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1);
139+
})
140+
}

0 commit comments

Comments
 (0)