tracing: support no-std + alloc (#263)

## Motivation

Users have expressed interest in using `tracing` on no_std platforms
with `liballoc`. Now that `tracing-core` supports no_std (see #256), 
we can now add no_std support to `tracing` as well.

## Solution

This branch adds `no_std` support to `tracing` by adding a `std`
feature flag (on by default) which can be disabled to use `libcore` +
`liballoc` instead of `libstd`.

When the `std` feature is disabled, `tracing-core` will use
`spin::Once` rather than `std::sync::Once`, and the thread-local
scoped dispatcher will be disabled (since it necessitates a defined OS
threading model, which may not exist on `no_std` platforms).

Some tests, which rely on the thread-local dispatcher, are disabled when
the standard lib is disabled. The alternative is to use
`set_global_default` in those tests, which would necessitate placing
each test in its own separate file. Since the behavior being tested is
otherwise the same regardless of whether or not the standard library is
used, I opted to disable these tests instead.

Refs: #256
Closes: #213

Signed-off-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
Eliza Weisman 2019-08-09 17:11:07 -07:00 committed by GitHub
parent 557c71b719
commit ecdb675150
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 261 additions and 141 deletions

View File

@ -36,6 +36,8 @@ stages:
displayName: "Test static max level features" displayName: "Test static max level features"
- bash: cd tracing-core && cargo test --no-default-features - bash: cd tracing-core && cargo test --no-default-features
displayName: "Test tracing-core no-std support" displayName: "Test tracing-core no-std support"
- bash: cd tracing && cargo test --no-default-features
displayName: "Test tracing no-std support"
- job: custom_nightly - job: custom_nightly
pool: pool:
vmImage: ubuntu-16.04 vmImage: ubuntu-16.04

View File

@ -45,7 +45,7 @@ proc-macro2 = "0.4"
[dev-dependencies] [dev-dependencies]
tracing = "0.1" tracing = "0.1"
tracing-core = "0.1" tracing-core = "0.1.4"
tracing-fmt = "0.0.1-alpha.2" tracing-fmt = "0.0.1-alpha.2"
[badges] [badges]

View File

@ -22,12 +22,13 @@ categories = [
"development-tools::debugging", "development-tools::debugging",
"development-tools::profiling", "development-tools::profiling",
"asynchronous", "asynchronous",
"no-std",
] ]
keywords = ["logging", "tracing"] keywords = ["logging", "tracing"]
edition = "2018" edition = "2018"
[dependencies] [dependencies]
tracing-core = "0.1.2" tracing-core = { version = "0.1.4", default-features = false }
log = { version = "0.4", optional = true } log = { version = "0.4", optional = true }
tracing-attributes = "0.1.0" tracing-attributes = "0.1.0"
cfg-if = "0.1.9" cfg-if = "0.1.9"
@ -40,6 +41,8 @@ log = "0.4"
criterion = { version = "0.2", default_features = false } criterion = { version = "0.2", default_features = false }
[features] [features]
default = ["std"]
max_level_off = [] max_level_off = []
max_level_error = [] max_level_error = []
max_level_warn = [] max_level_warn = []
@ -55,6 +58,8 @@ release_max_level_debug = []
release_max_level_trace = [] release_max_level_trace = []
async-await = ["tracing-attributes/async-await"] async-await = ["tracing-attributes/async-await"]
std = ["tracing-core/std"]
[[bench]] [[bench]]
name = "subscriber" name = "subscriber"
@ -67,3 +72,6 @@ harness = false
[badges] [badges]
azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" } azure-devops = { project = "tracing/tracing", pipeline = "tokio-rs.tracing", build = "1" }
maintenance = { status = "actively-developed" } maintenance = { status = "actively-developed" }
[target.'cfg(not(feature = "std"))'.dependencies]
spin = "0.5"

View File

@ -123,20 +123,20 @@ impl Counters {
fn main() { fn main() {
let (counters, subscriber) = Counters::new(); let (counters, subscriber) = Counters::new();
tracing::subscriber::with_default(subscriber, || { tracing::subscriber::set_global_default(subscriber).unwrap();
let mut foo: u64 = 2;
span!(Level::TRACE, "my_great_span", foo_count = &foo).in_scope(|| { let mut foo: u64 = 2;
foo += 1; span!(Level::TRACE, "my_great_span", foo_count = &foo).in_scope(|| {
info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span"); foo += 1;
span!( info!({ yak_shaved = true, yak_count = 1 }, "hi from inside my span");
Level::TRACE, span!(
"my other span", Level::TRACE,
foo_count = &foo, "my other span",
baz_count = 5 foo_count = &foo,
) baz_count = 5
.in_scope(|| { )
warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak"); .in_scope(|| {
}); warn!({ yak_shaved = false, yak_count = -1 }, "failed to shave yak");
}); });
}); });

View File

@ -20,34 +20,33 @@ use self::sloggish_subscriber::SloggishSubscriber;
fn main() { fn main() {
let subscriber = SloggishSubscriber::new(2); 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, "", version = &field::display(5.0)).in_scope(|| { span!(Level::TRACE, "server", host = "localhost", port = 8080).in_scope(|| {
span!(Level::TRACE, "server", host = "localhost", port = 8080).in_scope(|| { info!("starting");
info!("starting"); info!("listening");
info!("listening"); let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381);
let peer1 = span!(Level::TRACE, "conn", peer_addr = "82.9.9.9", port = 42381); peer1.in_scope(|| {
peer1.in_scope(|| { debug!("connected");
debug!("connected"); debug!({ length = 2 }, "message received");
debug!({ length = 2 }, "message received"); });
}); let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230);
let peer2 = span!(Level::TRACE, "conn", peer_addr = "8.8.8.8", port = 18230); peer2.in_scope(|| {
peer2.in_scope(|| { debug!("connected");
debug!("connected"); });
}); peer1.in_scope(|| {
peer1.in_scope(|| { warn!({ algo = "xor" }, "weak encryption requested");
warn!({ algo = "xor" }, "weak encryption requested"); debug!({ length = 8 }, "response sent");
debug!({ length = 8 }, "response sent"); debug!("disconnected");
debug!("disconnected"); });
}); peer2.in_scope(|| {
peer2.in_scope(|| { debug!({ length = 5 }, "message received");
debug!({ length = 5 }, "message received"); debug!({ length = 8 }, "response sent");
debug!({ length = 8 }, "response sent"); debug!("disconnected");
debug!("disconnected"); });
}); warn!("internal error");
warn!("internal error"); info!("exit");
info!("exit"); })
})
});
}); });
} }

