Skip to content

Commit 8f1222c

Browse files
committed
core: don't use NoSubscriber as local placeholder (#2001)
## Motivation Currently, it is not actually possible to use `set_default(NoSubscriber)` or similar to temporarily disable the global default subscriber (see #1999). This is because `NoSubscriber` is currently used as a placeholder value when the thread-local cell that stores the current scoped default subscriber is initialized. Therefore, we currently check if the current scoped subscriber is `NoSubscriber`, and if it is, we fall back to returning the global default instead. This was fine, _when `NoSubscriber` was a private internal type only_. However, PR #1549 makes `NoSubscriber` into a public API type. When users can publicly construct `NoSubscriber` instances, it makes sense to want to be able to use `NoSubscriber` to disable the current subscriber. This is not possible when there is a global default set, because the local default being `NoSubscriber` will cause the global default to be returned. ## Solution This branch changes the thread-local cell to store an `Option<Dispatch>` instead, and use the `None` case to indicate no local default is set. This way, when the local default is explicitly set to `NoSubscriber`, we will return `NoSubscriber` rather than falling back. This may also be a slight performance improvement, because we now check if there's no global default by checking if the `Option` is `None`, rather than downcasting it to a `NoSubscriber`. I've also added a test reproducing #1999. Fixes #1999 Signed-off-by: Eliza Weisman <[email protected]>
1 parent b817ca0 commit 8f1222c

File tree

2 files changed

+34
-15
lines changed

2 files changed

+34
-15
lines changed

tracing-core/src/dispatcher.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ pub struct Dispatch {
160160
#[cfg(feature = "std")]
161161
thread_local! {
162162
static CURRENT_STATE: State = State {
163-
default: RefCell::new(Dispatch::none()),
163+
default: RefCell::new(None),
164164
can_enter: Cell::new(true),
165165
};
166166
}
@@ -178,7 +178,7 @@ static mut GLOBAL_DISPATCH: Option<Dispatch> = None;
178178
#[cfg(feature = "std")]
179179
struct State {
180180
/// This thread's current default dispatcher.
181-
default: RefCell<Dispatch>,
181+
default: RefCell<Option<Dispatch>>,
182182
/// Whether or not we can currently begin dispatching a trace event.
183183
///
184184
/// This is set to `false` when functions such as `enter`, `exit`, `event`,
@@ -641,7 +641,9 @@ impl Default for Dispatch {
641641

642642
impl fmt::Debug for Dispatch {
643643
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644-
f.pad("Dispatch(...)")
644+
f.debug_tuple("Dispatch")
645+
.field(&format_args!("{:p}", self.subscriber))
646+
.finish()
645647
}
646648
}
647649

@@ -682,7 +684,13 @@ impl State {
682684
let prior = CURRENT_STATE
683685
.try_with(|state| {
684686
state.can_enter.set(true);
685-
state.default.replace(new_dispatch)
687+
state
688+
.default
689+
.replace(Some(new_dispatch))
690+
// if the scoped default was not set on this thread, set the
691+
// `prior` default to the global default to populate the
692+
// scoped default when unsetting *this* default
693+
.unwrap_or_else(|| get_global().cloned().unwrap_or_else(Dispatch::none))
686694
})
687695
.ok();
688696
EXISTS.store(true, Ordering::Release);
@@ -705,16 +713,10 @@ impl State {
705713
impl<'a> Entered<'a> {
706714
#[inline]
707715
fn current(&self) -> RefMut<'a, Dispatch> {
708-
let mut default = self.0.default.borrow_mut();
709-
710-
if default.is::<NoSubscriber>() {
711-
if let Some(global) = get_global() {
712-
// don't redo this call on the next check
713-
*default = global.clone();
714-
}
715-
}
716-
717-
default
716+
let default = self.0.default.borrow_mut();
717+
RefMut::map(default, |default| {
718+
default.get_or_insert_with(|| get_global().cloned().unwrap_or_else(Dispatch::none))
719+
})
718720
}
719721
}
720722

@@ -738,7 +740,7 @@ impl Drop for DefaultGuard {
738740
// lead to the drop of a subscriber which, in the process,
739741
// could then also attempt to access the same thread local
740742
// state -- causing a clash.
741-
let prev = CURRENT_STATE.try_with(|state| state.default.replace(dispatch));
743+
let prev = CURRENT_STATE.try_with(|state| state.default.replace(Some(dispatch)));
742744
drop(prev)
743745
}
744746
}

tracing/tests/no_subscriber.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#![cfg(feature = "std")]
2+
3+
use tracing::subscriber::{self, NoSubscriber};
4+
5+
mod support;
6+
7+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
8+
#[test]
9+
fn no_subscriber_disables_global() {
10+
// Reproduces https://github.com/tokio-rs/tracing/issues/1999
11+
let (subscriber, handle) = support::subscriber::mock().done().run_with_handle();
12+
subscriber::set_global_default(subscriber).expect("setting global default must succeed");
13+
subscriber::with_default(NoSubscriber::default(), || {
14+
tracing::info!("this should not be recorded");
15+
});
16+
handle.assert_finished();
17+
}

0 commit comments

Comments
 (0)