tracing_subscriber/
reload.rs

1//! Wrapper for a `Layer` to allow it to be dynamically reloaded.
2//!
3//! This module provides a [`Layer` type] implementing the [`Layer` trait] or [`Filter` trait]
4//! which wraps another type implementing the corresponding trait. This
5//! allows the wrapped type to be replaced with another
6//! instance of that type at runtime.
7//!
8//! This can be used in cases where a subset of `Layer` or `Filter` functionality
9//! should be dynamically reconfigured, such as when filtering directives may
10//! change at runtime. Note that this layer introduces a (relatively small)
11//! amount of overhead, and should thus only be used as needed.
12//!
13//! # Examples
14//!
15//! Reloading a [global filtering](crate::layer#global-filtering) layer:
16//!
17//! ```rust
18//! # use tracing::info;
19//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
20//! let filter = filter::LevelFilter::WARN;
21//! let (filter, reload_handle) = reload::Layer::new(filter);
22//! tracing_subscriber::registry()
23//!   .with(filter)
24//!   .with(fmt::Layer::default())
25//!   .init();
26//! #
27//! # // specifying the Registry type is required
28//! # let _: &reload::Handle<filter::LevelFilter, tracing_subscriber::Registry> = &reload_handle;
29//! #
30//! info!("This will be ignored");
31//! reload_handle.modify(|filter| *filter = filter::LevelFilter::INFO);
32//! info!("This will be logged");
33//! ```
34//!
35//! Reloading a [`Filtered`](crate::filter::Filtered) layer:
36//!
37//! ```rust
38//! # use tracing::info;
39//! use tracing_subscriber::{filter, fmt, reload, prelude::*};
40//! let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN);
41//! let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer);
42//! #
43//! # // specifying the Registry type is required
44//! # let _: &reload::Handle<filter::Filtered<fmt::Layer<tracing_subscriber::Registry>,
45//! # filter::LevelFilter, tracing_subscriber::Registry>,tracing_subscriber::Registry>
46//! # = &reload_handle;
47//! #
48//! tracing_subscriber::registry()
49//!   .with(filtered_layer)
50//!   .init();
51//! info!("This will be ignored");
52//! reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO);
53//! info!("This will be logged");
54//! ```
55//!
56//! ## Note
57//!
58//! The [`Layer`] implementation is unable to implement downcasting functionality,
59//! so certain [`Layer`] will fail to downcast if wrapped in a `reload::Layer`.
60//!
61//! If you only want to be able to dynamically change the
62//! `Filter` on a layer, prefer wrapping that `Filter` in the `reload::Layer`.
63//!
64//! [`Filter` trait]: crate::layer::Filter
65//! [`Layer` type]: Layer
66//! [`Layer` trait]: super::layer::Layer
67use crate::layer;
68use crate::sync::RwLock;
69
70use core::any::TypeId;
71use std::{
72    error, fmt,
73    marker::PhantomData,
74    sync::{Arc, Weak},
75};
76use tracing_core::{
77    callsite, span,
78    subscriber::{Interest, Subscriber},
79    Dispatch, Event, LevelFilter, Metadata,
80};
81
82/// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime.
83#[derive(Debug)]
84pub struct Layer<L, S> {
85    // TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may
86    // eventually wish to replace it with a sharded lock implementation on top
87    // of our internal `RwLock` wrapper type. If possible, we should profile
88    // this first to determine if it's necessary.
89    inner: Arc<RwLock<L>>,
90    _s: PhantomData<fn(S)>,
91}
92
93/// Allows reloading the state of an associated [`Layer`](crate::layer::Layer).
94#[derive(Debug)]
95pub struct Handle<L, S> {
96    inner: Weak<RwLock<L>>,
97    _s: PhantomData<fn(S)>,
98}
99
100/// Indicates that an error occurred when reloading a layer.
101#[derive(Debug)]
102pub struct Error {
103    kind: ErrorKind,
104}
105
106#[derive(Debug)]
107enum ErrorKind {
108    SubscriberGone,
109    Poisoned,
110}
111
112// ===== impl Layer =====
113
114impl<L, S> crate::Layer<S> for Layer<L, S>
115where
116    L: crate::Layer<S> + 'static,
117    S: Subscriber,
118{
119    fn on_register_dispatch(&self, subscriber: &Dispatch) {
120        try_lock!(self.inner.read()).on_register_dispatch(subscriber);
121    }
122
123    fn on_layer(&mut self, subscriber: &mut S) {
124        try_lock!(self.inner.write(), else return).on_layer(subscriber);
125    }
126
127    #[inline]
128    fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
129        try_lock!(self.inner.read(), else return Interest::sometimes()).register_callsite(metadata)
130    }
131
132    #[inline]
133    fn enabled(&self, metadata: &Metadata<'_>, ctx: layer::Context<'_, S>) -> bool {
134        try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
135    }
136
137    #[inline]
138    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
139        try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
140    }
141
142    #[inline]
143    fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
144        try_lock!(self.inner.read()).on_record(span, values, ctx)
145    }
146
147    #[inline]
148    fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: layer::Context<'_, S>) {
149        try_lock!(self.inner.read()).on_follows_from(span, follows, ctx)
150    }
151
152    #[inline]
153    fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool {
154        try_lock!(self.inner.read(), else return false).event_enabled(event, ctx)
155    }
156
157    #[inline]
158    fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) {
159        try_lock!(self.inner.read()).on_event(event, ctx)
160    }
161
162    #[inline]
163    fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
164        try_lock!(self.inner.read()).on_enter(id, ctx)
165    }
166
167    #[inline]
168    fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
169        try_lock!(self.inner.read()).on_exit(id, ctx)
170    }
171
172    #[inline]
173    fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
174        try_lock!(self.inner.read()).on_close(id, ctx)
175    }
176
177    #[inline]
178    fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) {
179        try_lock!(self.inner.read()).on_id_change(old, new, ctx)
180    }
181
182    #[inline]
183    fn max_level_hint(&self) -> Option<LevelFilter> {
184        try_lock!(self.inner.read(), else return None).max_level_hint()
185    }
186
187    #[doc(hidden)]
188    unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
189        // Safety: it is generally unsafe to downcast through a reload, because
190        // the pointer can be invalidated after the lock is dropped.
191        // `NoneLayerMarker` is a special case because it
192        // is never dereferenced.
193        //
194        // Additionally, even if the marker type *is* dereferenced (which it
195        // never will be), the pointer should be valid even if the subscriber
196        // is reloaded, because all `NoneLayerMarker` pointers that we return
197        // actually point to the global static singleton `NoneLayerMarker`,
198        // rather than to a field inside the lock.
199        if id == TypeId::of::<layer::NoneLayerMarker>() {
200            return try_lock!(self.inner.read(), else return None).downcast_raw(id);
201        }
202
203        None
204    }
205}
206
207// ===== impl Filter =====
208
209#[cfg(all(feature = "registry", feature = "std"))]
210#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))]
211impl<S, L> crate::layer::Filter<S> for Layer<L, S>
212where
213    L: crate::layer::Filter<S> + 'static,
214    S: Subscriber,
215{
216    #[inline]
217    fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest {
218        try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata)
219    }
220
221    #[inline]
222    fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool {
223        try_lock!(self.inner.read(), else return false).enabled(metadata, ctx)
224    }
225
226    #[inline]
227    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) {
228        try_lock!(self.inner.read()).on_new_span(attrs, id, ctx)
229    }
230
231    #[inline]
232    fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) {
233        try_lock!(self.inner.read()).on_record(span, values, ctx)
234    }
235
236    #[inline]
237    fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
238        try_lock!(self.inner.read()).on_enter(id, ctx)
239    }
240
241    #[inline]
242    fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) {
243        try_lock!(self.inner.read()).on_exit(id, ctx)
244    }
245
246    #[inline]
247    fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) {
248        try_lock!(self.inner.read()).on_close(id, ctx)
249    }
250
251    #[inline]
252    fn max_level_hint(&self) -> Option<LevelFilter> {
253        try_lock!(self.inner.read(), else return None).max_level_hint()
254    }
255}
256
257impl<L, S> Layer<L, S> {
258    /// Wraps the given [`Layer`] or [`Filter`], returning a `reload::Layer`
259    /// and a `Handle` that allows the inner value to be modified at runtime.
260    ///
261    /// [`Layer`]: crate::layer::Layer
262    /// [`Filter`]: crate::layer::Filter
263    pub fn new(inner: L) -> (Self, Handle<L, S>) {
264        let this = Self {
265            inner: Arc::new(RwLock::new(inner)),
266            _s: PhantomData,
267        };
268        let handle = this.handle();
269        (this, handle)
270    }
271
272    /// Returns a `Handle` that can be used to reload the wrapped [`Layer`] or [`Filter`].
273    ///
274    /// [`Layer`]: crate::layer::Layer
275    /// [`Filter`]: crate::filter::Filter
276    pub fn handle(&self) -> Handle<L, S> {
277        Handle {
278            inner: Arc::downgrade(&self.inner),
279            _s: PhantomData,
280        }
281    }
282}
283
284// ===== impl Handle =====
285
286impl<L, S> Handle<L, S> {
287    /// Replace the current [`Layer`] or [`Filter`] with the provided `new_value`.
288    ///
289    /// [`Handle::reload`] cannot be used with the [`Filtered`] layer; use
290    /// [`Handle::modify`] instead (see [this issue] for additional details).
291    ///
292    /// However, if the _only_ the [`Filter`]  needs to be modified, use
293    /// `reload::Layer` to wrap the `Filter` directly.
294    ///
295    /// [`Layer`]: crate::layer::Layer
296    /// [`Filter`]: crate::layer::Filter
297    /// [`Filtered`]: crate::filter::Filtered
298    ///
299    /// [this issue]: https://github.com/tokio-rs/tracing/issues/1629
300    pub fn reload(&self, new_value: impl Into<L>) -> Result<(), Error> {
301        self.modify(|layer| {
302            *layer = new_value.into();
303        })
304    }
305
306    /// Invokes a closure with a mutable reference to the current layer or filter,
307    /// allowing it to be modified in place.
308    pub fn modify(&self, f: impl FnOnce(&mut L)) -> Result<(), Error> {
309        let inner = self.inner.upgrade().ok_or(Error {
310            kind: ErrorKind::SubscriberGone,
311        })?;
312
313        let mut lock = try_lock!(inner.write(), else return Err(Error::poisoned()));
314        f(&mut *lock);
315        // Release the lock before rebuilding the interest cache, as that
316        // function will lock the new layer.
317        drop(lock);
318
319        callsite::rebuild_interest_cache();
320
321        // If the `log` crate compatibility feature is in use, set `log`'s max
322        // level as well, in case the max `tracing` level changed. We do this
323        // *after* rebuilding the interest cache, as that's when the `tracing`
324        // max level filter is re-computed.
325        #[cfg(feature = "tracing-log")]
326        tracing_log::log::set_max_level(tracing_log::AsLog::as_log(
327            &crate::filter::LevelFilter::current(),
328        ));
329
330        Ok(())
331    }
332
333    /// Returns a clone of the layer or filter's current value if it still exists.
334    /// Otherwise, if the subscriber has been dropped, returns `None`.
335    pub fn clone_current(&self) -> Option<L>
336    where
337        L: Clone,
338    {
339        self.with_current(L::clone).ok()
340    }
341
342    /// Invokes a closure with a borrowed reference to the current layer or filter,
343    /// returning the result (or an error if the subscriber no longer exists).
344    pub fn with_current<T>(&self, f: impl FnOnce(&L) -> T) -> Result<T, Error> {
345        let inner = self.inner.upgrade().ok_or(Error {
346            kind: ErrorKind::SubscriberGone,
347        })?;
348        let inner = try_lock!(inner.read(), else return Err(Error::poisoned()));
349        Ok(f(&*inner))
350    }
351}
352
353impl<L, S> Clone for Handle<L, S> {
354    fn clone(&self) -> Self {
355        Handle {
356            inner: self.inner.clone(),
357            _s: PhantomData,
358        }
359    }
360}
361
362// ===== impl Error =====
363
364impl Error {
365    fn poisoned() -> Self {
366        Self {
367            kind: ErrorKind::Poisoned,
368        }
369    }
370
371    /// Returns `true` if this error occurred because the layer was poisoned by
372    /// a panic on another thread.
373    pub fn is_poisoned(&self) -> bool {
374        matches!(self.kind, ErrorKind::Poisoned)
375    }
376
377    /// Returns `true` if this error occurred because the `Subscriber`
378    /// containing the reloadable layer was dropped.
379    pub fn is_dropped(&self) -> bool {
380        matches!(self.kind, ErrorKind::SubscriberGone)
381    }
382}
383
384impl fmt::Display for Error {
385    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386        let msg = match self.kind {
387            ErrorKind::SubscriberGone => "subscriber no longer exists",
388            ErrorKind::Poisoned => "lock poisoned",
389        };
390        f.pad(msg)
391    }
392}
393
394impl error::Error for Error {}