View File

@ -35,7 +35,7 @@
//! ``` //! ```
//! //!
//! [`log` crate]: https://docs.rs/log/0.4.6/log/#compile-time-filters //! [`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; use tracing_core::Level;
/// A filter comparable to trace verbosity `Level`. /// A filter comparable to trace verbosity `Level`.

View File

@ -281,7 +281,7 @@
//! # fn main() { //! # fn main() {
//! //!
//! let my_subscriber = FooSubscriber::new(); //! let my_subscriber = FooSubscriber::new();
//! //! # #[cfg(feature = "std")]
//! tracing::subscriber::with_default(my_subscriber, || { //! tracing::subscriber::with_default(my_subscriber, || {
//! // Any trace events generated in this closure or by functions it calls //! // Any trace events generated in this closure or by functions it calls
//! // will be collected by `my_subscriber`. //! // will be collected by `my_subscriber`.
@ -335,13 +335,24 @@
//! due to oddities in macro expansion. //! due to oddities in macro expansion.
//! * `async-await`: enables support for instrumenting `async fn`s with the //! * `async-await`: enables support for instrumenting `async fn`s with the
//! [`#[instrument]`][instrument] attribute. //! [`#[instrument]`][instrument] attribute.
//!
//! ```toml
//! [dependencies]
//! tracing = { version = "0.1", features = ["async-await"] }
//! ```
//!
//! **Note**: this also requires the [`tracing-futures`] crate with the //! **Note**: this also requires the [`tracing-futures`] crate with the
//! `std-future` feature flag enabled. //! `std-future` feature flag enabled.
//! //!
//! ```toml //! * `std`: Depend on the Rust standard library (enabled by default).
//! [dependencies] //!
//! tracing = { version = "0.1", features = ["log", "async-await"] } //! `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/ //! [`log`]: https://docs.rs/log/0.4.6/log/
//! [`span`]: span/index.html //! [`span`]: span/index.html
@ -365,6 +376,11 @@
//! [`tracing-timing`]: https://crates.io/crates/tracing-timing //! [`tracing-timing`]: https://crates.io/crates/tracing-timing
//! [static verbosity level]: level_filters/index.html#compile-time-filters //! [static verbosity level]: level_filters/index.html#compile-time-filters
//! [instrument]: https://docs.rs/tracing-attributes/0.1.0/tracing_attributes/attr.instrument.html //! [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] #[macro_use]
extern crate cfg_if; extern crate cfg_if;
use tracing_core; use tracing_core;
@ -407,8 +423,20 @@ mod macros;
pub mod field; pub mod field;
pub mod level_filters; pub mod level_filters;
pub mod span; pub mod span;
pub(crate) mod stdlib;
pub mod subscriber; 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 { mod sealed {
pub trait Sealed {} pub trait Sealed {}
} }

View File

