From 1412519fc24b37e4a7f8f671ccde161581ce4d16 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 27 Apr 2021 09:39:51 -0700 Subject: [PATCH] ## Motivation Users may wish to erase the type of a `Collect` implementation, such as when it is dynamically constructed from a complex parameterized type. PR #1358 added a `Collect` implementation for `Box`, allowing the use of type-erased trait objects. In some cases, it may also be useful to share a type-erased collector, _without_ using `Dispatch` --- such as when different sets of `tracing-subscriber` subscribers are layered on one shared collector. ## Solution This branch builds on #1358 by adding an `impl Collect for Arc`. I also added quick tests for both `Arc`ed and `Box`ed collectors. Signed-off-by: Eliza Weisman --- tracing-core/src/collect.rs | 67 ++++++++++++++++++++++++++ tracing/tests/collector.rs | 93 ++++++++++++++++++++++++++++++++++--- 2 files changed, 153 insertions(+), 7 deletions(-) diff --git a/tracing-core/src/collect.rs b/tracing-core/src/collect.rs index 54129cc757..cb09b55076 100644 --- a/tracing-core/src/collect.rs +++ b/tracing-core/src/collect.rs @@ -622,3 +622,70 @@ impl Collect for alloc::boxed::Box { self.as_ref().downcast_raw(id) } } + +#[cfg(feature = "alloc")] +impl Collect for alloc::sync::Arc { + #[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 { + 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] + unsafe fn downcast_raw(&self, id: TypeId) -> Option> { + if id == TypeId::of::() { + return Some(NonNull::from(self).cast()); + } + + self.as_ref().downcast_raw(id) + } +} diff --git a/tracing/tests/collector.rs b/tracing/tests/collector.rs index 2224b1af19..10620c5364 100644 --- a/tracing/tests/collector.rs +++ b/tracing/tests/collector.rs @@ -10,9 +10,15 @@ extern crate tracing; use tracing::{ collect::{with_default, Collect, Interest}, - span, Event, Level, Metadata, + field::display, + span::{Attributes, Id, Record}, + 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() { @@ -32,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(TestCollector, || { event!(Level::TRACE, foo = false); }) } + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn boxed_collector() { + let (collector, handle) = collector::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 collector: Box = Box::new(collector); + + with_default(collector, || { + 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_collector() { + use std::sync::Arc; + + let (collector, handle) = collector::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 collector: Arc = Arc::new(collector); + + // Test using a clone of the `Arc`ed collector + with_default(collector.clone(), || { + let from = "my span"; + let span = span!( + Level::TRACE, + "foo", + bar = format_args!("hello from {}", from) + ); + span.in_scope(|| {}); + }); + + with_default(collector, || { + tracing::info!("hello from my event"); + }); + + handle.assert_finished(); +}