Skip to content

Commit ccfa2cc

Browse files
committed
core: add support for Arc<dyn Subscriber + ...>
## Motivation (#1374) Users may wish to erase the type of a `Subscriber` implementation, such as when it is dynamically constructed from a complex parameterized type. PR #1358 added a `Subscriber` implementation for `Box<dyn Subscriber + Send + Sync + 'static>`, allowing the use of type-erased trait objects. In some cases, it may also be useful to share a type-erased subscriber, _without_ using `Dispatch` --- such as when different sets of `tracing-subscriber` subscribers are layered on one shared subscriber. ## Solution This branch builds on #1358 by adding an `impl Subscriber for Arc<dyn Subscriber + Send + Sync + 'static>`. I also added quick tests for both `Arc`ed and `Box`ed subscribers. Signed-off-by: Eliza Weisman <[email protected]>
1 parent f9a3f17 commit ccfa2cc

File tree

2 files changed

+163
-7
lines changed

2 files changed

+163
-7
lines changed

tracing-core/src/subscriber.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::{span, Event, LevelFilter, Metadata};
44
use crate::stdlib::{
55
any::{Any, TypeId},
66
boxed::Box,
7+
sync::Arc,
78
};
89

910
/// Trait representing the functions required to collect trace data.
@@ -639,3 +640,80 @@ impl Subscriber for Box<dyn Subscriber + Send + Sync + 'static> {
639640
self.as_ref().downcast_raw(id)
640641
}
641642
}
643+
644+
impl Subscriber for Arc<dyn Subscriber + Send + Sync + 'static> {
645+
#[inline]
646+
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
647+
self.as_ref().register_callsite(metadata)
648+
}
649+
650+
#[inline]
651+
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
652+
self.as_ref().enabled(metadata)
653+
}
654+
655+
#[inline]
656+
fn max_level_hint(&self) -> Option<LevelFilter> {
657+
self.as_ref().max_level_hint()
658+
}
659+
660+
#[inline]
661+
fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
662+
self.as_ref().new_span(span)
663+
}
664+
665+
#[inline]
666+
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
667+
self.as_ref().record(span, values)
668+
}
669+
670+
#[inline]
671+
fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
672+
self.as_ref().record_follows_from(span, follows)
673+
}
674+
675+
#[inline]
676+
fn event(&self, event: &Event<'_>) {
677+
self.as_ref().event(event)
678+
}
679+
680+
#[inline]
681+
fn enter(&self, span: &span::Id) {
682+
self.as_ref().enter(span)
683+
}
684+
685+
#[inline]
686+
fn exit(&self, span: &span::Id) {
687+
self.as_ref().exit(span)
688+
}
689+
690+
#[inline]
691+
fn clone_span(&self, id: &span::Id) -> span::Id {
692+
self.as_ref().clone_span(id)
693+
}
694+
695+
#[inline]
696+
fn try_close(&self, id: span::Id) -> bool {
697+
self.as_ref().try_close(id)
698+
}
699+
700+
#[inline]
701+
#[allow(deprecated)]
702+
fn drop_span(&self, id: span::Id) {
703+
self.as_ref().try_close(id);
704+
}
705+
706+
#[inline]
707+
fn current_span(&self) -> span::Current {
708+
self.as_ref().current_span()
709+
}
710+
711+
#[inline]
712+
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
713+
if id == TypeId::of::<Self>() {
714+
return Some(self as *const Self as *const _);
715+
}
716+
717+
self.as_ref().downcast_raw(id)
718+
}
719+
}

tracing/tests/subscriber.rs

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@
99
#[macro_use]
1010
extern crate tracing;
1111
use tracing::{
12-
span,
12+
field::display,
13+
span::{Attributes, Id, Record},
1314
subscriber::{with_default, Interest, Subscriber},
1415
Event, Level, Metadata,
1516
};
1617

18+
mod support;
19+
20+
use self::support::*;
21+
1722
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
1823
#[test]
1924
fn event_macros_dont_infinite_loop() {
@@ -33,25 +38,98 @@ fn event_macros_dont_infinite_loop() {
3338
true
3439
}
3540

36-
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
37-
span::Id::from_u64(0xAAAA)
41+
fn new_span(&self, _: &Attributes<'_>) -> Id {
42+
Id::from_u64(0xAAAA)
3843
}
3944

40-
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
45+
fn record(&self, _: &Id, _: &Record<'_>) {}
4146

42-
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
47+
fn record_follows_from(&self, _: &Id, _: &Id) {}
4348

4449
fn event(&self, event: &Event<'_>) {
4550
assert!(event.metadata().fields().iter().any(|f| f.name() == "foo"));
4651
event!(Level::TRACE, baz = false);
4752
}
4853

49-
fn enter(&self, _: &span::Id) {}
54+
fn enter(&self, _: &Id) {}
5055

51-
fn exit(&self, _: &span::Id) {}
56+
fn exit(&self, _: &Id) {}
5257
}
5358

5459
with_default(TestSubscriber, || {
5560
event!(Level::TRACE, foo = false);
5661
})
5762
}
63+
64+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
65+
#[test]
66+
fn boxed_subscriber() {
67+
let (subscriber, handle) = subscriber::mock()
68+
.new_span(
69+
span::mock().named("foo").with_field(
70+
field::mock("bar")
71+
.with_value(&display("hello from my span"))
72+
.only(),
73+
),
74+
)
75+
.enter(span::mock().named("foo"))
76+
.exit(span::mock().named("foo"))
77+
.drop_span(span::mock().named("foo"))
78+
.done()
79+
.run_with_handle();
80+
let subscriber: Box<dyn Subscriber + Send + Sync + 'static> = Box::new(subscriber);
81+
82+
with_default(subscriber, || {
83+
let from = "my span";
84+
let span = span!(
85+
Level::TRACE,
86+
"foo",
87+
bar = format_args!("hello from {}", from)
88+
);
89+
span.in_scope(|| {});
90+
});
91+
92+
handle.assert_finished();
93+
}
94+
95+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
96+
#[test]
97+
fn arced_subscriber() {
98+
use std::sync::Arc;
99+
100+
let (subscriber, handle) = subscriber::mock()
101+
.new_span(
102+
span::mock().named("foo").with_field(
103+
field::mock("bar")
104+
.with_value(&display("hello from my span"))
105+
.only(),
106+
),
107+
)
108+
.enter(span::mock().named("foo"))
109+
.exit(span::mock().named("foo"))
110+
.drop_span(span::mock().named("foo"))
111+
.event(
112+
event::mock()
113+
.with_fields(field::mock("message").with_value(&display("hello from my event"))),
114+
)
115+
.done()
116+
.run_with_handle();
117+
let subscriber: Arc<dyn Subscriber + Send + Sync + 'static> = Arc::new(subscriber);
118+
119+
// Test using a clone of the `Arc`ed subscriber
120+
with_default(subscriber.clone(), || {
121+
let from = "my span";
122+
let span = span!(
123+
Level::TRACE,
124+
"foo",
125+
bar = format_args!("hello from {}", from)
126+
);
127+
span.in_scope(|| {});
128+
});
129+
130+
with_default(subscriber, || {
131+
tracing::info!("hello from my event");
132+
});
133+
134+
handle.assert_finished();
135+
}

0 commit comments

Comments
 (0)