@ -1069,7 +1069,6 @@ macro_rules! event {
/// ```rust /// ```rust
/// # #[macro_use] /// # #[macro_use]
/// # extern crate tracing; /// # extern crate tracing;
/// # use std::time::SystemTime;
/// # #[derive(Debug, Copy, Clone)] struct Position { x: f32, y: f32 } /// # #[derive(Debug, Copy, Clone)] struct Position { x: f32, y: f32 }
/// # impl Position { /// # impl Position {
/// # const ORIGIN: Self = Self { x: 0.0, y: 0.0 }; /// # const ORIGIN: Self = Self { x: 0.0, y: 0.0 };
@ -1468,7 +1467,10 @@ macro_rules! debug {
/// ```rust /// ```rust
/// # #[macro_use] /// # #[macro_use]
/// # extern crate tracing; /// # 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() { /// # fn main() {
/// # struct Connection { port: u32, speed: f32 } /// # struct Connection { port: u32, speed: f32 }
/// use tracing::field; /// use tracing::field;
@ -1476,10 +1478,10 @@ macro_rules! debug {
/// let addr = Ipv4Addr::new(127, 0, 0, 1); /// let addr = Ipv4Addr::new(127, 0, 0, 1);
/// let conn = Connection { port: 40, speed: 3.20 }; /// let conn = Connection { port: 40, speed: 3.20 };
/// ///
/// info!({ port = conn.port }, "connected to {}", addr); /// info!({ port = conn.port }, "connected to {:?}", addr);
/// info!( /// info!(
/// target: "connection_events", /// target: "connection_events",
/// ip = %addr, /// ip = ?addr,
/// conn.port, /// conn.port,
/// ?conn.speed, /// ?conn.speed,
/// ); /// );
@ -2101,11 +2103,7 @@ macro_rules! callsite {
level: $lvl:expr, level: $lvl:expr,
fields: $($fields:tt)* fields: $($fields:tt)*
) => {{ ) => {{
use std::sync::{ use $crate::{callsite, subscriber::Interest, Metadata, __macro_support::*};
atomic::{self, AtomicUsize, Ordering},
Once,
};
use $crate::{callsite, subscriber::Interest, Metadata};
struct MyCallsite; struct MyCallsite;
static META: Metadata<'static> = { static META: Metadata<'static> = {
$crate::metadata! { $crate::metadata! {
@ -2117,10 +2115,7 @@ macro_rules! callsite {
kind: $kind, kind: $kind,
} }
}; };
// FIXME: Rust 1.34 deprecated ATOMIC_USIZE_INIT. When Tokio's minimum static INTEREST: AtomicUsize = AtomicUsize::new(0);
// supported version is 1.34, replace this with the const fn `::new`.
#[allow(deprecated)]
static INTEREST: AtomicUsize = atomic::ATOMIC_USIZE_INIT;
static REGISTRATION: Once = Once::new(); static REGISTRATION: Once = Once::new();
impl MyCallsite { impl MyCallsite {
#[inline] #[inline]

View File

@ -318,14 +318,14 @@
//! [guard]: struct.Entered.html //! [guard]: struct.Entered.html
pub use tracing_core::span::{Attributes, Id, Record}; pub use tracing_core::span::{Attributes, Id, Record};
use crate::stdlib::{
cmp, fmt,
hash::{Hash, Hasher},
};
use crate::{ use crate::{
dispatcher::{self, Dispatch}, dispatcher::{self, Dispatch},
field, Metadata, field, Metadata,
}; };
use std::{
cmp, fmt,
hash::{Hash, Hasher},
};
/// Trait implemented by types which have a span `Id`. /// Trait implemented by types which have a span `Id`.
pub trait AsId: crate::sealed::Sealed { pub trait AsId: crate::sealed::Sealed {

55
tracing/src/stdlib.rs Normal file
View File

@ -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::*;
}
}

View File

@ -11,6 +11,7 @@ pub use tracing_core::subscriber::*;
/// [`Span`]: ../span/struct.Span.html /// [`Span`]: ../span/struct.Span.html
/// [`Subscriber`]: ../subscriber/trait.Subscriber.html /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
/// [`Event`]: :../event/struct.Event.html /// [`Event`]: :../event/struct.Event.html
#[cfg(feature = "std")]
pub fn with_default<T, S>(subscriber: S, f: impl FnOnce() -> T) -> T pub fn with_default<T, S>(subscriber: S, f: impl FnOnce() -> T) -> T
where where
S: Subscriber + Send + Sync + 'static, S: Subscriber + Send + Sync + 'static,

View File

@ -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] #[macro_use]
extern crate tracing; extern crate tracing;
mod support; mod support;

View File

@ -4,12 +4,14 @@
// added all filters are re-evaluated. The tests being run only in separate // added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with each other // threads with shared global state lets them interfere with each other
#[cfg(not(feature = "std"))]
extern crate std;
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
mod support; mod support;
use self::support::*; use self::support::*;
use tracing::subscriber::with_default;
use tracing::Level; use tracing::Level;
use std::sync::{ use std::sync::{
@ -40,25 +42,27 @@ fn filter_caching_is_lexically_scoped() {
}) })
.run(); .run();
with_default(subscriber, || { // Since this test is in its own file anyway, we can do this. Thus, this
// Call the function once. The filter should be re-evaluated. // test will work even with no-std.
assert!(my_great_function()); tracing::subscriber::set_global_default(subscriber).unwrap();
assert_eq!(count.load(Ordering::Relaxed), 1);
// Call the function again. The cached result should be used. // Call the function once. The filter should be re-evaluated.
assert!(my_great_function()); assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1); assert_eq!(count.load(Ordering::Relaxed), 1);
assert!(my_other_function()); // Call the function again. The cached result should be used.
assert_eq!(count.load(Ordering::Relaxed), 2); assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 1);
assert!(my_great_function()); assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2); assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_other_function()); assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2); assert_eq!(count.load(Ordering::Relaxed), 2);
assert!(my_great_function()); assert!(my_other_function());
assert_eq!(count.load(Ordering::Relaxed), 2); assert_eq!(count.load(Ordering::Relaxed), 2);
});
assert!(my_great_function());
assert_eq!(count.load(Ordering::Relaxed), 2);
} }

