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 <eliza@buoyant.io>
This commit is contained in:
Eliza Weisman 2021-04-28 15:15:44 -07:00
parent f9a3f17fbb
commit ccfa2cc687
2 changed files with 163 additions and 7 deletions

View File

@ -4,6 +4,7 @@ use crate::{span, Event, LevelFilter, Metadata};
use crate::stdlib::{
any::{Any, TypeId},
boxed::Box,
sync::Arc,
};
/// Trait representing the functions required to collect trace data.
@ -639,3 +640,80 @@ impl Subscriber for Box<dyn Subscriber + Send + Sync + 'static> {
self.as_ref().downcast_raw(id)
}
}
impl Subscriber for Arc<dyn Subscriber + Send + Sync + 'static> {
#[inline]
fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
self.as_ref().register_callsite(metadata)
}
#[inline]
fn enabled(&self, metadata: &Metadata<'_>) -> bool {
self.as_ref().enabled(metadata)
}
#[inline]
fn max_level_hint(&self) -> Option<LevelFilter> {
self.as_ref().max_level_hint()
}
#[inline]
fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
self.as_ref().new_span(span)
}
#[inline]
fn record(&self, span: &span::Id, values: &span::Record<'_>) {
self.as_ref().record(span, values)
}
#[inline]
fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
self.as_ref().record_follows_from(span, follows)
}
#[inline]
fn event(&self, event: &Event<'_>) {
self.as_ref().event(event)
}
#[inline]
fn enter(&self, span: &span::Id) {
self.as_ref().enter(span)
}
#[inline]
fn exit(&self, span: &span::Id) {
self.as_ref().exit(span)
}
#[inline]
fn clone_span(&self, id: &span::Id) -> span::Id {
self.as_ref().clone_span(id)
}
#[inline]
fn try_close(&self, id: span::Id) -> bool {
self.as_ref().try_close(id)
}
#[inline]
#[allow(deprecated)]
fn drop_span(&self, id: span::Id) {
self.as_ref().try_close(id);
}
#[inline]
fn current_span(&self) -> span::Current {
self.as_ref().current_span()
}
#[inline]
unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> {
if id == TypeId::of::<Self>() {
return Some(self as *const Self as *const _);
}
self.as_ref().downcast_raw(id)
}
}

View File

@ -9,11 +9,16 @@
#[macro_use]
extern crate tracing;
use tracing::{
span,
field::display,
span::{Attributes, Id, Record},
subscriber::{with_default, Interest, Subscriber},
Event, Level, Metadata,
};
mod support;
use self::support::*;
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn event_macros_dont_infinite_loop() {
@ -33,25 +38,98 @@ fn event_macros_dont_infinite_loop() {
true
}
fn new_span(&self, _: &span::Attributes<'_>) -> span::Id {
span::Id::from_u64(0xAAAA)
fn new_span(&self, _: &Attributes<'_>) -> Id {
Id::from_u64(0xAAAA)
}
fn record(&self, _: &span::Id, _: &span::Record<'_>) {}
fn record(&self, _: &Id, _: &Record<'_>) {}
fn record_follows_from(&self, _: &span::Id, _: &span::Id) {}
fn record_follows_from(&self, _: &Id, _: &Id) {}
fn event(&self, event: &Event<'_>) {
assert!(event.metadata().fields().iter().any(|f| f.name() == "foo"));
event!(Level::TRACE, baz = false);
}
fn enter(&self, _: &span::Id) {}
fn enter(&self, _: &Id) {}
fn exit(&self, _: &span::Id) {}
fn exit(&self, _: &Id) {}
}
with_default(TestSubscriber, || {
event!(Level::TRACE, foo = false);
})
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn boxed_subscriber() {
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&display("hello from my span"))
.only(),
),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.done()
.run_with_handle();
let subscriber: Box<dyn Subscriber + Send + Sync + 'static> = Box::new(subscriber);
with_default(subscriber, || {
let from = "my span";
let span = span!(
Level::TRACE,
"foo",
bar = format_args!("hello from {}", from)
);
span.in_scope(|| {});
});
handle.assert_finished();
}
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
#[test]
fn arced_subscriber() {
use std::sync::Arc;
let (subscriber, handle) = subscriber::mock()
.new_span(
span::mock().named("foo").with_field(
field::mock("bar")
.with_value(&display("hello from my span"))
.only(),
),
)
.enter(span::mock().named("foo"))
.exit(span::mock().named("foo"))
.drop_span(span::mock().named("foo"))
.event(
event::mock()
.with_fields(field::mock("message").with_value(&display("hello from my event"))),
)
.done()
.run_with_handle();
let subscriber: Arc<dyn Subscriber + Send + Sync + 'static> = Arc::new(subscriber);
// Test using a clone of the `Arc`ed subscriber
with_default(subscriber.clone(), || {
let from = "my span";
let span = span!(
Level::TRACE,
"foo",
bar = format_args!("hello from {}", from)
);
span.in_scope(|| {});
});
with_default(subscriber, || {
tracing::info!("hello from my event");
});
handle.assert_finished();
}