diff --git a/azure-pipelines.yml b/azure-pipelines.yml index c1ee788e..e58d5955 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -36,6 +36,8 @@ stages: displayName: "Test static max level features" - bash: cd tracing-core && cargo test --no-default-features displayName: "Test tracing-core no-std support" + - bash: cd tracing && cargo test --no-default-features + displayName: "Test tracing no-std support" - job: custom_nightly pool: vmImage: ubuntu-16.04 diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index bbace6bb..fd4ffa8b 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -45,7 +45,7 @@ proc-macro2 = "0.4" [dev-dependencies] tracing = "0.1" -tracing-core = "0.1" +tracing-core = "0.1.4" tracing-fmt = "0.0.1-alpha.2" [badges] diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 5afe7851..a020c691 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -22,12 +22,13 @@ categories = [ "development-tools::debugging", "development-tools::profiling", "asynchronous", + "no-std", ] keywords = ["logging", "tracing"] edition = "2018" [dependencies] -tracing-core = "0.1.2" +tracing-core = { version = "0.1.4", default-features = false } log = { version = "0.4", optional = true } tracing-attributes = "0.1.0" cfg-if = "0.1.9" @@ -40,6 +41,8 @@ log = "0.4" criterion = { version = "0.2", default_features = false } [features] +default = ["std"] + max_level_off = [] max_level_error = [] max_level_warn = [] @@ -55,6 +58,8 @@ release_max_level_debug = [] release_max_level_trace = [] async-await = ["tracing-attributes/async-await"] +std = ["tracing-core/std"] + [[bench]] name = "subscriber" @@ -67,3 +72,6 @@ harness = false [badges] azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" } maintenance = { status = "actively-developed" } + +[target.'cfg(not(feature = "std"))'.dependencies] +spin = "0.5" diff --git a/tracing/examples/counters.rs b/tracing/examples/counters.rs index 8f58a850..a2cecd96 100644 --- a/tracing/examples/counters.rs +++ b/tracing/examples/counters.rs @@ -123,20 +123,20 @@ impl Counters { fn main() { let (counters, subscriber) = Counters::new(); - tracing::subscriber::with_default(subscriber, || { - let mut foo: u64 = 2; - span!(Level::TRACE, "my_great_span", foo_count = &foo).in_scope(|| { - foo += 1; - info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span"); - span!( - Level::TRACE, - "my other span", - foo_count = &foo, - baz_count = 5 - ) - .in_scope(|| { - warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak"); - }); + tracing::subscriber::set_global_default(subscriber).unwrap(); + + let mut foo: u64 = 2; + span!(Level::TRACE, "my_great_span", foo_count = &foo).in_scope(|| { + foo += 1; + info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span"); + span!( + Level::TRACE, + "my other span", + foo_count = &foo, + baz_count = 5 + ) + .in_scope(|| { + warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak"); }); }); diff --git a/tracing/examples/sloggish/main.rs b/tracing/examples/sloggish/main.rs index 4631cedb..2408e456 100644 --- a/tracing/examples/sloggish/main.rs +++ b/tracing/examples/sloggish/main.rs @@ -20,34 +20,33 @@ use self::sloggish_subscriber::SloggishSubscriber; fn main() { let subscriber = SloggishSubscriber::new(2); + tracing::subscriber::set_global_default(subscriber).unwrap(); - tracing::subscriber::with_default(subscriber, || { - span!(Level::TRACE, "", version = &field::display(5.0)).in_scope(|| { - span!(Level::TRACE, "server", host = "localhost", port = 8080).in_scope(|| { - info!("starting"); - info!("listening"); - let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); - peer1.in_scope(|| { - debug!("connected"); - debug!({ length = 2 }, "message received"); - }); - let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); - peer2.in_scope(|| { - debug!("connected"); - }); - peer1.in_scope(|| { - warn!({ algo = "xor" }, "weak encryption requested"); - debug!({ length = 8 }, "response sent"); - debug!("disconnected"); - }); - peer2.in_scope(|| { - debug!({ length = 5 }, "message received"); - debug!({ length = 8 }, "response sent"); - debug!("disconnected"); - }); - warn!("internal error"); - info!("exit"); - }) - }); + span!(Level::TRACE, "", version = &field::display(5.0)).in_scope(|| { + span!(Level::TRACE, "server", host = "localhost", port = 8080).in_scope(|| { + info!("starting"); + info!("listening"); + let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); + peer1.in_scope(|| { + debug!("connected"); + debug!({ length = 2 }, "message received"); + }); + let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); + peer2.in_scope(|| { + debug!("connected"); + }); + peer1.in_scope(|| { + warn!({ algo = "xor" }, "weak encryption requested"); + debug!({ length = 8 }, "response sent"); + debug!("disconnected"); + }); + peer2.in_scope(|| { + debug!({ length = 5 }, "message received"); + debug!({ length = 8 }, "response sent"); + debug!("disconnected"); + }); + warn!("internal error"); + info!("exit"); + }) }); } diff --git a/tracing/src/level_filters.rs b/tracing/src/level_filters.rs index 0831e053..d84d0bf5 100644 --- a/tracing/src/level_filters.rs +++ b/tracing/src/level_filters.rs @@ -35,7 +35,7 @@ //! ``` //! //! [`log` crate]: https://docs.rs/log/0.4.6/log/#compile-time-filters -use std::cmp::Ordering; +use crate::stdlib::cmp::Ordering; use tracing_core::Level; /// A filter comparable to trace verbosity `Level`. diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 221bd382..67af86a5 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -281,7 +281,7 @@ //! # fn main() { //! //! let my_subscriber = FooSubscriber::new(); -//! +//! # #[cfg(feature = "std")] //! tracing::subscriber::with_default(my_subscriber, || { //! // Any trace events generated in this closure or by functions it calls //! // will be collected by `my_subscriber`. @@ -335,13 +335,24 @@ //! due to oddities in macro expansion. //! * `async-await`: enables support for instrumenting `async fn`s with the //! [`#[instrument]`][instrument] attribute. +//! +//! ```toml +//! [dependencies] +//! tracing = { version = "0.1", features = ["async-await"] } +//! ``` +//! //! **Note**: this also requires the [`tracing-futures`] crate with the //! `std-future` feature flag enabled. //! -//! ```toml -//! [dependencies] -//! tracing = { version = "0.1", features = ["log", "async-await"] } -//! ``` +//! * `std`: Depend on the Rust standard library (enabled by default). +//! +//! `no_std` users may disable this feature with `default-features = false`: +//! +//! ```toml +//! [dependencies] +//! tracing = { version = "0.1.5", default-features = false } +//! ``` +//! **Note**:`tracing`'s `no_std` support requires `liballoc`. //! //! [`log`]: https://docs.rs/log/0.4.6/log/ //! [`span`]: span/index.html @@ -365,6 +376,11 @@ //! [`tracing-timing`]: https://crates.io/crates/tracing-timing //! [static verbosity level]: level_filters/index.html#compile-time-filters //! [instrument]: https://docs.rs/tracing-attributes/0.1.0/tracing_attributes/attr.instrument.html +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(not(feature = "std"))] +extern crate alloc; + #[macro_use] extern crate cfg_if; use tracing_core; @@ -407,8 +423,20 @@ mod macros; pub mod field; pub mod level_filters; pub mod span; +pub(crate) mod stdlib; pub mod subscriber; +#[doc(hidden)] +pub mod __macro_support { + pub use crate::stdlib::sync::atomic::{AtomicUsize, Ordering}; + + #[cfg(feature = "std")] + pub use crate::stdlib::sync::Once; + + #[cfg(not(feature = "std"))] + pub type Once = spin::Once<()>; +} + mod sealed { pub trait Sealed {} } diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 20fab059..6514a882 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -1069,7 +1069,6 @@ macro_rules! event { /// ```rust /// # #[macro_use] /// # extern crate tracing; -/// # use std::time::SystemTime; /// # #[derive(Debug, Copy, Clone)] struct Position { x: f32, y: f32 } /// # impl Position { /// # const ORIGIN: Self = Self { x: 0.0, y: 0.0 }; @@ -1468,7 +1467,10 @@ macro_rules! debug { /// ```rust /// # #[macro_use] /// # extern crate tracing; -/// # use std::net::Ipv4Addr; +/// # // this is so the test will still work in no-std mode +/// # #[derive(Debug)] +/// # pub struct Ipv4Addr; +/// # impl Ipv4Addr { fn new(o1: u8, o2: u8, o3: u8, o4: u8) -> Self { Self } } /// # fn main() { /// # struct Connection { port: u32, speed: f32 } /// use tracing::field; @@ -1476,10 +1478,10 @@ macro_rules! debug { /// let addr = Ipv4Addr::new(127, 0, 0, 1); /// let conn = Connection { port: 40, speed: 3.20 }; /// -/// info!({ port = conn.port }, "connected to {}", addr); +/// info!({ port = conn.port }, "connected to {:?}", addr); /// info!( /// target: "connection_events", -/// ip = %addr, +/// ip = ?addr, /// conn.port, /// ?conn.speed, /// ); @@ -2101,11 +2103,7 @@ macro_rules! callsite { level: $lvl:expr, fields: $($fields:tt)* ) => {{ - use std::sync::{ - atomic::{self, AtomicUsize, Ordering}, - Once, - }; - use $crate::{callsite, subscriber::Interest, Metadata}; + use $crate::{callsite, subscriber::Interest, Metadata, __macro_support::*}; struct MyCallsite; static META: Metadata<'static> = { $crate::metadata! { @@ -2117,10 +2115,7 @@ macro_rules! callsite { kind: $kind, } }; - // FIXME: Rust 1.34 deprecated ATOMIC_USIZE_INIT. When Tokio's minimum - // supported version is 1.34, replace this with the const fn `::new`. - #[allow(deprecated)] - static INTEREST: AtomicUsize = atomic::ATOMIC_USIZE_INIT; + static INTEREST: AtomicUsize = AtomicUsize::new(0); static REGISTRATION: Once = Once::new(); impl MyCallsite { #[inline] diff --git a/tracing/src/span.rs b/tracing/src/span.rs index 614f059a..fe898ebb 100644 --- a/tracing/src/span.rs +++ b/tracing/src/span.rs @@ -318,14 +318,14 @@ //! [guard]: struct.Entered.html pub use tracing_core::span::{Attributes, Id, Record}; +use crate::stdlib::{ + cmp, fmt, + hash::{Hash, Hasher}, +}; use crate::{ dispatcher::{self, Dispatch}, field, Metadata, }; -use std::{ - cmp, fmt, - hash::{Hash, Hasher}, -}; /// Trait implemented by types which have a span `Id`. pub trait AsId: crate::sealed::Sealed { diff --git a/tracing/src/stdlib.rs b/tracing/src/stdlib.rs new file mode 100644 index 00000000..12b54084 --- /dev/null +++ b/tracing/src/stdlib.rs @@ -0,0 +1,55 @@ +//! Re-exports either the Rust `std` library or `core` and `alloc` when `std` is +//! disabled. +//! +//! `crate::stdlib::...` should be used rather than `std::` when adding code that +//! will be available with the standard library disabled. +//! +//! Note that this module is called `stdlib` rather than `std`, as Rust 1.34.0 +//! does not permit redefining the name `stdlib` (although this works on the +//! latest stable Rust). +#[cfg(feature = "std")] +pub(crate) use std::*; + +#[cfg(not(feature = "std"))] +pub(crate) use self::no_std::*; + +#[cfg(not(feature = "std"))] +mod no_std { + // We pre-emptively export everything from libcore/liballoc, (even modules + // we aren't using currently) to make adding new code easier. Therefore, + // some of these imports will be unused. + #![allow(unused_imports)] + + pub(crate) use core::{ + any, array, ascii, cell, char, clone, cmp, convert, default, f32, f64, ffi, future, hash, + hint, i128, i16, i8, isize, iter, marker, mem, num, ops, option, pin, ptr, result, task, + time, u128, u16, u32, u8, usize, + }; + + pub(crate) use alloc::{boxed, collections, rc, string, vec}; + + pub(crate) mod borrow { + pub(crate) use alloc::borrow::*; + pub(crate) use core::borrow::*; + } + + pub(crate) mod fmt { + pub(crate) use alloc::fmt::*; + pub(crate) use core::fmt::*; + } + + pub(crate) mod slice { + pub(crate) use alloc::slice::*; + pub(crate) use core::slice::*; + } + + pub(crate) mod str { + pub(crate) use alloc::str::*; + pub(crate) use core::str::*; + } + + pub(crate) mod sync { + pub(crate) use alloc::sync::*; + pub(crate) use core::sync::*; + } +} diff --git a/tracing/src/subscriber.rs b/tracing/src/subscriber.rs index 77d797f1..3a8dcd6a 100644 --- a/tracing/src/subscriber.rs +++ b/tracing/src/subscriber.rs @@ -11,6 +11,7 @@ pub use tracing_core::subscriber::*; /// [`Span`]: ../span/struct.Span.html /// [`Subscriber`]: ../subscriber/trait.Subscriber.html /// [`Event`]: :../event/struct.Event.html +#[cfg(feature = "std")] pub fn with_default(subscriber: S, f: impl FnOnce() -> T) -> T where S: Subscriber + Send + Sync + 'static, diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs index 4f4b3293..e1efc843 100644 --- a/tracing/tests/event.rs +++ b/tracing/tests/event.rs @@ -1,3 +1,11 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +// +// The alternative would be for each of these tests to be defined in a separate +// file, which is :( +#![cfg(feature = "std")] + #[macro_use] extern crate tracing; mod support; diff --git a/tracing/tests/filter_caching_is_lexically_scoped.rs b/tracing/tests/filter_caching_is_lexically_scoped.rs index d09bde15..db148e65 100644 --- a/tracing/tests/filter_caching_is_lexically_scoped.rs +++ b/tracing/tests/filter_caching_is_lexically_scoped.rs @@ -4,12 +4,14 @@ // added all filters are re-evaluated. The tests being run only in separate // threads with shared global state lets them interfere with each other +#[cfg(not(feature = "std"))] +extern crate std; + #[macro_use] extern crate tracing; mod support; use self::support::*; -use tracing::subscriber::with_default; use tracing::Level; use std::sync::{ @@ -40,25 +42,27 @@ fn filter_caching_is_lexically_scoped() { }) .run(); - with_default(subscriber, || { - // Call the function once. The filter should be re-evaluated. - assert!(my_great_function()); - assert_eq!(count.load(Ordering::Relaxed), 1); + // Since this test is in its own file anyway, we can do this. Thus, this + // test will work even with no-std. + tracing::subscriber::set_global_default(subscriber).unwrap(); - // Call the function again. The cached result should be used. - assert!(my_great_function()); - assert_eq!(count.load(Ordering::Relaxed), 1); + // Call the function once. The filter should be re-evaluated. + assert!(my_great_function()); + assert_eq!(count.load(Ordering::Relaxed), 1); - assert!(my_other_function()); - assert_eq!(count.load(Ordering::Relaxed), 2); + // Call the function again. The cached result should be used. + assert!(my_great_function()); + assert_eq!(count.load(Ordering::Relaxed), 1); - assert!(my_great_function()); - assert_eq!(count.load(Ordering::Relaxed), 2); + assert!(my_other_function()); + assert_eq!(count.load(Ordering::Relaxed), 2); - assert!(my_other_function()); - assert_eq!(count.load(Ordering::Relaxed), 2); + assert!(my_great_function()); + assert_eq!(count.load(Ordering::Relaxed), 2); - assert!(my_great_function()); - assert_eq!(count.load(Ordering::Relaxed), 2); - }); + assert!(my_other_function()); + assert_eq!(count.load(Ordering::Relaxed), 2); + + assert!(my_great_function()); + assert_eq!(count.load(Ordering::Relaxed), 2); } diff --git a/tracing/tests/filters_are_not_reevaluated_for_the_same_span.rs b/tracing/tests/filters_are_not_reevaluated_for_the_same_span.rs index 4954a2af..1689f0d9 100644 --- a/tracing/tests/filters_are_not_reevaluated_for_the_same_span.rs +++ b/tracing/tests/filters_are_not_reevaluated_for_the_same_span.rs @@ -3,13 +3,14 @@ // registry. The registry was changed so that each time a new dispatcher is // added all filters are re-evaluated. The tests being run only in separate // threads with shared global state lets them interfere with each other +#[cfg(not(feature = "std"))] +extern crate std; #[macro_use] extern crate tracing; mod support; use self::support::*; -use tracing::subscriber::with_default; use tracing::Level; use std::sync::{ @@ -40,30 +41,33 @@ fn filters_are_not_reevaluated_for_the_same_span() { }) .run_with_handle(); - with_default(subscriber, move || { - // Enter "alice" and then "bob". The dispatcher expects to see "bob" but - // not "alice." - let alice = span!(Level::TRACE, "alice"); - let bob = alice.in_scope(|| { - let bob = span!(Level::TRACE, "bob"); - bob.in_scope(|| ()); - bob - }); + // Since this test is in its own file anyway, we can do this. Thus, this + // test will work even with no-std. + tracing::subscriber::set_global_default(subscriber).unwrap(); - // The filter should have seen each span a single time. - assert_eq!(alice_count.load(Ordering::Relaxed), 1); - assert_eq!(bob_count.load(Ordering::Relaxed), 1); - - alice.in_scope(|| bob.in_scope(|| {})); - - // The subscriber should see "bob" again, but the filter should not have - // been called. - assert_eq!(alice_count.load(Ordering::Relaxed), 1); - assert_eq!(bob_count.load(Ordering::Relaxed), 1); - - bob.in_scope(|| {}); - assert_eq!(alice_count.load(Ordering::Relaxed), 1); - assert_eq!(bob_count.load(Ordering::Relaxed), 1); + // Enter "alice" and then "bob". The dispatcher expects to see "bob" but + // not "alice." + let alice = span!(Level::TRACE, "alice"); + let bob = alice.in_scope(|| { + let bob = span!(Level::TRACE, "bob"); + bob.in_scope(|| ()); + bob }); + + // The filter should have seen each span a single time. + assert_eq!(alice_count.load(Ordering::Relaxed), 1); + assert_eq!(bob_count.load(Ordering::Relaxed), 1); + + alice.in_scope(|| bob.in_scope(|| {})); + + // The subscriber should see "bob" again, but the filter should not have + // been called. + assert_eq!(alice_count.load(Ordering::Relaxed), 1); + assert_eq!(bob_count.load(Ordering::Relaxed), 1); + + bob.in_scope(|| {}); + assert_eq!(alice_count.load(Ordering::Relaxed), 1); + assert_eq!(bob_count.load(Ordering::Relaxed), 1); + handle.assert_finished(); } diff --git a/tracing/tests/filters_are_reevaluated_for_different_call_sites.rs b/tracing/tests/filters_are_reevaluated_for_different_call_sites.rs index 9d26ae1b..6cea0525 100644 --- a/tracing/tests/filters_are_reevaluated_for_different_call_sites.rs +++ b/tracing/tests/filters_are_reevaluated_for_different_call_sites.rs @@ -3,13 +3,14 @@ // registry. The registry was changed so that each time a new dispatcher is // added all filters are re-evaluated. The tests being run only in separate // threads with shared global state lets them interfere with each other +#[cfg(not(feature = "std"))] +extern crate std; #[macro_use] extern crate tracing; mod support; use self::support::*; -use tracing::subscriber::with_default; use tracing::Level; use std::sync::{ @@ -43,38 +44,40 @@ fn filters_are_reevaluated_for_different_call_sites() { }) .run(); - with_default(subscriber, move || { - // Enter "charlie" and then "dave". The dispatcher expects to see "dave" but - // not "charlie." - let charlie = span!(Level::TRACE, "charlie"); - let dave = charlie.in_scope(|| { - let dave = span!(Level::TRACE, "dave"); - dave.in_scope(|| {}); - dave - }); + // Since this test is in its own file anyway, we can do this. Thus, this + // test will work even with no-std. + tracing::subscriber::set_global_default(subscriber).unwrap(); - // The filter should have seen each span a single time. - assert_eq!(charlie_count.load(Ordering::Relaxed), 1); - assert_eq!(dave_count.load(Ordering::Relaxed), 1); - - charlie.in_scope(|| dave.in_scope(|| {})); - - // The subscriber should see "dave" again, but the filter should not have - // been called. - assert_eq!(charlie_count.load(Ordering::Relaxed), 1); - assert_eq!(dave_count.load(Ordering::Relaxed), 1); - - // A different span with the same name has a different call site, so it - // should cause the filter to be reapplied. - let charlie2 = span!(Level::TRACE, "charlie"); - charlie.in_scope(|| {}); - assert_eq!(charlie_count.load(Ordering::Relaxed), 2); - assert_eq!(dave_count.load(Ordering::Relaxed), 1); - - // But, the filter should not be re-evaluated for the new "charlie" span - // when it is re-entered. - charlie2.in_scope(|| span!(Level::TRACE, "dave").in_scope(|| {})); - assert_eq!(charlie_count.load(Ordering::Relaxed), 2); - assert_eq!(dave_count.load(Ordering::Relaxed), 2); + // Enter "charlie" and then "dave". The dispatcher expects to see "dave" but + // not "charlie." + let charlie = span!(Level::TRACE, "charlie"); + let dave = charlie.in_scope(|| { + let dave = span!(Level::TRACE, "dave"); + dave.in_scope(|| {}); + dave }); + + // The filter should have seen each span a single time. + assert_eq!(charlie_count.load(Ordering::Relaxed), 1); + assert_eq!(dave_count.load(Ordering::Relaxed), 1); + + charlie.in_scope(|| dave.in_scope(|| {})); + + // The subscriber should see "dave" again, but the filter should not have + // been called. + assert_eq!(charlie_count.load(Ordering::Relaxed), 1); + assert_eq!(dave_count.load(Ordering::Relaxed), 1); + + // A different span with the same name has a different call site, so it + // should cause the filter to be reapplied. + let charlie2 = span!(Level::TRACE, "charlie"); + charlie.in_scope(|| {}); + assert_eq!(charlie_count.load(Ordering::Relaxed), 2); + assert_eq!(dave_count.load(Ordering::Relaxed), 1); + + // But, the filter should not be re-evaluated for the new "charlie" span + // when it is re-entered. + charlie2.in_scope(|| span!(Level::TRACE, "dave").in_scope(|| {})); + assert_eq!(charlie_count.load(Ordering::Relaxed), 2); + assert_eq!(dave_count.load(Ordering::Relaxed), 2); } diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index 8c72c573..420db215 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -1,3 +1,8 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +#![cfg(feature = "std")] + #[macro_use] extern crate tracing; mod support; diff --git a/tracing/tests/subscriber.rs b/tracing/tests/subscriber.rs index 4c042e29..4ea309be 100644 --- a/tracing/tests/subscriber.rs +++ b/tracing/tests/subscriber.rs @@ -1,3 +1,11 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +// +// The alternative would be for each of these tests to be defined in a separate +// file, which is :( +#![cfg(feature = "std")] + #[macro_use] extern crate tracing; use tracing::{