View File

@ -3,13 +3,14 @@
// registry. The registry was changed so that each time a new dispatcher is // 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 // added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with each other // threads with shared global state lets them interfere with each other
#[cfg(not(feature = "std"))]
extern crate std;
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
mod support; mod support;
use self::support::*; use self::support::*;
use tracing::subscriber::with_default;
use tracing::Level; use tracing::Level;
use std::sync::{ use std::sync::{
@ -40,30 +41,33 @@ fn filters_are_not_reevaluated_for_the_same_span() {
}) })
.run_with_handle(); .run_with_handle();
with_default(subscriber, move || { // Since this test is in its own file anyway, we can do this. Thus, this
// Enter "alice" and then "bob". The dispatcher expects to see "bob" but // test will work even with no-std.
// not "alice." tracing::subscriber::set_global_default(subscriber).unwrap();
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. // Enter "alice" and then "bob". The dispatcher expects to see "bob" but
assert_eq!(alice_count.load(Ordering::Relaxed), 1); // not "alice."
assert_eq!(bob_count.load(Ordering::Relaxed), 1); let alice = span!(Level::TRACE, "alice");
let bob = alice.in_scope(|| {
alice.in_scope(|| bob.in_scope(|| {})); let bob = span!(Level::TRACE, "bob");
bob.in_scope(|| ());
// The subscriber should see "bob" again, but the filter should not have bob
// 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);
}); });
// 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(); handle.assert_finished();
} }

View File

@ -3,13 +3,14 @@
// registry. The registry was changed so that each time a new dispatcher is // 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 // added all filters are re-evaluated. The tests being run only in separate
// threads with shared global state lets them interfere with each other // threads with shared global state lets them interfere with each other
#[cfg(not(feature = "std"))]
extern crate std;
#[macro_use] #[macro_use]
extern crate tracing; extern crate tracing;
mod support; mod support;
use self::support::*; use self::support::*;
use tracing::subscriber::with_default;
use tracing::Level; use tracing::Level;
use std::sync::{ use std::sync::{
@ -43,38 +44,40 @@ fn filters_are_reevaluated_for_different_call_sites() {
}) })
.run(); .run();
with_default(subscriber, move || { // Since this test is in its own file anyway, we can do this. Thus, this
// Enter "charlie" and then "dave". The dispatcher expects to see "dave" but // test will work even with no-std.
// not "charlie." tracing::subscriber::set_global_default(subscriber).unwrap();
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. // Enter "charlie" and then "dave". The dispatcher expects to see "dave" but
assert_eq!(charlie_count.load(Ordering::Relaxed), 1); // not "charlie."
assert_eq!(dave_count.load(Ordering::Relaxed), 1); let charlie = span!(Level::TRACE, "charlie");
let dave = charlie.in_scope(|| {
charlie.in_scope(|| dave.in_scope(|| {})); let dave = span!(Level::TRACE, "dave");
dave.in_scope(|| {});
// The subscriber should see "dave" again, but the filter should not have dave
// 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);
}); });
// 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);
} }

View File

@ -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] #[macro_use]
extern crate tracing; extern crate tracing;
mod support; mod support;

View File

@ -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] #[macro_use]
extern crate tracing; extern crate tracing;
use tracing::{ use tracing::{