From 485fd48512b0f9a448799eaab0bdf6b7eb5fb946 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Tue, 19 Oct 2021 14:55:53 -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 Signed-off-by: Eliza Weisman --- .github/workflows/CI.yml | 8 +- tracing-opentelemetry/Cargo.toml | 2 +- tracing-subscriber/Cargo.toml | 6 +- tracing-subscriber/src/field/debug.rs | 24 +- tracing-subscriber/src/field/delimited.rs | 3 +- tracing-subscriber/src/field/display.rs | 24 +- tracing-subscriber/src/field/mod.rs | 26 +- tracing-subscriber/src/filter/directive.rs | 27 +- tracing-subscriber/src/filter/env/mod.rs | 17 +- tracing-subscriber/src/filter/filter_fn.rs | 85 +-- tracing-subscriber/src/filter/mod.rs | 44 +- tracing-subscriber/src/filter/targets.rs | 20 +- tracing-subscriber/src/fmt/fmt_layer.rs | 18 +- tracing-subscriber/src/fmt/mod.rs | 24 +- tracing-subscriber/src/fmt/writer.rs | 11 +- tracing-subscriber/src/layer/context.rs | 81 +-- tracing-subscriber/src/layer/layered.rs | 28 +- tracing-subscriber/src/layer/mod.rs | 504 +++++++++--------- tracing-subscriber/src/layer/tests.rs | 40 +- tracing-subscriber/src/lib.rs | 87 ++- tracing-subscriber/src/macros.rs | 14 + tracing-subscriber/src/prelude.rs | 7 +- tracing-subscriber/src/registry/extensions.rs | 2 + tracing-subscriber/src/registry/mod.rs | 285 ++++------ tracing-subscriber/src/registry/sharded.rs | 4 +- tracing-subscriber/src/util.rs | 37 +- .../tests/layer_filters/boxed.rs | 31 -- tracing-subscriber/tests/utils.rs | 1 + 28 files changed, 724 insertions(+), 736 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 7d7e7af508..255437923c 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -243,9 +243,15 @@ jobs: working-directory: tracing # this skips running doctests under the `--no-default-features` flag, # as rustdoc isn't aware of cargo's feature flags. - - name: "Test tracing-subscriber with all features disabled" + - name: "Test tracing-subscriber no-std support" run: cargo test --lib --tests --no-default-features working-directory: tracing-subscriber + - name: "Test tracing-subscriber with liballoc only" + run: cargo test --lib --tests --no-default-features --features "alloc" + working-directory: tracing-subscriber + - name: "Test tracing-subscriber with no default features" + run: cargo test --lib --tests --no-default-features --features "std" + working-directory: tracing-subscriber style: # Check style. diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml index 0f969806b1..f16e76ed1c 100644 --- a/tracing-opentelemetry/Cargo.toml +++ b/tracing-opentelemetry/Cargo.toml @@ -25,7 +25,7 @@ default = ["tracing-log"] opentelemetry = { version = "0.16", default-features = false, features = ["trace"] } tracing = { path = "../tracing", version = "0.1", default-features = false, features = ["std"] } tracing-core = { path = "../tracing-core", version = "0.1" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.2", default-features = false, features = ["registry"] } +tracing-subscriber = { path = "../tracing-subscriber", version = "0.2", default-features = false, features = ["registry", "std"] } tracing-log = { path = "../tracing-log", version = "0.1", default-features = false, optional = true } [dev-dependencies] diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 0e7a4f194b..0b3da12788 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -23,7 +23,9 @@ keywords = ["logging", "tracing", "metrics", "subscriber"] [features] -default = ["smallvec", "fmt", "ansi", "tracing-log"] +default = ["smallvec", "fmt", "ansi", "tracing-log", "std"] +alloc = [] +std = ["alloc", "tracing-core/std"] env-filter = ["matchers", "regex", "lazy_static", "tracing"] fmt = ["registry"] ansi = ["fmt", "ansi_term"] @@ -37,7 +39,7 @@ local-time = ["time/local-offset"] tracing-core = { path = "../tracing-core", version = "0.1.20" } # only required by the filter feature -tracing = { optional = true, path = "../tracing", version = "0.1", default-features = false, features = ["std"] } +tracing = { optional = true, path = "../tracing", version = "0.1", default-features = false } matchers = { optional = true, version = "0.1.0" } regex = { optional = true, version = "1", default-features = false, features = ["std"] } smallvec = { optional = true, version = "1" } diff --git a/tracing-subscriber/src/field/debug.rs b/tracing-subscriber/src/field/debug.rs index 10b9eb274c..cc67d29fe7 100644 --- a/tracing-subscriber/src/field/debug.rs +++ b/tracing-subscriber/src/field/debug.rs @@ -1,8 +1,8 @@ //! `MakeVisitor` wrappers for working with `fmt::Debug` fields. -use super::{MakeVisitor, VisitFmt, VisitOutput, VisitWrite}; +use super::{MakeVisitor, VisitFmt, VisitOutput}; use tracing_core::field::{Field, Visit}; -use std::{fmt, io}; +use core::fmt; /// A visitor wrapper that ensures any `fmt::Debug` fields are formatted using /// the alternate (`:#`) formatter. @@ -84,13 +84,19 @@ where } } -impl VisitWrite for Alt -where - V: VisitWrite, -{ - #[inline] - fn writer(&mut self) -> &mut dyn io::Write { - self.0.writer() +feature! { + #![feature = "std"] + use super::VisitWrite; + use std::io; + + impl VisitWrite for Alt + where + V: VisitWrite, + { + #[inline] + fn writer(&mut self) -> &mut dyn io::Write { + self.0.writer() + } } } diff --git a/tracing-subscriber/src/field/delimited.rs b/tracing-subscriber/src/field/delimited.rs index 1861f877a2..8c78c4b203 100644 --- a/tracing-subscriber/src/field/delimited.rs +++ b/tracing-subscriber/src/field/delimited.rs @@ -1,7 +1,7 @@ //! A `MakeVisitor` wrapper that separates formatted fields with a delimiter. use super::{MakeVisitor, VisitFmt, VisitOutput}; -use std::fmt; +use core::fmt; use tracing_core::field::{Field, Visit}; /// A `MakeVisitor` wrapper that wraps a visitor that writes formatted output so @@ -133,6 +133,7 @@ where } #[cfg(test)] +#[cfg(all(test, feature = "alloc"))] mod test { use super::*; use crate::field::test_util::*; diff --git a/tracing-subscriber/src/field/display.rs b/tracing-subscriber/src/field/display.rs index 15f66c4b71..e0bbc55ed2 100644 --- a/tracing-subscriber/src/field/display.rs +++ b/tracing-subscriber/src/field/display.rs @@ -1,8 +1,8 @@ //! `MakeVisitor` wrappers for working with `fmt::Display` fields. -use super::{MakeVisitor, VisitFmt, VisitOutput, VisitWrite}; +use super::{MakeVisitor, VisitFmt, VisitOutput}; use tracing_core::field::{Field, Visit}; -use std::{fmt, io}; +use core::fmt; /// A visitor wrapper that ensures any strings named "message" are formatted /// using `fmt::Display` @@ -90,13 +90,19 @@ where } } -impl VisitWrite for Messages -where - V: VisitWrite, -{ - #[inline] - fn writer(&mut self) -> &mut dyn io::Write { - self.0.writer() +feature! { + #![feature = "std"] + use super::VisitWrite; + use std::io; + + impl VisitWrite for Messages + where + V: VisitWrite, + { + #[inline] + fn writer(&mut self) -> &mut dyn io::Write { + self.0.writer() + } } } diff --git a/tracing-subscriber/src/field/mod.rs b/tracing-subscriber/src/field/mod.rs index 4eb8f07977..f7d03f2cc6 100644 --- a/tracing-subscriber/src/field/mod.rs +++ b/tracing-subscriber/src/field/mod.rs @@ -1,8 +1,8 @@ //! Utilities for working with [fields] and [field visitors]. //! -//! [fields]: https://docs.rs/tracing-core/latest/tracing_core/field/index.html -//! [field visitors]: https://docs.rs/tracing-core/latest/tracing_core/field/trait.Visit.html -use std::{fmt, io}; +//! [fields]: tracing_core::field +//! [field visitors]: tracing_core::field::Visit +use core::{fmt, marker::PhantomData}; pub use tracing_core::field::Visit; use tracing_core::{ span::{Attributes, Record}, @@ -108,11 +108,16 @@ where } } -/// Extension trait implemented by visitors to indicate that they write to an -/// `io::Write` instance, and allow access to that writer. -pub trait VisitWrite: VisitOutput> { - /// Returns the writer that this visitor writes to. - fn writer(&mut self) -> &mut dyn io::Write; +feature! { + #![feature = "std"] + use std::io; + + /// Extension trait implemented by visitors to indicate that they write to an + /// `io::Write` instance, and allow access to that writer. + pub trait VisitWrite: VisitOutput> { + /// Returns the writer that this visitor writes to. + fn writer(&mut self) -> &mut dyn io::Write; + } } /// Extension trait implemented by visitors to indicate that they write to a @@ -223,7 +228,7 @@ where #[derive(Debug)] #[doc(hidden)] pub struct MakeExtMarker { - _p: std::marker::PhantomData, + _p: PhantomData, } #[derive(Debug)] @@ -232,10 +237,11 @@ pub struct RecordFieldsMarker { _p: (), } -#[cfg(test)] +#[cfg(all(test, feature = "alloc"))] #[macro_use] pub(in crate::field) mod test_util { use super::*; + pub(in crate::field) use alloc::string::String; use tracing_core::{ callsite::Callsite, field::{Field, Value}, diff --git a/tracing-subscriber/src/filter/directive.rs b/tracing-subscriber/src/filter/directive.rs index 02954be0ae..54b89d4cc8 100644 --- a/tracing-subscriber/src/filter/directive.rs +++ b/tracing-subscriber/src/filter/directive.rs @@ -1,5 +1,10 @@ use crate::filter::level::{self, LevelFilter}; -use std::{cmp::Ordering, error::Error, fmt, iter::FromIterator, str::FromStr}; +#[cfg(not(feature = "std"))] +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 +40,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 +49,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 +125,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() @@ -358,6 +365,7 @@ impl FromStr for StaticDirective { // === impl ParseError === impl ParseError { + #[cfg(feature = "std")] pub(crate) fn new() -> Self { ParseError { kind: ParseErrorKind::Other(None), @@ -377,17 +385,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), @@ -396,8 +406,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 cc98371034..edaa8e7ef0 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -94,16 +94,13 @@ use tracing_core::{ /// without filtering on field values. When these features are not required, /// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`]. /// -/// [`Layer`]: ../layer/trait.Layer.html -/// [`env_logger`]: https://docs.rs/env_logger/0.7.1/env_logger/#enabling-logging -/// [`Span`]: https://docs.rs/tracing-core/latest/tracing_core/span/index.html -/// [fields]: https://docs.rs/tracing-core/latest/tracing_core/struct.Field.html -/// [`Event`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Event.html -/// [`level`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Level.html -/// [`Metadata`]: https://docs.rs/tracing-core/latest/tracing_core/struct.Metadata.html +/// [`Span`]: tracing_core::span +/// [fields]: tracing_core::Field +/// [`Event`]: tracing_core::Event +/// [`level`]: tracing_core::Level +/// [`Metadata`]: tracing_core::Metadata /// [`Targets`]: crate::filter::Targets -#[cfg(feature = "env-filter")] -#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] #[derive(Debug)] pub struct EnvFilter { statics: directive::Statics, @@ -121,7 +118,7 @@ type FieldMap = HashMap; /// Indicates that an error occurred while parsing a `EnvFilter` from an /// environment variable. -#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] #[derive(Debug)] pub struct FromEnvError { kind: ErrorKind, diff --git a/tracing-subscriber/src/filter/filter_fn.rs b/tracing-subscriber/src/filter/filter_fn.rs index abb0a6f6a1..332bf860a6 100644 --- a/tracing-subscriber/src/filter/filter_fn.rs +++ b/tracing-subscriber/src/filter/filter_fn.rs @@ -1,10 +1,8 @@ -#[cfg(feature = "registry")] -use crate::layer::Filter; use crate::{ filter::LevelFilter, layer::{Context, Layer}, }; -use std::{any::type_name, fmt, marker::PhantomData}; +use core::{any::type_name, fmt, marker::PhantomData}; use tracing_core::{Interest, Metadata, Subscriber}; /// A filter implemented by a closure or function pointer that @@ -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<'_, S>) -> 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 Layer 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<'_, S>) -> bool, - R: Fn(&'static Metadata<'static>) -> Interest, -{ - fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> 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 Layer for DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static, @@ -741,6 +700,48 @@ where } } +// === PLF impls === + +feature! { + #![all(feature = "registry", feature = "std")] + use crate::layer::Filter; + + impl Filter for FilterFn + where + F: Fn(&Metadata<'_>) -> bool, + { + fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> 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<'_, S>) -> bool, + R: Fn(&'static Metadata<'static>) -> Interest, + { + fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> 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 61f425fe43..000a271951 100644 --- a/tracing-subscriber/src/filter/mod.rs +++ b/tracing-subscriber/src/filter/mod.rs @@ -8,36 +8,42 @@ //! //! [`layer` module's documentation]: crate::layer#filtering-with-layers //! [`Layer`]: crate::layer -mod directive; -#[cfg(feature = "env-filter")] -mod env; mod filter_fn; -#[cfg(feature = "registry")] -mod layer_filters; + +feature! { + #![all(feature = "env-filter", feature = "std")] + mod env; + pub use self::env::*; +} + +feature! { + #![all(feature = "registry", feature = "std")] + mod layer_filters; + pub use self::layer_filters::*; +} + mod level; -pub mod targets; -pub use self::directive::ParseError; 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::layer_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::*; -pub use self::targets::Targets; +feature! { + #![any(feature = "std", feature = "alloc")] + pub mod 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 ac4abb750e..e0c7fcf820 100644 --- a/tracing-subscriber/src/filter/targets.rs +++ b/tracing-subscriber/src/filter/targets.rs @@ -13,8 +13,11 @@ use crate::{ }, layer, }; -use std::{ +#[cfg(not(feature = "std"))] +use alloc::string::String; +use core::{ iter::{Extend, FilterMap, FromIterator}, + slice, str::FromStr, }; use tracing_core::{Interest, Metadata, Subscriber}; @@ -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/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 45e7177cd6..9052e30a08 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -57,6 +57,7 @@ use tracing_core::{ /// ``` /// /// [`Layer`]: ../layer/trait.Layer.html +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] pub struct Layer< S, @@ -864,23 +865,6 @@ where self.ctx.lookup_current() } - /// Returns 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. - /// - /// [stored data]: ../registry/struct.SpanRef.html - #[deprecated( - note = "wraps layer::Context::scope, which is deprecated", - since = "0.2.19" - )] - #[allow(deprecated)] - pub fn scope(&self) -> crate::layer::Scope<'_, S> - where - S: for<'lookup> LookupSpan<'lookup>, - { - self.ctx.scope() - } - /// Returns the current span for this formatter. pub fn current_span(&self) -> Current { self.ctx.current_span() diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 6521920f3e..bc89e03e2d 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -261,8 +261,11 @@ use std::{any::TypeId, error::Error, io}; use tracing_core::{span, subscriber::Interest, Event, Metadata}; mod fmt_layer; +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod format; +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod time; +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod writer; #[allow(deprecated)] pub use fmt_layer::LayerBuilder; @@ -285,6 +288,7 @@ pub use self::{ /// A `Subscriber` that logs formatted representations of `tracing` events. /// /// This consists of an inner `Formatter` wrapped in a layer that performs filtering. +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] pub struct Subscriber< N = format::DefaultFields, @@ -297,6 +301,7 @@ pub struct Subscriber< /// A `Subscriber` that logs formatted representations of `tracing` events. /// This type only logs formatted events; it does not perform any filtering. +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub type Formatter< N = format::DefaultFields, E = format::Format, @@ -304,6 +309,7 @@ pub type Formatter< > = layer::Layered, Registry>; /// Configures and constructs `Subscriber`s. +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] pub struct SubscriberBuilder< N = format::DefaultFields, @@ -373,12 +379,12 @@ pub struct SubscriberBuilder< /// }) /// ``` /// -/// [`SubscriberBuilder`]: struct.SubscriberBuilder.html -/// [formatting subscriber]: struct.Subscriber.html -/// [`SubscriberBuilder::default()`]: struct.SubscriberBuilder.html#method.default -/// [`init`]: struct.SubscriberBuilder.html#method.init -/// [`try_init`]: struct.SubscriberBuilder.html#method.try_init -/// [`finish`]: struct.SubscriberBuilder.html#method.finish +/// [formatting subscriber]: Subscriber +/// [`SubscriberBuilder::default()`]: SubscriberBuilder::default() +/// [`init`]: SubscriberBuilder::init() +/// [`try_init`]: SubscriberBuilder::try_init() +/// [`finish`]: SubscriberBuilder::finish() +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub fn fmt() -> SubscriberBuilder { SubscriberBuilder::default() } @@ -388,9 +394,9 @@ pub fn fmt() -> SubscriberBuilder { /// /// This is a shorthand for the equivalent [`Layer::default`] function. /// -/// [formatting layer]: struct.Layer.html -/// [composed]: ../layer/index.html -/// [`Layer::default`]: struct.Layer.html#method.default +/// [formatting layer]: Layer +/// [composed]: crate::layer +#[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub fn layer() -> Layer { Layer::default() } diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs index 053be93e07..0197479af8 100644 --- a/tracing-subscriber/src/fmt/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -2,7 +2,7 @@ //! //! [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html use std::{ - fmt::{self, Debug}, + fmt, io::{self, Write}, sync::{Arc, Mutex, MutexGuard}, }; @@ -666,6 +666,7 @@ pub struct ArcWriter(Arc); /// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a /// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s /// `format_into` methods expect an `io::Write`. +#[cfg(any(feature = "json", feature = "time"))] pub(in crate::fmt) struct WriteAdaptor<'a> { fmt_write: &'a mut dyn fmt::Write, } @@ -745,8 +746,8 @@ impl BoxMakeWriter { } } -impl Debug for BoxMakeWriter { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Debug for BoxMakeWriter { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxMakeWriter") .field(&format_args!("<{}>", self.name)) .finish() @@ -1159,12 +1160,13 @@ where // === impl WriteAdaptor === +#[cfg(any(feature = "json", feature = "time"))] impl<'a> WriteAdaptor<'a> { pub(in crate::fmt) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self { Self { fmt_write } } } - +#[cfg(any(feature = "json", feature = "time"))] impl<'a> io::Write for WriteAdaptor<'a> { fn write(&mut self, buf: &[u8]) -> io::Result { let s = @@ -1182,6 +1184,7 @@ impl<'a> io::Write for WriteAdaptor<'a> { } } +#[cfg(any(feature = "json", feature = "time"))] impl<'a> fmt::Debug for WriteAdaptor<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("WriteAdaptor { .. }") diff --git a/tracing-subscriber/src/layer/context.rs b/tracing-subscriber/src/layer/context.rs index 7c205a7965..e119595260 100644 --- a/tracing-subscriber/src/layer/context.rs +++ b/tracing-subscriber/src/layer/context.rs @@ -1,7 +1,8 @@ use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, 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 [`Layer`]s by the /// wrapped [`Subscriber`]. @@ -41,36 +42,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, S> Context<'a, S> @@ -239,7 +214,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"))] @@ -294,7 +269,7 @@ where // If we found a span, and our per-layer filter enables it, return that // span! - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] { if let Some(span) = span?.try_with_filter(self.filter) { Some(span) @@ -324,7 +299,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 S, @@ -339,40 +314,6 @@ where .find_map(|id| subscriber.span(id)?.try_with_filter(self.filter)) } - /// Returns 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. - /// - /// If this iterator is empty, then there are no spans in the current context. - /// - ///
-    ///     Note: This requires the wrapped subscriber to
-    ///     implement the 
-    ///     LookupSpan trait. See the documentation on
-    ///     Context's
-    ///     declaration for details.
-    /// 
- /// - /// [stored data]: ../registry/struct.SpanRef.html - #[deprecated( - note = "equivalent to `self.current_span().id().and_then(|id| self.span_scope(id).from_root())` but consider passing an explicit ID instead of relying on the contextual span", - since = "0.2.19" - )] - #[allow(deprecated)] - pub fn scope(&self) -> Scope<'_, S> - where - S: for<'lookup> LookupSpan<'lookup>, - { - Scope( - self.lookup_current() - .as_ref() - .map(registry::SpanRef::scope) - .map(registry::Scope::from_root) - .into_iter() - .flatten(), - ) - } - /// Returns an iterator over the [stored data] for all the spans in the /// current context, starting with the specified span and ending with the /// root of the trace tree and ending with the current span. @@ -427,7 +368,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 @@ -439,7 +380,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 S: for<'lookup> LookupSpan<'lookup>, @@ -447,7 +388,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 S: for<'lookup> LookupSpan<'lookup>, @@ -459,7 +400,7 @@ where } } - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option where S: for<'lookup> LookupSpan<'lookup>, @@ -486,7 +427,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/layer/layered.rs b/tracing-subscriber/src/layer/layered.rs index 39b478c973..9e8fd206fa 100644 --- a/tracing-subscriber/src/layer/layered.rs +++ b/tracing-subscriber/src/layer/layered.rs @@ -10,9 +10,9 @@ use crate::{ layer::{Context, Layer}, registry::LookupSpan, }; -#[cfg(feature = "registry")] +#[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; -use std::{any::TypeId, fmt, marker::PhantomData}; +use core::{any::TypeId, cmp, fmt, marker::PhantomData}; /// A [`Subscriber`] composed of a `Subscriber` wrapped by one or more /// [`Layer`]s. @@ -140,16 +140,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 Subscriber; - #[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() @@ -335,7 +335,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() } @@ -356,9 +356,10 @@ where S: Subscriber, { pub(super) fn new(layer: A, inner: B, inner_has_layer_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_layer_filter = inner_has_layer_filter || inner_is_registry; @@ -421,14 +422,12 @@ where outer_hint: Option, inner_hint: Option, ) -> Option { - use std::cmp::max; - if self.inner_is_registry { return outer_hint; } if self.has_layer_filter && self.inner_has_layer_filter { - return Some(max(outer_hint?, inner_hint?)); + return Some(cmp::max(outer_hint?, inner_hint?)); } if self.has_layer_filter && inner_hint.is_none() { @@ -439,7 +438,7 @@ where return None; } - max(outer_hint, inner_hint) + cmp::max(outer_hint, inner_hint) } } @@ -449,13 +448,14 @@ 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) diff --git a/tracing-subscriber/src/layer/mod.rs b/tracing-subscriber/src/layer/mod.rs index 2a647623f8..e509b3f234 100644 --- a/tracing-subscriber/src/layer/mod.rs +++ b/tracing-subscriber/src/layer/mod.rs @@ -493,11 +493,7 @@ //! [`LevelFilter`]: crate::filter::LevelFilter //! [feat]: crate#feature-flags use crate::filter; -use std::{ - any::TypeId, - ops::{Deref, DerefMut}, - sync::Arc, -}; + use tracing_core::{ metadata::Metadata, span, @@ -505,6 +501,14 @@ use tracing_core::{ Event, LevelFilter, }; +use core::any::TypeId; + +feature! { + #![feature = "alloc"] + use alloc::boxed::Box; + use core::ops::{Deref, DerefMut}; +} + mod context; mod layered; pub use self::{context::*, layered::*}; @@ -854,8 +858,8 @@ where /// /// [`Filtered`]: crate::filter::Filtered /// [plf]: #per-layer-filtering - #[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"))))] fn with_filter(self, filter: F) -> filter::Filtered where Self: Sized, @@ -874,181 +878,182 @@ where } } -/// A per-[`Layer`] filter that determines whether a span or event is enabled -/// for an individual layer. -/// -/// See [the module-level documentation][plf] for details on using [`Filter`]s. -/// -/// [plf]: crate::layer#per-layer-filtering -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -#[cfg_attr(docsrs, doc(notable_trait))] -pub trait Filter { - /// Returns `true` if this layer is interested in a span or event with the - /// given [`Metadata`] in the current [`Context`], similarly to - /// [`Subscriber::enabled`]. +feature! { + #![all(feature = "registry", feature = "std")] + /// A per-[`Layer`] filter that determines whether a span or event is enabled + /// for an individual layer. /// - /// If this returns `false`, the span or event will be disabled _for the - /// wrapped [`Layer`]_. Unlike [`Layer::enabled`], the span or event will - /// still be recorded if any _other_ layers choose to enable it. However, - /// the layer [filtered] by this filter will skip recording that span or - /// event. + /// See [the module-level documentation][plf] for details on using [`Filter`]s. /// - /// If all layers indicate that they do not wish to see this span or event, - /// it will be disabled. - /// - /// [`metadata`]: tracing_core::Metadata - /// [`Subscriber::enabled`]: tracing_core::Subscriber::enabled - /// [filtered]: crate::filter::Filtered - fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool; + /// [plf]: crate::layer#per-layer-filtering + #[cfg_attr(docsrs, doc(notable_trait))] + pub trait Filter { + /// Returns `true` if this layer is interested in a span or event with the + /// given [`Metadata`] in the current [`Context`], similarly to + /// [`Subscriber::enabled`]. + /// + /// If this returns `false`, the span or event will be disabled _for the + /// wrapped [`Layer`]_. Unlike [`Layer::enabled`], the span or event will + /// still be recorded if any _other_ layers choose to enable it. However, + /// the layer [filtered] by this filter will skip recording that span or + /// event. + /// + /// If all layers indicate that they do not wish to see this span or event, + /// it will be disabled. + /// + /// [`metadata`]: tracing_core::Metadata + /// [`Subscriber::enabled`]: tracing_core::Subscriber::enabled + /// [filtered]: crate::filter::Filtered + fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool; - /// Returns an [`Interest`] indicating whether this layer will [always], - /// [sometimes], or [never] be interested in the given [`Metadata`]. - /// - /// When a given callsite will [always] or [never] be enabled, the results - /// of evaluating the filter may be cached for improved performance. - /// Therefore, if a filter is capable of determining that it will always or - /// never enable a particular callsite, providing an implementation of this - /// function is recommended. - /// - ///
-    /// Note: If a Filter will perform
-    /// dynamic filtering that depends on the current context in which
-    /// a span or event was observered (e.g. only enabling an event when it
-    /// occurs within a particular span), it must return
-    /// Interest::sometimes() from this method. If it returns
-    /// Interest::always() or Interest::never(), the
-    /// enabled method may not be called when a particular instance
-    /// of that span or event is recorded.
-    /// 
- /// - /// This method is broadly similar to [`Subscriber::register_callsite`]; - /// however, since the returned value represents only the interest of - /// *this* layer, the resulting behavior is somewhat different. - /// - /// If a [`Subscriber`] returns [`Interest::always()`][always] or - /// [`Interest::never()`][never] for a given [`Metadata`], its [`enabled`] - /// method is then *guaranteed* to never be called for that callsite. On the - /// other hand, when a `Filter` returns [`Interest::always()`][always] or - /// [`Interest::never()`][never] for a callsite, _other_ [`Layer`]s may have - /// differing interests in that callsite. If this is the case, the callsite - /// will recieve [`Interest::sometimes()`][sometimes], and the [`enabled`] - /// method will still be called for that callsite when it records a span or - /// event. - /// - /// Returning [`Interest::always()`][always] or [`Interest::never()`][never] from - /// `Filter::callsite_enabled` will permanently enable or disable a - /// callsite (without requiring subsequent calls to [`enabled`]) if and only - /// if the following is true: - /// - /// - all [`Layer`]s that comprise the subscriber include `Filter`s - /// (this includes a tree of [`Layered`] layers that share the same - /// `Filter`) - /// - all those `Filter`s return the same [`Interest`]. - /// - /// For example, if a [`Subscriber`] consists of two [`Filtered`] layers, - /// and both of those layers return [`Interest::never()`][never], that - /// callsite *will* never be enabled, and the [`enabled`] methods of those - /// [`Filter`]s will not be called. - /// - /// ## Default Implementation - /// - /// The default implementation of this method assumes that the - /// `Filter`'s [`enabled`] method _may_ perform dynamic filtering, and - /// returns [`Interest::sometimes()`][sometimes], to ensure that [`enabled`] - /// is called to determine whether a particular _instance_ of the callsite - /// is enabled in the current context. If this is *not* the case, and the - /// `Filter`'s [`enabled`] method will always return the same result - /// for a particular [`Metadata`], this method can be overridden as - /// follows: - /// - /// ``` - /// use tracing_subscriber::layer; - /// use tracing_core::{Metadata, subscriber::Interest}; - /// - /// struct MyFilter { - /// // ... - /// } - /// - /// impl MyFilter { - /// // The actual logic for determining whether a `Metadata` is enabled - /// // must be factored out from the `enabled` method, so that it can be - /// // called without a `Context` (which is not provided to the - /// // `callsite_enabled` method). - /// fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { - /// // ... - /// # drop(metadata); true - /// } - /// } - /// - /// impl layer::Filter for MyFilter { - /// fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { - /// // Even though we are implementing `callsite_enabled`, we must still provide a - /// // working implementation of `enabled`, as returning `Interest::always()` or - /// // `Interest::never()` will *allow* caching, but will not *guarantee* it. - /// // Other filters may still return `Interest::sometimes()`, so we may be - /// // asked again in `enabled`. - /// self.is_enabled(metadata) - /// } - /// - /// fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { - /// // The result of `self.enabled(metadata, ...)` will always be - /// // the same for any given `Metadata`, so we can convert it into - /// // an `Interest`: - /// if self.is_enabled(metadata) { - /// Interest::always() - /// } else { - /// Interest::never() - /// } - /// } - /// } - /// ``` - /// - /// [`Metadata`]: tracing_core::Metadata - /// [`Interest`]: tracing_core::Interest - /// [always]: tracing_core::Interest::always - /// [sometimes]: tracing_core::Interest::sometimes - /// [never]: tracing_core::Interest::never - /// [`Subscriber::register_callsite`]: tracing_core::Subscriber::register_callsite - /// [`Subscriber`]: tracing_core::Subscriber - /// [`enabled`]: Filter::enabled - /// [`Filtered`]: crate::filter::Filtered - fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { - let _ = meta; - Interest::sometimes() - } + /// Returns an [`Interest`] indicating whether this layer will [always], + /// [sometimes], or [never] be interested in the given [`Metadata`]. + /// + /// When a given callsite will [always] or [never] be enabled, the results + /// of evaluating the filter may be cached for improved performance. + /// Therefore, if a filter is capable of determining that it will always or + /// never enable a particular callsite, providing an implementation of this + /// function is recommended. + /// + ///
+        /// Note: If a Filter will perform
+        /// dynamic filtering that depends on the current context in which
+        /// a span or event was observered (e.g. only enabling an event when it
+        /// occurs within a particular span), it must return
+        /// Interest::sometimes() from this method. If it returns
+        /// Interest::always() or Interest::never(), the
+        /// enabled method may not be called when a particular instance
+        /// of that span or event is recorded.
+        /// 
+ /// + /// This method is broadly similar to [`Subscriber::register_callsite`]; + /// however, since the returned value represents only the interest of + /// *this* layer, the resulting behavior is somewhat different. + /// + /// If a [`Subscriber`] returns [`Interest::always()`][always] or + /// [`Interest::never()`][never] for a given [`Metadata`], its [`enabled`] + /// method is then *guaranteed* to never be called for that callsite. On the + /// other hand, when a `Filter` returns [`Interest::always()`][always] or + /// [`Interest::never()`][never] for a callsite, _other_ [`Layer`]s may have + /// differing interests in that callsite. If this is the case, the callsite + /// will recieve [`Interest::sometimes()`][sometimes], and the [`enabled`] + /// method will still be called for that callsite when it records a span or + /// event. + /// + /// Returning [`Interest::always()`][always] or [`Interest::never()`][never] from + /// `Filter::callsite_enabled` will permanently enable or disable a + /// callsite (without requiring subsequent calls to [`enabled`]) if and only + /// if the following is true: + /// + /// - all [`Layer`]s that comprise the subscriber include `Filter`s + /// (this includes a tree of [`Layered`] layers that share the same + /// `Filter`) + /// - all those `Filter`s return the same [`Interest`]. + /// + /// For example, if a [`Subscriber`] consists of two [`Filtered`] layers, + /// and both of those layers return [`Interest::never()`][never], that + /// callsite *will* never be enabled, and the [`enabled`] methods of those + /// [`Filter`]s will not be called. + /// + /// ## Default Implementation + /// + /// The default implementation of this method assumes that the + /// `Filter`'s [`enabled`] method _may_ perform dynamic filtering, and + /// returns [`Interest::sometimes()`][sometimes], to ensure that [`enabled`] + /// is called to determine whether a particular _instance_ of the callsite + /// is enabled in the current context. If this is *not* the case, and the + /// `Filter`'s [`enabled`] method will always return the same result + /// for a particular [`Metadata`], this method can be overridden as + /// follows: + /// + /// ``` + /// use tracing_subscriber::layer; + /// use tracing_core::{Metadata, subscriber::Interest}; + /// + /// struct MyFilter { + /// // ... + /// } + /// + /// impl MyFilter { + /// // The actual logic for determining whether a `Metadata` is enabled + /// // must be factored out from the `enabled` method, so that it can be + /// // called without a `Context` (which is not provided to the + /// // `callsite_enabled` method). + /// fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { + /// // ... + /// # drop(metadata); true + /// } + /// } + /// + /// impl layer::Filter for MyFilter { + /// fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { + /// // Even though we are implementing `callsite_enabled`, we must still provide a + /// // working implementation of `enabled`, as returning `Interest::always()` or + /// // `Interest::never()` will *allow* caching, but will not *guarantee* it. + /// // Other filters may still return `Interest::sometimes()`, so we may be + /// // asked again in `enabled`. + /// self.is_enabled(metadata) + /// } + /// + /// fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { + /// // The result of `self.enabled(metadata, ...)` will always be + /// // the same for any given `Metadata`, so we can convert it into + /// // an `Interest`: + /// if self.is_enabled(metadata) { + /// Interest::always() + /// } else { + /// Interest::never() + /// } + /// } + /// } + /// ``` + /// + /// [`Metadata`]: tracing_core::Metadata + /// [`Interest`]: tracing_core::Interest + /// [always]: tracing_core::Interest::always + /// [sometimes]: tracing_core::Interest::sometimes + /// [never]: tracing_core::Interest::never + /// [`Subscriber::register_callsite`]: tracing_core::Subscriber::register_callsite + /// [`Subscriber`]: tracing_core::Subscriber + /// [`enabled`]: Filter::enabled + /// [`Filtered`]: crate::filter::Filtered + fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { + let _ = meta; + Interest::sometimes() + } - /// Returns an optional hint of the highest [verbosity level][level] that - /// this `Filter` will enable. - /// - /// If this method returns a [`LevelFilter`], it will be used as a hint to - /// determine the most verbose level that will be enabled. This will allow - /// spans and events which are more verbose than that level to be skipped - /// more efficiently. An implementation of this method is optional, but - /// strongly encouraged. - /// - /// If the maximum level the `Filter` will enable can change over the - /// course of its lifetime, it is free to return a different value from - /// multiple invocations of this method. However, note that changes in the - /// maximum level will **only** be reflected after the callsite [`Interest`] - /// cache is rebuilt, by calling the - /// [`tracing_core::callsite::rebuild_interest_cache`][rebuild] function. - /// Therefore, if the `Filter will change the value returned by this - /// method, it is responsible for ensuring that - /// [`rebuild_interest_cache`][rebuild] is called after the value of the max - /// level changes. - /// - /// ## Default Implementation - /// - /// By default, this method returns `None`, indicating that the maximum - /// level is unknown. - /// - /// [level]: tracing_core::metadata::Level - /// [`LevelFilter`]: crate::filter::LevelFilter - /// [`Interest`]: tracing_core::subscriber::Interest - /// [rebuild]: tracing_core::callsite::rebuild_interest_cache - fn max_level_hint(&self) -> Option { - None + /// Returns an optional hint of the highest [verbosity level][level] that + /// this `Filter` will enable. + /// + /// If this method returns a [`LevelFilter`], it will be used as a hint to + /// determine the most verbose level that will be enabled. This will allow + /// spans and events which are more verbose than that level to be skipped + /// more efficiently. An implementation of this method is optional, but + /// strongly encouraged. + /// + /// If the maximum level the `Filter` will enable can change over the + /// course of its lifetime, it is free to return a different value from + /// multiple invocations of this method. However, note that changes in the + /// maximum level will **only** be reflected after the callsite [`Interest`] + /// cache is rebuilt, by calling the + /// [`tracing_core::callsite::rebuild_interest_cache`][rebuild] function. + /// Therefore, if the `Filter will change the value returned by this + /// method, it is responsible for ensuring that + /// [`rebuild_interest_cache`][rebuild] is called after the value of the max + /// level changes. + /// + /// ## Default Implementation + /// + /// By default, this method returns `None`, indicating that the maximum + /// level is unknown. + /// + /// [level]: tracing_core::metadata::Level + /// [`LevelFilter`]: crate::filter::LevelFilter + /// [`Interest`]: tracing_core::subscriber::Interest + /// [rebuild]: tracing_core::callsite::rebuild_interest_cache + fn max_level_hint(&self) -> Option { + None + } } } @@ -1063,6 +1068,7 @@ pub trait SubscriberExt: Subscriber + crate::sealed::Sealed { layer.with_subscriber(self) } } + /// A layer that does nothing. #[derive(Clone, Debug, Default)] pub struct Identity { @@ -1173,89 +1179,93 @@ where } } -macro_rules! layer_impl_body { - () => { - #[inline] - fn on_layer(&mut self, subscriber: &mut S) { - self.deref_mut().on_layer(subscriber); - } +feature! { + #![any(feature = "std", feature = "alloc")] - #[inline] - fn new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { - self.deref().new_span(attrs, id, ctx) - } + macro_rules! layer_impl_body { + () => { + #[inline] + fn on_layer(&mut self, subscriber: &mut S) { + self.deref_mut().on_layer(subscriber); + } - #[inline] - fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { - self.deref().register_callsite(metadata) - } + #[inline] + fn new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { + self.deref().new_span(attrs, id, ctx) + } - #[inline] - fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { - self.deref().enabled(metadata, ctx) - } + #[inline] + fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { + self.deref().register_callsite(metadata) + } - #[inline] - fn max_level_hint(&self) -> Option { - self.deref().max_level_hint() - } + #[inline] + fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { + self.deref().enabled(metadata, ctx) + } - #[inline] - fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { - self.deref().on_record(span, values, ctx) - } + #[inline] + fn max_level_hint(&self) -> Option { + self.deref().max_level_hint() + } - #[inline] - fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { - self.deref().on_follows_from(span, follows, ctx) - } + #[inline] + fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { + self.deref().on_record(span, values, ctx) + } - #[inline] - fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { - self.deref().on_event(event, ctx) - } + #[inline] + fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { + self.deref().on_follows_from(span, follows, ctx) + } - #[inline] - fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { - self.deref().on_enter(id, ctx) - } + #[inline] + fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { + self.deref().on_event(event, ctx) + } - #[inline] - fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { - self.deref().on_exit(id, ctx) - } + #[inline] + fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { + self.deref().on_enter(id, ctx) + } - #[inline] - fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { - self.deref().on_close(id, ctx) - } + #[inline] + fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { + self.deref().on_exit(id, ctx) + } - #[inline] - fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { - self.deref().on_id_change(old, new, ctx) - } + #[inline] + fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { + self.deref().on_close(id, ctx) + } - #[doc(hidden)] - #[inline] - unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { - self.deref().downcast_raw(id) - } - }; -} + #[inline] + fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { + self.deref().on_id_change(old, new, ctx) + } -impl Layer for Box -where - L: Layer, - S: Subscriber, -{ - layer_impl_body! {} -} + #[doc(hidden)] + #[inline] + unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { + self.deref().downcast_raw(id) + } + }; + } -impl Layer for Box + Send + Sync> -where - S: Subscriber, -{ - layer_impl_body! {} + impl Layer for Box + where + L: Layer, + S: Subscriber, + { + layer_impl_body! {} + } + + impl Layer for Box + Send + Sync> + where + S: Subscriber, + { + layer_impl_body! {} + } } // === impl SubscriberExt === diff --git a/tracing-subscriber/src/layer/tests.rs b/tracing-subscriber/src/layer/tests.rs index 34c91a7c86..d7ad617698 100644 --- a/tracing-subscriber/src/layer/tests.rs +++ b/tracing-subscriber/src/layer/tests.rs @@ -12,15 +12,15 @@ impl Layer for NopLayer2 {} /// A layer that holds a string. /// /// Used to test that pointers returned by downcasting are actually valid. -struct StringLayer(String); +struct StringLayer(&'static str); impl Layer for StringLayer {} -struct StringLayer2(String); +struct StringLayer2(&'static str); impl Layer for StringLayer2 {} -struct StringLayer3(String); +struct StringLayer3(&'static str); impl Layer for StringLayer3 {} -pub(crate) struct StringSubscriber(String); +pub(crate) struct StringSubscriber(&'static str); impl Subscriber for StringSubscriber { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { @@ -69,15 +69,17 @@ fn three_layers_are_subscriber() { } #[test] -fn box_layer_is_layer() { - let l: Box + Send + Sync> = Box::new(NopLayer); - assert_layer(&l); - l.with_subscriber(NoSubscriber::default()); +fn three_layers_are_layer() { + let layers = NopLayer.and_then(NopLayer).and_then(NopLayer); + assert_layer(&layers); + let _ = layers.with_subscriber(NoSubscriber::default()); } #[test] -fn arc_layer_is_layer() { - let l: Arc + Send + Sync> = Arc::new(NopLayer); +#[cfg(feature = "alloc")] +fn box_layer_is_layer() { + use alloc::boxed::Box; + let l: Box + Send + Sync> = Box::new(NopLayer); assert_layer(&l); l.with_subscriber(NoSubscriber::default()); } @@ -87,29 +89,29 @@ fn downcasts_to_subscriber() { let s = NopLayer .and_then(NopLayer) .and_then(NopLayer) - .with_subscriber(StringSubscriber("subscriber".into())); + .with_subscriber(StringSubscriber("subscriber")); let subscriber = ::downcast_ref::(&s).expect("subscriber should downcast"); - assert_eq!(&subscriber.0, "subscriber"); + assert_eq!(subscriber.0, "subscriber"); } #[test] fn downcasts_to_layer() { - let s = StringLayer("layer_1".into()) - .and_then(StringLayer2("layer_2".into())) - .and_then(StringLayer3("layer_3".into())) + let s = StringLayer("layer_1") + .and_then(StringLayer2("layer_2")) + .and_then(StringLayer3("layer_3")) .with_subscriber(NoSubscriber::default()); let layer = ::downcast_ref::(&s).expect("layer 1 should downcast"); - assert_eq!(&layer.0, "layer_1"); + assert_eq!(layer.0, "layer_1"); let layer = ::downcast_ref::(&s).expect("layer 2 should downcast"); - assert_eq!(&layer.0, "layer_2"); + assert_eq!(layer.0, "layer_2"); let layer = ::downcast_ref::(&s).expect("layer 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/src/lib.rs b/tracing-subscriber/src/lib.rs index 8d1fbaae57..730fc05f36 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -46,6 +46,9 @@ //! //! ## Feature Flags //! +//! - `std`: Enables APIs that depend on the on the Rust standard library +//! (enabled by default). +//! - `alloc`: Depend on [`liballoc`] (enabled by "std"). //! - `env-filter`: Enables the [`EnvFilter`] type, which implements filtering //! similar to the [`env_logger` crate]. //! - `fmt`: Enables the [`fmt`] module, which provides a subscriber @@ -70,6 +73,34 @@ //! - [`parking_lot`]: Use the `parking_lot` crate's `RwLock` implementation //! rather than the Rust standard library's implementation. //! +//! ### `no_std` Support +//! +//! In embedded systems and other bare-metal applications, `tracing` can be +//! used without requiring the Rust standard library, although some features are +//! disabled. Although most of the APIs provided by `tracing-subscriber`, such +//! as [`fmt`] and [`EnvFilter`], require the standard library, some +//! functionality, such as the [`Subscriber`] trait, can still be used in +//! `no_std` environments. +//! +//! The dependency on the standard library is controlled by two crate feature +//! flags, "std", which enables the dependency on [`libstd`], and "alloc", which +//! enables the dependency on [`liballoc`] (and is enabled by the "std" +//! feature). These features are enabled by default, but `no_std` users can +//! disable them using: +//! +//! ```toml +//! # Cargo.toml +//! tracing-subscriber = { version = "0.3", default-features = false } +//! ``` +//! +//! Additional APIs are available when [`liballoc`] is available. To enable +//! `liballoc` but not `std`, use: +//! +//! ```toml +//! # Cargo.toml +//! tracing-subscriber = { version = "0.3", default-features = false, features = ["alloc"] } +//! ``` +//! //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported @@ -93,6 +124,7 @@ //! [`env_logger` crate]: https://crates.io/crates/env_logger //! [`parking_lot`]: https://crates.io/crates/parking_lot //! [`time` crate]: https://crates.io/crates/time +//! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html #![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", @@ -134,48 +166,51 @@ // future, reducing diff noise. Allow this even though clippy considers it // "needless". #![allow(clippy::needless_update)] +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(feature = "alloc")] +extern crate alloc; #[macro_use] mod macros; pub mod field; pub mod filter; -#[cfg(feature = "fmt")] -#[cfg_attr(docsrs, doc(cfg(feature = "fmt")))] -pub mod fmt; -pub mod layer; pub mod prelude; pub mod registry; -pub mod reload; -pub(crate) mod sync; -pub mod util; -#[cfg(feature = "env-filter")] -#[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] -pub use filter::EnvFilter; +pub mod layer; +pub mod util; -pub use layer::Layer; +feature! { + #![feature = "std"] + pub mod reload; + pub(crate) mod sync; +} -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub use registry::Registry; +feature! { + #![all(feature = "fmt", feature = "std")] + pub mod fmt; + pub use fmt::fmt; + pub use fmt::Subscriber as FmtSubscriber; +} -/// -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub fn registry() -> Registry { - Registry::default() +feature! { + #![all(feature = "env-filter", feature = "std")] + pub use filter::EnvFilter; } -#[cfg(feature = "fmt")] -#[cfg_attr(docsrs, doc(cfg(feature = "fmt")))] -pub use fmt::Subscriber as FmtSubscriber; +pub use layer::Layer; -#[cfg(feature = "fmt")] -#[cfg_attr(docsrs, doc(cfg(feature = "fmt")))] -pub use fmt::fmt; +feature! { + #![all(feature = "registry", feature = "std")] + pub use registry::Registry; -use std::default::Default; + /// + pub fn registry() -> Registry { + Registry::default() + } +} mod sealed { pub trait Sealed {} diff --git a/tracing-subscriber/src/macros.rs b/tracing-subscriber/src/macros.rs index 77b3e05bba..81351132f5 100644 --- a/tracing-subscriber/src/macros.rs +++ b/tracing-subscriber/src/macros.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "std")] macro_rules! try_lock { ($lock:expr) => { try_lock!($lock, else return) @@ -12,3 +13,16 @@ macro_rules! try_lock { } }; } + +macro_rules! feature { + ( + #![$meta:meta] + $($item:item)* + ) => { + $( + #[cfg($meta)] + #[cfg_attr(docsrs, doc(cfg($meta)))] + $item + )* + } +} diff --git a/tracing-subscriber/src/prelude.rs b/tracing-subscriber/src/prelude.rs index 396825a6fb..c2230907b0 100644 --- a/tracing-subscriber/src/prelude.rs +++ b/tracing-subscriber/src/prelude.rs @@ -13,6 +13,7 @@ pub use crate::layer::{ pub use crate::util::SubscriberInitExt as _; -#[cfg(feature = "fmt")] -#[cfg_attr(docsrs, doc(cfg(feature = "fmt")))] -pub use crate::fmt::writer::MakeWriterExt as _; +feature! { + #![all(feature = "fmt", feature = "std")] + pub use crate::fmt::writer::MakeWriterExt as _; +} diff --git a/tracing-subscriber/src/registry/extensions.rs b/tracing-subscriber/src/registry/extensions.rs index 35f2781c57..ea20d94095 100644 --- a/tracing-subscriber/src/registry/extensions.rs +++ b/tracing-subscriber/src/registry/extensions.rs @@ -35,6 +35,7 @@ impl Hasher for IdHasher { /// An immutable, read-only reference to a Span's extensions. #[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct Extensions<'a> { inner: RwLockReadGuard<'a, ExtensionsInner>, } @@ -53,6 +54,7 @@ impl<'a> Extensions<'a> { /// An mutable reference to a Span's extensions. #[derive(Debug)] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct ExtensionsMut<'a> { inner: RwLockWriteGuard<'a, ExtensionsInner>, } diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs index 04ba54a37f..b2a6af4ab3 100644 --- a/tracing-subscriber/src/registry/mod.rs +++ b/tracing-subscriber/src/registry/mod.rs @@ -54,34 +54,32 @@ //! access to the [`Context`][ctx] methods, such as [`Context::span`][lookup], that //! require the root subscriber to be a registry. //! -//! [`Layer`]: ../layer/trait.Layer.html +//! [`Layer`]: crate::layer::Layer //! [`Subscriber`]: //! https://docs.rs/tracing-core/latest/tracing_core/subscriber/trait.Subscriber.html -//! [`Registry`]: struct.Registry.html -//! [ctx]: ../layer/struct.Context.html -//! [lookup]: ../layer/struct.Context.html#method.span -//! [`LookupSpan`]: trait.LookupSpan.html -//! [`SpanData`]: trait.SpanData.html -use std::fmt::Debug; - -#[cfg(feature = "registry")] -use crate::filter::FilterId; +//! [ctx]: crate::layer::Context +//! [lookup]: crate::layer::Context::span() use tracing_core::{field::FieldSet, span::Id, Metadata}; -/// A module containing a type map of span extensions. -mod extensions; -#[cfg(feature = "registry")] -mod sharded; -#[cfg(feature = "registry")] -mod stack; - -pub use extensions::{Extensions, ExtensionsMut}; -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub use sharded::Data; -#[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] -pub use sharded::Registry; +feature! { + #![feature = "std"] + /// A module containing a type map of span extensions. + mod extensions; + pub use extensions::{Extensions, ExtensionsMut}; + +} + +feature! { + #![all(feature = "registry", feature = "std")] + + mod sharded; + mod stack; + + pub use sharded::Data; + pub use sharded::Registry; + + use crate::filter::FilterId; +} /// Provides access to stored span data. /// @@ -174,12 +172,16 @@ pub trait SpanData<'a> { /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn extensions(&self) -> Extensions<'_>; /// Returns a mutable reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn extensions_mut(&self) -> ExtensionsMut<'_>; /// Returns `true` if this span is enabled for the [per-layer filter][plf] @@ -225,32 +227,82 @@ pub struct Scope<'a, R> { registry: &'a R, next: Option, - #[cfg(feature = "registry")] + #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } -impl<'a, R> Scope<'a, R> -where - R: LookupSpan<'a>, -{ - /// Flips the order of the iterator, so that it is ordered from root to leaf. - /// - /// The iterator will first return the root span, then that span's immediate child, - /// and so on until it finally returns the span that [`SpanRef::scope`] was called on. - /// - /// If any items were consumed from the [`Scope`] before calling this method then they - /// will *not* be returned from the [`ScopeFromRoot`]. +feature! { + #![any(feature = "alloc", feature = "std")] + + #[cfg(not(feature = "std"))] + use alloc::vec::{self, Vec}; + use core::{fmt,iter}; + + /// An iterator over the parents of a span, ordered from root to leaf. /// - /// **Note**: this will allocate if there are many spans remaining, or if the - /// "smallvec" feature flag is not enabled. - #[allow(clippy::wrong_self_convention)] - pub fn from_root(self) -> ScopeFromRoot<'a, R> { + /// This is returned by the [`Scope::from_root`] method. + pub struct ScopeFromRoot<'a, R> + where + R: LookupSpan<'a>, + { #[cfg(feature = "smallvec")] - type Buf = smallvec::SmallVec; + spans: iter::Rev>>, #[cfg(not(feature = "smallvec"))] - type Buf = Vec; - ScopeFromRoot { - spans: self.collect::>().into_iter().rev(), + spans: iter::Rev>>, + } + + #[cfg(feature = "smallvec")] + type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16]; + + impl<'a, R> Scope<'a, R> + where + R: LookupSpan<'a>, + { + /// Flips the order of the iterator, so that it is ordered from root to leaf. + /// + /// The iterator will first return the root span, then that span's immediate child, + /// and so on until it finally returns the span that [`SpanRef::scope`] was called on. + /// + /// If any items were consumed from the [`Scope`] before calling this method then they + /// will *not* be returned from the [`ScopeFromRoot`]. + /// + /// **Note**: this will allocate if there are many spans remaining, or if the + /// "smallvec" feature flag is not enabled. + #[allow(clippy::wrong_self_convention)] + pub fn from_root(self) -> ScopeFromRoot<'a, R> { + #[cfg(feature = "smallvec")] + type Buf = smallvec::SmallVec; + #[cfg(not(feature = "smallvec"))] + type Buf = Vec; + ScopeFromRoot { + spans: self.collect::>().into_iter().rev(), + } + } + } + + impl<'a, R> Iterator for ScopeFromRoot<'a, R> + where + R: LookupSpan<'a>, + { + type Item = SpanRef<'a, R>; + + #[inline] + fn next(&mut self) -> Option { + self.spans.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.spans.size_hint() + } + } + + impl<'a, R> fmt::Debug for ScopeFromRoot<'a, R> + where + R: LookupSpan<'a>, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("ScopeFromRoot { .. }") } } } @@ -265,14 +317,14 @@ 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")] + #[cfg(all(feature = "registry", feature = "std"))] { if !curr.is_enabled_for(self.filter) { // The current span in the chain is disabled for this @@ -286,93 +338,6 @@ where } } -/// An iterator over the parents of a span, ordered from root to leaf. -/// -/// This is returned by the [`Scope::from_root`] method. -pub struct ScopeFromRoot<'a, R> -where - R: LookupSpan<'a>, -{ - #[cfg(feature = "smallvec")] - spans: std::iter::Rev>>, - #[cfg(not(feature = "smallvec"))] - spans: std::iter::Rev>>, -} - -impl<'a, R> Iterator for ScopeFromRoot<'a, R> -where - R: LookupSpan<'a>, -{ - type Item = SpanRef<'a, R>; - - #[inline] - fn next(&mut self) -> Option { - self.spans.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.spans.size_hint() - } -} - -impl<'a, R> Debug for ScopeFromRoot<'a, R> -where - R: LookupSpan<'a>, -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.pad("ScopeFromRoot { .. }") - } -} - -/// An iterator over the parents of a span. -/// -/// This is returned by the [`SpanRef::parents`] method. -/// -/// [`SpanRef::parents`]: struct.SpanRef.html#method.parents -#[deprecated(note = "replaced by `Scope`")] -#[derive(Debug)] -pub struct Parents<'a, R>(Scope<'a, R>); - -#[allow(deprecated)] -impl<'a, R> Iterator for Parents<'a, R> -where - R: LookupSpan<'a>, -{ - type Item = SpanRef<'a, R>; - - fn next(&mut self) -> Option { - self.0.next() - } -} - -/// An iterator over a span's parents, starting with the root of the trace -/// tree. -/// -/// For additonal details, see [`SpanRef::from_root`]. -/// -/// [`Span::from_root`]: struct.SpanRef.html#method.from_root -#[deprecated(note = "replaced by `ScopeFromRoot`", since = "0.2.19")] -#[derive(Debug)] -pub struct FromRoot<'a, R>(ScopeFromRoot<'a, R>) -where - R: LookupSpan<'a>; - -#[allow(deprecated)] -impl<'a, R> Iterator for FromRoot<'a, R> -where - R: LookupSpan<'a>, -{ - type Item = SpanRef<'a, R>; - - fn next(&mut self) -> Option { - self.0.next() - } -} - -#[cfg(feature = "smallvec")] -type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16]; - impl<'a, R> SpanRef<'a, R> where R: LookupSpan<'a>, @@ -442,7 +407,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. @@ -463,7 +428,7 @@ where } } - #[cfg(not(feature = "registry"))] + #[cfg(not(all(feature = "registry", feature = "std")))] Some(Self { registry: self.registry, data, @@ -547,48 +512,12 @@ where } } - /// Returns an iterator over all parents of this span, starting with the - /// immediate parent. - /// - /// The iterator will first return the span's immediate parent, followed by - /// that span's parent, followed by _that_ span's parent, and so on, until a - /// it reaches a root span. - #[deprecated( - note = "equivalent to `self.parent().into_iter().flat_map(SpanRef::scope)`, but consider whether excluding `self` is actually intended" - )] - #[allow(deprecated)] - pub fn parents(&self) -> Parents<'a, R> { - Parents(Scope { - registry: self.registry, - next: self.parent_id().cloned(), - - #[cfg(feature = "registry")] - filter: self.filter, - }) - } - - /// Returns an iterator over all parents of this span, starting with the - /// root of the trace tree. - /// - /// The iterator will return the root of the trace tree, followed by the - /// next span, and then the next, until this span's immediate parent is - /// returned. - /// - /// **Note**: this will allocate if there are many spans remaining, or if the - /// "smallvec" feature flag is not enabled. - #[deprecated( - note = "equivalent to `self.parent().into_iter().flat_map(|span| span.scope().from_root())`, but consider whether excluding `self` is actually intended", - since = "0.2.19" - )] - #[allow(deprecated)] - pub fn from_root(&self) -> FromRoot<'a, R> { - FromRoot(self.parents().0.from_root()) - } - /// Returns a reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn extensions(&self) -> Extensions<'_> { self.data.extensions() } @@ -597,11 +526,13 @@ where /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn extensions_mut(&self) -> ExtensionsMut<'_> { 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)); @@ -611,19 +542,19 @@ 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 } } } -#[cfg(all(test, feature = "registry"))] +#[cfg(all(test, feature = "registry", feature = "std"))] mod tests { use crate::{ layer::{Context, Layer}, diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs index 0619aadd0d..cfafef6d1c 100644 --- a/tracing-subscriber/src/registry/sharded.rs +++ b/tracing-subscriber/src/registry/sharded.rs @@ -87,7 +87,7 @@ use tracing_core::{ /// [fields]: https://docs.rs/tracing-core/latest/tracing-core/field/index.html /// [stored span data]: crate::registry::SpanData::extensions_mut #[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(cfg(feature = "registry")))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] #[derive(Debug)] pub struct Registry { spans: Pool, @@ -106,7 +106,7 @@ pub struct Registry { /// [`Layer`s]: ../layer/trait.Layer.html /// [extensions]: struct.Extensions.html #[cfg(feature = "registry")] -#[cfg_attr(docsrs, doc(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/util.rs b/tracing-subscriber/src/util.rs index e1d72b2a24..1c98aa4d26 100644 --- a/tracing-subscriber/src/util.rs +++ b/tracing-subscriber/src/util.rs @@ -1,6 +1,8 @@ //! Extension traits and other utilities to make working with subscribers more //! ergonomic. -use std::{error::Error, fmt}; +use core::fmt; +#[cfg(feature = "std")] +use std::error::Error; use tracing_core::dispatcher::{self, Dispatch}; #[cfg(feature = "tracing-log")] use tracing_log::AsLog; @@ -31,6 +33,8 @@ where /// /// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log + #[cfg(feature = "std")] + #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn set_default(self) -> dispatcher::DefaultGuard { #[cfg(feature = "tracing-log")] let _ = tracing_log::LogTracer::init(); @@ -92,29 +96,56 @@ impl SubscriberInitExt for T where T: Into {} /// Error returned by [`try_init`](SubscriberInitExt::try_init) if a global default subscriber could not be initialized. pub struct TryInitError { + #[cfg(feature = "std")] inner: Box, + + #[cfg(not(feature = "std"))] + _p: (), } // ==== impl TryInitError ==== impl TryInitError { + #[cfg(feature = "std")] fn new(e: impl Into>) -> Self { Self { inner: e.into() } } + + #[cfg(not(feature = "std"))] + fn new(_: T) -> Self { + Self { _p: () } + } } impl fmt::Debug for TryInitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.inner, f) + #[cfg(feature = "std")] + { + fmt::Debug::fmt(&self.inner, f) + } + + #[cfg(not(feature = "std"))] + { + f.write_str("TryInitError(())") + } } } impl fmt::Display for TryInitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Display::fmt(&self.inner, f) + #[cfg(feature = "std")] + { + fmt::Display::fmt(&self.inner, f) + } + + #[cfg(not(feature = "std"))] + { + f.write_str("failed to set global default subscriber") + } } } +#[cfg(feature = "std")] impl Error for TryInitError { fn source(&self) -> Option<&(dyn Error + 'static)> { self.inner.source() diff --git a/tracing-subscriber/tests/layer_filters/boxed.rs b/tracing-subscriber/tests/layer_filters/boxed.rs index 4788490a80..0fe37188e1 100644 --- a/tracing-subscriber/tests/layer_filters/boxed.rs +++ b/tracing-subscriber/tests/layer_filters/boxed.rs @@ -1,5 +1,4 @@ use super::*; -use std::sync::Arc; use tracing_subscriber::{filter, prelude::*, Layer}; fn layer() -> (ExpectLayer, subscriber::MockHandle) { @@ -41,33 +40,3 @@ fn dyn_box_works() { handle.assert_finished(); } - -/// the same as `box_works` but with an `Arc`. -#[test] -fn arc_works() { - let (layer, handle) = layer(); - let layer = Box::new(layer.with_filter(filter())); - - let _guard = tracing_subscriber::registry().with(layer).set_default(); - - for i in 0..2 { - tracing::info!(i); - } - - handle.assert_finished(); -} - -/// the same as `box_works` but with a type-erased `Arc`. -#[test] -fn dyn_arc_works() { - let (layer, handle) = layer(); - let layer: Arc + Send + Sync + 'static> = Arc::new(layer.with_filter(filter())); - - let _guard = tracing_subscriber::registry().with(layer).set_default(); - - for i in 0..2 { - tracing::info!(i); - } - - handle.assert_finished(); -} diff --git a/tracing-subscriber/tests/utils.rs b/tracing-subscriber/tests/utils.rs index 1229a6a493..ff025a2a23 100644 --- a/tracing-subscriber/tests/utils.rs +++ b/tracing-subscriber/tests/utils.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "std")] mod support; use self::support::*; use tracing_subscriber::prelude::*;