diff --git a/tracing-core/src/subscriber.rs b/tracing-core/src/subscriber.rs index e6565407c..0e0726323 100644 --- a/tracing-core/src/subscriber.rs +++ b/tracing-core/src/subscriber.rs @@ -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 { self.as_ref().downcast_raw(id) } } + +impl Subscriber for 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] + #[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::() { + return Some(self as *const Self as *const _); + } + + self.as_ref().downcast_raw(id) + } +} diff --git a/tracing/tests/subscriber.rs b/tracing/tests/subscriber.rs new file mode 100644 index 000000000..26e88c1a4 --- /dev/null +++ b/tracing/tests/subscriber.rs @@ -0,0 +1,135 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +// +// The alternative would be for each of these tests to be defined in a separate +// file, which is :( +#![cfg(feature = "std")] + +#[macro_use] +extern crate tracing; +use tracing::{ + 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() { + // This test ensures that an event macro within a subscriber + // won't cause an infinite loop of events. + struct TestSubscriber; + impl Subscriber for TestSubscriber { + fn register_callsite(&self, _: &Metadata<'_>) -> Interest { + // Always return sometimes so that `enabled` will be called + // (which can loop). + Interest::sometimes() + } + + fn enabled(&self, meta: &Metadata<'_>) -> bool { + assert!(meta.fields().iter().any(|f| f.name() == "foo")); + event!(Level::TRACE, bar = false); + true + } + + fn new_span(&self, _: &Attributes<'_>) -> Id { + Id::from_u64(0xAAAA) + } + + fn record(&self, _: &Id, _: &Record<'_>) {} + + 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, _: &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 = 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 = 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(); +}