From 0a48a8f06a711afffa31a8424f07ed6d20f4e886 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 23 Mar 2022 15:26:04 -0700 Subject: [PATCH] subscriber: add minimal #![no_std] support (#1648) Backports #1648 from `master`. Depends on #1649 ## Motivation Presently, the `tracing-subscriber` crate requires the Rust standard library and doesn't build with `#![no_std]` targets. For the most part, this is fine, as much of `tracing-subscriber` inherently depends on `std` APIs. However, `tracing-subscriber` also contains some key abstractions that are necessary for interoperability: the `Layer` and `LookupSpan` traits. Since these traits are in `tracing-subscriber`, `no-std` users cannot currently access them. Some of the other utilities in this crate, such as the field visitor combinators, may also be useful for `#![no_std]` projects. ## Solution This branch adds "std" and "alloc" feature flags to `tracing-subscriber`, for conditionally enabling `libstd` and `liballoc`, respectively. The `registry`, `fmt`, `EnvFilter`, and `reload` APIs all require libstd, and cannot be implemented without it, but the core `Layer` and `LookupSpan` traits are now available with `#![no_std]`. Fixes #999 --- tracing-subscriber/src/filter/directive.rs | 26 ++++-- tracing-subscriber/src/filter/env/mod.rs | 1 - tracing-subscriber/src/filter/filter_fn.rs | 83 ++++++++++--------- tracing-subscriber/src/filter/mod.rs | 41 +++++---- tracing-subscriber/src/filter/targets.rs | 20 ++++- tracing-subscriber/src/lib.rs | 2 +- tracing-subscriber/src/registry/mod.rs | 31 +++---- tracing-subscriber/src/registry/sharded.rs | 6 +- tracing-subscriber/src/subscribe/context.rs | 78 ++++++----------- tracing-subscriber/src/subscribe/layered.rs | 26 +++--- tracing-subscriber/src/subscribe/mod.rs | 57 +++++++------ tracing-subscriber/src/subscribe/tests.rs | 29 +++---- .../tests/layer_filters/boxed.rs | 43 ++++++++++ 13 files changed, 249 insertions(+), 194 deletions(-) create mode 100644 tracing-subscriber/tests/layer_filters/boxed.rs diff --git a/tracing-subscriber/src/filter/directive.rs b/tracing-subscriber/src/filter/directive.rs index 5c6b38eb4b..a402633923 100644 --- a/tracing-subscriber/src/filter/directive.rs +++ b/tracing-subscriber/src/filter/directive.rs @@ -1,5 +1,9 @@ use crate::filter::level::{self, LevelFilter}; -use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr}; +use alloc::{ + string::String, + vec::{self, Vec}, +}; +use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr}; use tracing_core::Metadata; /// Indicates that a string could not be parsed as a filtering directive. #[derive(Debug)] @@ -35,7 +39,8 @@ pub(in crate::filter) trait Match { #[derive(Debug)] enum ParseErrorKind { - Field(Box), + #[cfg(feature = "std")] + Field(Box), Level(level::ParseError), Other(Option<&'static str>), } @@ -43,11 +48,12 @@ enum ParseErrorKind { // === impl DirectiveSet === impl DirectiveSet { + #[cfg(feature = "std")] pub(crate) fn is_empty(&self) -> bool { self.directives.is_empty() } - pub(crate) fn iter(&self) -> std::slice::Iter<'_, T> { + pub(crate) fn iter(&self) -> slice::Iter<'_, T> { self.directives.iter() } } @@ -118,7 +124,7 @@ impl IntoIterator for DirectiveSet { #[cfg(feature = "smallvec")] type IntoIter = smallvec::IntoIter<[T; 8]>; #[cfg(not(feature = "smallvec"))] - type IntoIter = std::vec::IntoIter; + type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.directives.into_iter() @@ -353,6 +359,7 @@ impl FromStr for StaticDirective { // === impl ParseError === impl ParseError { + #[cfg(feature = "std")] pub(crate) fn new() -> Self { ParseError { kind: ParseErrorKind::Other(None), @@ -372,17 +379,19 @@ impl fmt::Display for ParseError { ParseErrorKind::Other(None) => f.pad("invalid filter directive"), ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg), ParseErrorKind::Level(ref l) => l.fmt(f), + #[cfg(feature = "std")] ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e), } } } -impl Error for ParseError { +#[cfg(feature = "std")] +impl std::error::Error for ParseError { fn description(&self) -> &str { "invalid filter directive" } - fn source(&self) -> Option<&(dyn Error + 'static)> { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self.kind { ParseErrorKind::Other(_) => None, ParseErrorKind::Level(ref l) => Some(l), @@ -391,8 +400,9 @@ impl Error for ParseError { } } -impl From> for ParseError { - fn from(e: Box) -> Self { +#[cfg(feature = "std")] +impl From> for ParseError { + fn from(e: Box) -> Self { Self { kind: ParseErrorKind::Field(e), } diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs index 3cf9a44350..b95c8c3700 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -102,7 +102,6 @@ use tracing_core::{ /// [`Metadata`]: tracing_core::Metadata /// [`Targets`]: crate::filter::Targets #[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] -#[cfg(feature = "env-filter")] #[derive(Debug)] pub struct EnvFilter { statics: directive::Statics, diff --git a/tracing-subscriber/src/filter/filter_fn.rs b/tracing-subscriber/src/filter/filter_fn.rs index 9961161e94..4155bf40a0 100644 --- a/tracing-subscriber/src/filter/filter_fn.rs +++ b/tracing-subscriber/src/filter/filter_fn.rs @@ -1,5 +1,3 @@ -#[cfg(feature = "registry")] -use crate::subscribe::Filter; use crate::{ filter::LevelFilter, subscribe::{Context, Subscribe}, @@ -323,25 +321,6 @@ where } } -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -impl Filter for FilterFn -where - F: Fn(&Metadata<'_>) -> bool, -{ - fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, C>) -> bool { - self.is_enabled(metadata) - } - - fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { - self.is_callsite_enabled(metadata) - } - - fn max_level_hint(&self) -> Option { - self.max_level_hint - } -} - impl Subscribe for FilterFn where F: Fn(&Metadata<'_>) -> bool + 'static, @@ -661,26 +640,6 @@ where } } -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -impl Filter for DynFilterFn -where - F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool, - R: Fn(&'static Metadata<'static>) -> Interest, -{ - fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, C>) -> bool { - self.is_enabled(metadata, cx) - } - - fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { - self.is_callsite_enabled(metadata) - } - - fn max_level_hint(&self) -> Option { - self.max_level_hint - } -} - impl Subscribe for DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool + 'static, @@ -741,6 +700,48 @@ where } } +// === PLF impls === + +feature! { + #![all(feature = "registry", feature = "std")] + use crate::subscribe::Filter; + + impl Filter for FilterFn + where + F: Fn(&Metadata<'_>) -> bool, + { + fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, C>) -> bool { + self.is_enabled(metadata) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } + } + + impl Filter for DynFilterFn + where + F: Fn(&Metadata<'_>, &Context<'_, C>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, + { + fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, C>) -> bool { + self.is_enabled(metadata, cx) + } + + fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + self.is_callsite_enabled(metadata) + } + + fn max_level_hint(&self) -> Option { + self.max_level_hint + } + } +} + fn is_below_max_level(hint: &Option, metadata: &Metadata<'_>) -> bool { hint.as_ref() .map(|hint| metadata.level() <= hint) diff --git a/tracing-subscriber/src/filter/mod.rs b/tracing-subscriber/src/filter/mod.rs index a409d055c4..f43e37fa41 100644 --- a/tracing-subscriber/src/filter/mod.rs +++ b/tracing-subscriber/src/filter/mod.rs @@ -8,37 +8,44 @@ //! //! [`subscribe` module documentation]: crate::subscribe#filtering-with-subscribers //! [`Subscribe`]: crate::subscribe -mod directive; -#[cfg(feature = "env-filter")] -mod env; mod filter_fn; mod level; -#[cfg(feature = "registry")] -mod subscriber_filters; -pub mod targets; -pub use self::directive::ParseError; +feature! { + #![all(feature = "env-filter", feature = "std")] + mod env; + pub use self::env::*; +} + +feature! { + #![all(feature = "registry", feature = "std")] + mod subscriber_filters; + pub use self::subscriber_filters::*; +} + pub use self::filter_fn::*; #[cfg(not(feature = "registry"))] pub(crate) use self::has_plf_stubs::*; -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub use self::subscriber_filters::*; - pub use self::level::{LevelFilter, ParseError as LevelParseError}; -#[cfg(feature = "env-filter")] -#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] -pub use self::env::*; +#[cfg(not(all(feature = "registry", feature = "std")))] +pub(crate) use self::has_plf_stubs::*; + +feature! { + #![any(feature = "std", feature = "alloc")] + pub mod targets; + pub use self::targets::Targets; -pub use self::targets::Targets; + mod directive; + pub use self::directive::ParseError; +} /// Stub implementations of the per-layer-fitler detection functions for when the /// `registry` feature is disabled. -#[cfg(not(feature = "registry"))] +#[cfg(not(all(feature = "registry", feature = "std")))] mod has_plf_stubs { - pub(crate) fn is_plf_downcast_marker(_: std::any::TypeId) -> bool { + pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool { false } diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs index 705d8002e0..efe1cd8db8 100644 --- a/tracing-subscriber/src/filter/targets.rs +++ b/tracing-subscriber/src/filter/targets.rs @@ -13,8 +13,11 @@ use crate::{ }, subscribe, }; -use std::{ +#[cfg(not(feature = "std"))] +use alloc::string::String; +use core::{ iter::{Extend, FilterMap, FromIterator}, + slice, str::FromStr, }; use tracing_core::{Collect, Interest, Metadata}; @@ -462,7 +465,7 @@ impl Iterator for IntoIter { #[derive(Debug)] pub struct Iter<'a>( FilterMap< - std::slice::Iter<'a, StaticDirective>, + slice::Iter<'a, StaticDirective>, fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>, >, ); @@ -494,6 +497,17 @@ impl<'a> Iterator for Iter<'a> { mod tests { use super::*; + feature! { + #![not(feature = "std")] + use alloc::{vec, vec::Vec, string::ToString}; + + // `dbg!` is only available with `libstd`; just nop it out when testing + // with alloc only. + macro_rules! dbg { + ($x:expr) => { $x } + } + } + fn expect_parse(s: &str) -> Targets { match dbg!(s).parse::() { Err(e) => panic!("string {:?} did not parse successfully: {}", s, e), @@ -643,6 +657,8 @@ mod tests { } #[test] + // `println!` is only available with `libstd`. + #[cfg(feature = "std")] fn size_of_filters() { fn print_sz(s: &str) { let filter = s.parse::().expect("filter should parse"); diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 62ee58743e..357ec72403 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -129,7 +129,7 @@ //! [`time` crate]: https://crates.io/crates/time //! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html //! [`libstd`]: https://doc.rust-lang.org/std/index.html -#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.12")] +#![doc(html_root_url = "https://docs.rs/tracing-subscriber/0.2.25")] #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico", diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs index 26df85856d..837be2fd97 100644 --- a/tracing-subscriber/src/registry/mod.rs +++ b/tracing-subscriber/src/registry/mod.rs @@ -60,8 +60,6 @@ //! [lookup]: crate::subscribe::Context::span() use core::fmt::Debug; -#[cfg(feature = "registry")] -use crate::filter::FilterId; use tracing_core::{field::FieldSet, span::Id, Metadata}; feature! { @@ -80,6 +78,8 @@ feature! { pub use sharded::Data; pub use sharded::Registry; + + use crate::filter::FilterId; } /// Provides access to stored span data. @@ -228,7 +228,7 @@ pub struct Scope<'a, R> { registry: &'a R, next: Option, - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } @@ -237,7 +237,6 @@ feature! { #[cfg(not(feature = "smallvec"))] use alloc::vec::{self, Vec}; - use core::{fmt,iter}; /// An iterator over the parents of a span, ordered from root to leaf. @@ -319,18 +318,20 @@ where loop { let curr = self.registry.span(self.next.as_ref()?)?; - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] let curr = curr.with_filter(self.filter); self.next = curr.data.parent().cloned(); // If the `Scope` is filtered, check if the current span is enabled // by the selected filter ID. - #[cfg(feature = "registry")] - if !curr.is_enabled_for(self.filter) { - // The current span in the chain is disabled for this - // filter. Try its parent. - continue; + #[cfg(all(feature = "registry", feature = "std"))] + { + if !curr.is_enabled_for(self.filter) { + // The current span in the chain is disabled for this + // filter. Try its parent. + continue; + } } return Some(curr); @@ -407,7 +408,7 @@ where let id = self.data.parent()?; let data = self.registry.span_data(id)?; - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] { // move these into mut bindings if the registry feature is enabled, // since they may be mutated in the loop. @@ -428,7 +429,7 @@ where } } - #[cfg(not(feature = "registry"))] + #[cfg(not(all(feature = "registry", feature = "std")))] Some(Self { registry: self.registry, data, @@ -532,7 +533,7 @@ where self.data.extensions_mut() } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn try_with_filter(self, filter: FilterId) -> Option { if self.is_enabled_for(filter) { return Some(self.with_filter(filter)); @@ -542,13 +543,13 @@ where } #[inline] - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool { self.data.is_enabled_for(filter) } #[inline] - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] fn with_filter(self, filter: FilterId) -> Self { Self { filter, ..self } } diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs index 736082234c..ba6f4afe58 100644 --- a/tracing-subscriber/src/registry/sharded.rs +++ b/tracing-subscriber/src/registry/sharded.rs @@ -86,8 +86,9 @@ use tracing_core::{ /// [ot]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spancontext /// [fields]: https://docs.rs/tracing-core/latest/tracing-core/field/index.html /// [stored span data]: crate::registry::SpanData::extensions_mut -#[derive(Debug)] +#[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] +#[derive(Debug)] pub struct Registry { spans: Pool, current_spans: ThreadLocal>, @@ -103,6 +104,9 @@ pub struct Registry { /// /// [`Subscriber`s]: crate::Subscribe /// [extensions]: Extensions +/// [`Registry`]: struct.Registry.html +#[cfg(feature = "registry")] +#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] #[derive(Debug)] pub struct Data<'a> { /// Immutable reference to the pooled `DataInner` entry. diff --git a/tracing-subscriber/src/subscribe/context.rs b/tracing-subscriber/src/subscribe/context.rs index f0563e7fef..a7fe8d65c6 100644 --- a/tracing-subscriber/src/subscribe/context.rs +++ b/tracing-subscriber/src/subscribe/context.rs @@ -1,7 +1,8 @@ use tracing_core::{collect::Collect, metadata::Metadata, span, Event}; use crate::registry::{self, LookupSpan, SpanRef}; -#[cfg(feature = "registry")] + +#[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; /// Represents information about the current context provided to [subscriber]s by the /// wrapped [collector]. @@ -43,36 +44,10 @@ pub struct Context<'a, S> { /// [`Filtered`]: crate::filter::Filtered /// [`FilterId`]: crate::filter::FilterId /// [`and`]: crate::filter::FilterId::and - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } -/// An iterator over the [stored data] for all the spans in the -/// current context, starting the root of the trace tree and ending with -/// the current span. -/// -/// This is returned by [`Context::scope`]. -/// -/// [stored data]: ../registry/struct.SpanRef.html -/// [`Context::scope`]: struct.Context.html#method.scope -#[deprecated(note = "renamed to crate::registry::ScopeFromRoot", since = "0.2.19")] -#[derive(Debug)] -pub struct Scope<'a, L>(std::iter::Flatten>>) -where - L: LookupSpan<'a>; - -#[allow(deprecated)] -impl<'a, L> Iterator for Scope<'a, L> -where - L: LookupSpan<'a>, -{ - type Item = SpanRef<'a, L>; - - fn next(&mut self) -> Option { - self.0.next() - } -} - // === impl Context === impl<'a, C> Context<'a, C> @@ -241,7 +216,7 @@ where { let span = self.subscriber.as_ref()?.span(id)?; - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] return span.try_with_filter(self.filter); #[cfg(not(feature = "registry"))] @@ -295,22 +270,23 @@ where ); // If we found a span, and our per-subscriber filter enables it, return that - // span! - #[cfg(feature = "registry")] - if let Some(span) = span?.try_with_filter(self.filter) { - Some(span) - } else { - // Otherwise, the span at the *top* of the stack is disabled by - // per-subscriber filtering, but there may be additional spans in the stack. - // - // Currently, `LookupSpan` doesn't have a nice way of exposing access to - // the whole span stack. However, if we can downcast the innermost - // collector to a a `Registry`, we can iterate over its current span - // stack. - // - // TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is - // implemented, change this to use that instead... - self.lookup_current_filtered(subscriber) + #[cfg(all(feature = "registry", feature = "std"))] + { + if let Some(span) = span?.try_with_filter(self.filter) { + Some(span) + } else { + // Otherwise, the span at the *top* of the stack is disabled by + // per-subscriber filtering, but there may be additional spans in the stack. + // + // Currently, `LookupSpan` doesn't have a nice way of exposing access to + // the whole span stack. However, if we can downcast the innermost + // collector to a a `Registry`, we can iterate over its current span + // stack. + // + // TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is + // implemented, change this to use that instead... + self.lookup_current_filtered(subscriber) + } } #[cfg(not(feature = "registry"))] @@ -324,7 +300,7 @@ where // factored out to prevent the loop and (potentially-recursive) subscriber // downcasting from being inlined if `lookup_current` is inlined. #[inline(never)] - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] fn lookup_current_filtered<'lookup>( &self, subscriber: &'lookup C, @@ -401,7 +377,7 @@ where Some(self.event_span(event)?.scope()) } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn with_filter(self, filter: FilterId) -> Self { // If we already have our own `FilterId`, combine it with the provided // one. That way, the new `FilterId` will consider a span to be disabled @@ -413,7 +389,7 @@ where Self { filter, ..self } } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool where C: for<'lookup> LookupSpan<'lookup>, @@ -421,7 +397,7 @@ where self.is_enabled_inner(span, filter).unwrap_or(false) } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option where C: for<'lookup> LookupSpan<'lookup>, @@ -433,7 +409,7 @@ where } } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option where C: for<'lookup> LookupSpan<'lookup>, @@ -460,7 +436,7 @@ impl<'a, S> Clone for Context<'a, S> { Context { subscriber, - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] filter: self.filter, } } diff --git a/tracing-subscriber/src/subscribe/layered.rs b/tracing-subscriber/src/subscribe/layered.rs index cf6b65fba3..2b55fa6780 100644 --- a/tracing-subscriber/src/subscribe/layered.rs +++ b/tracing-subscriber/src/subscribe/layered.rs @@ -9,9 +9,9 @@ use crate::{ registry::LookupSpan, subscribe::{Context, Subscribe}, }; -#[cfg(feature = "registry")] +#[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; -use std::{any::TypeId, fmt, marker::PhantomData, ptr::NonNull}; +use core::{any::TypeId, cmp, fmt, marker::PhantomData, ptr::NonNull}; /// A [collector] composed of a [collector] wrapped by one or more /// [subscriber]s. @@ -142,16 +142,16 @@ where } fn try_close(&self, id: span::Id) -> bool { - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] let subscriber = &self.inner as &dyn Collect; - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] let mut guard = subscriber .downcast_ref::() .map(|registry| registry.start_close(id.clone())); if self.inner.try_close(id.clone()) { // If we have a registry's close guard, indicate that the span is // closing. - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] { if let Some(g) = guard.as_mut() { g.is_closing() @@ -341,7 +341,7 @@ where self.inner.span_data(id) } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] fn register_filter(&mut self) -> FilterId { self.inner.register_filter() } @@ -362,9 +362,9 @@ where C: Collect, { pub(super) fn new(subscriber: A, inner: B, inner_has_subscriber_filter: bool) -> Self { - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] let inner_is_registry = TypeId::of::() == TypeId::of::(); - #[cfg(not(feature = "registry"))] + #[cfg(not(all(feature = "registry", feature = "std")))] let inner_is_registry = false; let inner_has_subscriber_filter = inner_has_subscriber_filter || inner_is_registry; @@ -427,14 +427,12 @@ where outer_hint: Option, inner_hint: Option, ) -> Option { - use std::cmp::max; - if self.inner_is_registry { return outer_hint; } if self.has_subscriber_filter && self.inner_has_subscriber_filter { - return Some(max(outer_hint?, inner_hint?)); + return Some(cmp::max(outer_hint?, inner_hint?)); } if self.has_subscriber_filter && inner_hint.is_none() { @@ -445,7 +443,7 @@ where return None; } - max(outer_hint, inner_hint) + cmp::max(outer_hint, inner_hint) } } @@ -455,13 +453,13 @@ where B: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] let alt = f.alternate(); let mut s = f.debug_struct("Layered"); // These additional fields are more verbose and usually only necessary // for internal debugging purposes, so only print them if alternate mode // is enabled. - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] if alt { s.field("inner_is_registry", &self.inner_is_registry) .field("has_subscriber_filter", &self.has_subscriber_filter) diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs index 32bd488bc7..8a1c5b911f 100644 --- a/tracing-subscriber/src/subscribe/mod.rs +++ b/tracing-subscriber/src/subscribe/mod.rs @@ -499,17 +499,21 @@ //! [`LevelFilter`]: crate::filter::LevelFilter //! [feat]: crate#feature-flags use crate::filter; -use std::{ - any::TypeId, - ops::{Deref, DerefMut}, - ptr::NonNull, -}; + use tracing_core::{ collect::{Collect, Interest}, metadata::Metadata, span, Event, LevelFilter, }; +use core::{any::TypeId, ptr::NonNull}; + +feature! { + #![feature = "alloc"] + use alloc::boxed::Box; + use core::ops::{Deref, DerefMut}; +} + mod context; mod layered; pub use self::{context::*, layered::*}; @@ -875,8 +879,8 @@ where /// A per-[`Subscribe`] filter that determines whether a span or event is enabled /// for an individual subscriber. -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[cfg(all(feature = "registry", feature = "std"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] #[cfg_attr(docsrs, doc(notable_trait))] pub trait Filter { /// Returns `true` if this subscriber is interested in a span or event with the @@ -1205,6 +1209,7 @@ where } } +#[cfg(any(feature = "std", feature = "alloc"))] macro_rules! subscriber_impl_body { () => { #[inline] @@ -1217,21 +1222,11 @@ macro_rules! subscriber_impl_body { self.deref().on_new_span(attrs, id, ctx) } - #[inline] - fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { - self.deref().register_callsite(metadata) - } - #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, C>) -> bool { self.deref().enabled(metadata, ctx) } - #[inline] - fn max_level_hint(&self) -> Option { - self.deref().max_level_hint() - } - #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, C>) { self.deref().on_record(span, values, ctx) @@ -1275,19 +1270,23 @@ macro_rules! subscriber_impl_body { }; } -impl Subscribe for Box -where - S: Subscribe, - C: Collect, -{ - subscriber_impl_body! {} -} +feature! { + #![any(feature = "std", feature = "alloc")] -impl Subscribe for Box> -where - C: Collect, -{ - subscriber_impl_body! {} + impl Subscribe for Box + where + S: Subscribe, + C: Collect, + { + subscriber_impl_body! {} + } + + impl Subscribe for Box> + where + C: Collect, + { + subscriber_impl_body! {} + } } // === impl CollectExt === diff --git a/tracing-subscriber/src/subscribe/tests.rs b/tracing-subscriber/src/subscribe/tests.rs index 1a1ebcbbf9..53a24431a8 100644 --- a/tracing-subscriber/src/subscribe/tests.rs +++ b/tracing-subscriber/src/subscribe/tests.rs @@ -20,8 +20,9 @@ impl Collect for NopCollector { fn event(&self, _: &Event<'_>) {} fn enter(&self, _: &span::Id) {} fn exit(&self, _: &span::Id) {} + fn current_span(&self) -> span::Current { - span::Current::unknown() + todo!() } } @@ -36,15 +37,15 @@ impl Subscribe for NopSubscriber2 {} /// A layer that holds a string. /// /// Used to test that pointers returned by downcasting are actually valid. -struct StringSubscriber(String); +struct StringSubscriber(&'static str); impl Subscribe for StringSubscriber {} -struct StringSubscriber2(String); +struct StringSubscriber2(&'static str); impl Subscribe for StringSubscriber2 {} -struct StringSubscriber3(String); +struct StringSubscriber3(&'static str); impl Subscribe for StringSubscriber3 {} -pub(crate) struct StringCollector(String); +pub(crate) struct StringCollector(&'static str); impl Collect for StringCollector { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { @@ -100,30 +101,30 @@ fn downcasts_to_subscriber() { let s = NopSubscriber .and_then(NopSubscriber) .and_then(NopSubscriber) - .with_collector(StringCollector("subscriber".into())); + .with_collector(StringCollector("subscriber")); let subscriber = ::downcast_ref::(&s).expect("collector should downcast"); - assert_eq!(&subscriber.0, "subscriber"); + assert_eq!(subscriber.0, "subscriber"); } #[test] fn downcasts_to_layer() { - let s = StringSubscriber("layer_1".into()) - .and_then(StringSubscriber2("layer_2".into())) - .and_then(StringSubscriber3("layer_3".into())) + let s = StringSubscriber("layer_1") + .and_then(StringSubscriber2("layer_2")) + .and_then(StringSubscriber3("layer_3")) .with_collector(NopCollector); let layer = ::downcast_ref::(&s).expect("subscriber 1 should downcast"); - assert_eq!(&layer.0, "layer_1"); + assert_eq!(layer.0, "layer_1"); let layer = ::downcast_ref::(&s).expect("subscriber 2 should downcast"); - assert_eq!(&layer.0, "layer_2"); + assert_eq!(layer.0, "layer_2"); let layer = ::downcast_ref::(&s).expect("subscriber 3 should downcast"); - assert_eq!(&layer.0, "layer_3"); + assert_eq!(layer.0, "layer_3"); } -#[cfg(feature = "registry")] +#[cfg(all(feature = "registry", feature = "std"))] mod registry_tests { use super::*; use crate::registry::LookupSpan; diff --git a/tracing-subscriber/tests/layer_filters/boxed.rs b/tracing-subscriber/tests/layer_filters/boxed.rs new file mode 100644 index 0000000000..25e3cdc905 --- /dev/null +++ b/tracing-subscriber/tests/layer_filters/boxed.rs @@ -0,0 +1,43 @@ +use super::*; +use tracing_subscriber::{filter, prelude::*, Layer}; + +fn subscribe() -> (ExpectSubscriber, subscriber::MockHandle) { + subscribe::mock().done().run_with_handle() +} + +fn filter() -> filter::DynFilterFn { + // Use dynamic filter fn to disable interest caching and max-level hints, + // allowing us to put all of these tests in the same file. + filter::dynamic_filter_fn(|_, _| false) +} + +/// reproduces https://github.com/tokio-rs/tracing/issues/1563#issuecomment-921363629 +#[test] +fn box_works() { + let (subscribe, handle) = subscribe(); + let subscribe = Box::new(subscribe.with_filter(filter())); + + let _guard = tracing_subscriber::registry().with(subscribe).set_default(); + + for i in 0..2 { + tracing::info!(i); + } + + handle.assert_finished(); +} + +/// the same as `box_works` but with a type-erased `Box`. +#[test] +fn dyn_box_works() { + let (subscribe, handle) = subscribe(); + let subscribe: Box + Send + Sync + 'static> = + Box::new(subscribe.with_filter(filter())); + + let _guard = tracing_subscriber::registry().with(subscribe).set_default(); + + for i in 0..2 { + tracing::info!(i); + } + + handle.assert_finished(); +}