Skip to content

Commit 4adc0a3

Browse files
authored
core: don't use NoCollector as local placeholder (#2001)
## Motivation Currently, it is not actually possible to use `set_default(NoCollector)` or similar to temporarily disable the global default collector (see #1999). This is because `NoCollector` is currently used as a placeholder value when the thread-local cell that stores the current scoped default collector is initialized. Therefore, we currently check if the current scoped collector is `NoCollector`, and if it is, we fall back to returning the global default instead. This was fine, _when `NoCollector` was a private internal type only_. However, PR #1549 makes `NoCollector` into a public API type. When users can publicly construct `NoCollector` instances, it makes sense to want to be able to use `NoCollector` to disable the current collector. This is not possible when there is a global default set, because the local default being `NoCollector` 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 `NoCollector`, we will return `NoCollector` 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 `NoCollector`. I've also added a test reproducing #1999. Fixes #1999 Signed-off-by: Eliza Weisman <[email protected]>
1 parent 63015eb commit 4adc0a3

File tree

2 files changed

+57
-17
lines changed

2 files changed

+57
-17
lines changed

tracing-core/src/dispatch.rs

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ enum Kind<T> {
179179
#[cfg(feature = "std")]
180180
thread_local! {
181181
static CURRENT_STATE: State = State {
182-
default: RefCell::new(Dispatch::none()),
182+
default: RefCell::new(None),
183183
can_enter: Cell::new(true),
184184
};
185185
}
@@ -212,7 +212,7 @@ static NO_COLLECTOR: NoCollector = NoCollector::new();
212212
#[cfg(feature = "std")]
213213
struct State {
214214
/// This thread's current default dispatcher.
215-
default: RefCell<Dispatch>,
215+
default: RefCell<Option<Dispatch>>,
216216
/// Whether or not we can currently begin dispatching a trace event.
217217
///
218218
/// This is set to `false` when functions such as `enter`, `exit`, `event`,
@@ -409,11 +409,12 @@ where
409409
let _guard = Entered(&state.can_enter);
410410

411411
let mut default = state.default.borrow_mut();
412+
let default = default
413+
// if the local default for this thread has never been set,
414+
// populate it with the global default, so we don't have to
415+
// keep getting the global on every `get_default_slow` call.
416+
.get_or_insert_with(|| get_global().clone());
412417

413-
if default.is::<NoCollector>() {
414-
// don't redo this call on the next check
415-
*default = get_global().clone();
416-
}
417418
return f(&*default);
418419
}
419420

@@ -811,7 +812,25 @@ impl Default for Dispatch {
811812

812813
impl fmt::Debug for Dispatch {
813814
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
814-
f.pad("Dispatch(...)")
815+
match &self.collector {
816+
#[cfg(feature = "alloc")]
817+
Kind::Global(collector) => f
818+
.debug_tuple("Dispatch::Global")
819+
.field(&format_args!("{:p}", collector))
820+
.finish(),
821+
822+
#[cfg(feature = "alloc")]
823+
Kind::Scoped(collector) => f
824+
.debug_tuple("Dispatch::Scoped")
825+
.field(&format_args!("{:p}", collector))
826+
.finish(),
827+
828+
#[cfg(not(feature = "alloc"))]
829+
collector => f
830+
.debug_tuple("Dispatch::Global")
831+
.field(&format_args!("{:p}", collector))
832+
.finish(),
833+
}
815834
}
816835
}
817836

@@ -854,7 +873,13 @@ impl State {
854873
let prior = CURRENT_STATE
855874
.try_with(|state| {
856875
state.can_enter.set(true);
857-
state.default.replace(new_dispatch)
876+
state
877+
.default
878+
.replace(Some(new_dispatch))
879+
// if the scoped default was not set on this thread, set the
880+
// `prior` default to the global default to populate the
881+
// scoped default when unsetting *this* default
882+
.unwrap_or_else(|| get_global().clone())
858883
})
859884
.ok();
860885
EXISTS.store(true, Ordering::Release);
@@ -878,14 +903,10 @@ impl State {
878903
impl<'a> Entered<'a> {
879904
#[inline]
880905
fn current(&self) -> RefMut<'a, Dispatch> {
881-
let mut default = self.0.default.borrow_mut();
882-
883-
if default.is::<NoCollector>() {
884-
// don't redo this call on the next check
885-
*default = get_global().clone();
886-
}
887-
888-
default
906+
let default = self.0.default.borrow_mut();
907+
RefMut::map(default, |default| {
908+
default.get_or_insert_with(|| get_global().clone())
909+
})
889910
}
890911
}
891912

@@ -910,7 +931,7 @@ impl Drop for DefaultGuard {
910931
// lead to the drop of a collector which, in the process,
911932
// could then also attempt to access the same thread local
912933
// state -- causing a clash.
913-
let prev = CURRENT_STATE.try_with(|state| state.default.replace(dispatch));
934+
let prev = CURRENT_STATE.try_with(|state| state.default.replace(Some(dispatch)));
914935
drop(prev)
915936
}
916937
}

tracing/tests/no_collector.rs

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

0 commit comments

Comments
 (0)