diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index e71d85239c..54ab8b02cd 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- rust: [stable, 1.40.0]
+ rust: [stable, 1.42.0]
steps:
- uses: actions/checkout@main
- uses: actions-rs/toolchain@v1
@@ -82,7 +82,7 @@ jobs:
with:
path: ${{ runner.tool_cache }}/cargo-hack/bin
key: cargo-hack-bin-${{ hashFiles('.github/caching/cargo-hack.lock') }}
- - run: echo "::add-path::${{ runner.tool_cache }}/cargo-hack/bin"
+ - run: echo "${{ runner.tool_cache }}/cargo-hack/bin" >> $GITHUB_PATH
# if `cargo-hack` somehow doesn't exist after loading it from the cache,
# make *sure* it's there.
- run: cargo hack --help || { cargo install --force cargo-hack; }
@@ -145,7 +145,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- rust: [stable, beta, nightly, 1.40.0]
+ rust: [stable, beta, nightly, 1.42.0]
steps:
- uses: actions/checkout@main
- uses: actions-rs/toolchain@v1
@@ -303,5 +303,5 @@ jobs:
with:
command: install
args: --root ${{ runner.tool_cache }}/cargo-audit --force cargo-audit
- - run: echo "::add-path::${{ runner.tool_cache }}/cargo-audit/bin"
+ - run: echo "${{ runner.tool_cache }}/cargo-audit/bin" >> $GITHUB_PATH
- run: cargo audit
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000000..1ca1d80290
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,26 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "cargo",
+ "command": "build",
+ "problemMatcher": [
+ "$rustc"
+ ],
+ "group": "build",
+ "label": "rust: cargo build"
+ },
+ {
+ "type": "cargo",
+ "command": "clippy",
+ "args": [
+ "--all"
+ ],
+ "problemMatcher": [
+ "$rustc"
+ ],
+ "group": "build",
+ "label": "rust: cargo clippy"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 1c3057aeee..a3e0b362aa 100644
--- a/README.md
+++ b/README.md
@@ -256,7 +256,7 @@ attachment that `Future::instrument` does.
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.40. The current Tracing version is not guaranteed to build on Rust
+version is 1.42. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
diff --git a/assets/favicon.ico b/assets/favicon.ico
new file mode 100644
index 0000000000..b5e12792e7
Binary files /dev/null and b/assets/favicon.ico differ
diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index efd932df71..d3de6c941c 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -10,15 +10,15 @@ default = []
[dev-dependencies]
# tracing crates
-tracing = { path = "../tracing", version = "0.1"}
-tracing-core = { path = "../tracing-core", version = "0.1"}
+tracing = { path = "../tracing", version = "0.2"}
+tracing-core = { path = "../tracing-core", version = "0.2"}
tracing-error = { path = "../tracing-error" }
tracing-flame = { path = "../tracing-flame" }
tracing-tower = { version = "0.1.0", path = "../tracing-tower" }
-tracing-subscriber = { path = "../tracing-subscriber", version = "0.2.12", features = ["json", "chrono"] }
-tracing-futures = { version = "0.2.1", path = "../tracing-futures", features = ["futures-01"] }
-tracing-attributes = { path = "../tracing-attributes", version = "0.1.2"}
-tracing-log = { path = "../tracing-log", version = "0.1.1", features = ["env_logger"] }
+tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["json", "chrono"] }
+tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["futures-01"] }
+tracing-attributes = { path = "../tracing-attributes", version = "0.2"}
+tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] }
tracing-serde = { path = "../tracing-serde" }
tracing-opentelemetry = { path = "../tracing-opentelemetry" }
tracing-journald = { path = "../tracing-journald" }
diff --git a/examples/examples/all-levels.rs b/examples/examples/all-levels.rs
index 34f8e944f3..692204add0 100644
--- a/examples/examples/all-levels.rs
+++ b/examples/examples/all-levels.rs
@@ -1,5 +1,11 @@
use tracing::Level;
+#[no_mangle]
+#[inline(never)]
+pub fn event() {
+ tracing::info!("general informational messages relevant to users");
+}
+
fn main() {
tracing_subscriber::fmt()
// all spans/events with a level higher than TRACE (e.g, info, warn, etc.)
@@ -7,6 +13,7 @@ fn main() {
.with_max_level(Level::TRACE)
// sets this to be the default, global subscriber for this application.
.init();
+ event();
tracing::error!("SOMETHING IS SERIOUSLY WRONG!!!");
tracing::warn!("important informational messages; might indicate an error");
diff --git a/examples/examples/hyper-echo.rs b/examples/examples/hyper-echo.rs
index 3404a8d5e9..19c664bfdb 100644
--- a/examples/examples/hyper-echo.rs
+++ b/examples/examples/hyper-echo.rs
@@ -16,78 +16,75 @@ async fn echo(req: Request
) -> Result, hyper::Error> {
uri = ?req.uri(),
headers = ?req.headers()
);
- let _enter = span.enter();
- info!("received request");
- let mut response = Response::new(Body::empty());
+ async move {
+ info!("received request");
+ let mut response = Response::new(Body::empty());
- let (rsp_span, resp) = match (req.method(), req.uri().path()) {
- // Serve some instructions at /
- (&Method::GET, "/") => {
- const BODY: &str = "Try POSTing data to /echo";
- *response.body_mut() = Body::from(BODY);
- (span!(Level::INFO, "response", body = %(&BODY)), response)
- }
+ match (req.method(), req.uri().path()) {
+ // Serve some instructions at /
+ (&Method::GET, "/") => {
+ const BODY: &str = "Try POSTing data to /echo";
+ *response.body_mut() = Body::from(BODY);
+ info!(body = %(&BODY), "response",);
+ Ok(response)
+ }
- // Simply echo the body back to the client.
- (&Method::POST, "/echo") => {
- let span = span!(Level::INFO, "response", response_kind = %"echo");
- *response.body_mut() = req.into_body();
- (span, response)
- }
+ // Simply echo the body back to the client.
+ (&Method::POST, "/echo") => {
+ info!(response_kind = %"echo", "response");
+ *response.body_mut() = req.into_body();
+ Ok(response)
+ }
- // Convert to uppercase before sending back to client.
- (&Method::POST, "/echo/uppercase") => {
- let body = hyper::body::to_bytes(req).await?;
- let upper = body
- .iter()
- .map(|byte| byte.to_ascii_uppercase())
- .collect::>();
- debug!(
- body = ?str::from_utf8(&body[..]),
- uppercased = ?str::from_utf8(&upper[..]),
- "uppercased request body"
- );
+ // Convert to uppercase before sending back to client.
+ (&Method::POST, "/echo/uppercase") => {
+ let body = hyper::body::to_bytes(req).await?;
+ let upper = body
+ .iter()
+ .map(|byte| byte.to_ascii_uppercase())
+ .collect::>();
+ debug!(
+ body = ?str::from_utf8(&body[..]),
+ uppercased = ?str::from_utf8(&upper[..]),
+ "uppercased request body"
+ );
- *response.body_mut() = Body::from(upper);
- (
- span!(Level::INFO, "response", response_kind = %"uppercase"),
- response,
- )
- }
+ info!(response_kind = %"uppercase", "response");
+ *response.body_mut() = Body::from(upper);
+ Ok(response)
+ }
- // Reverse the entire body before sending back to the client.
- (&Method::POST, "/echo/reversed") => {
- let span = span!(Level::TRACE, "response", response_kind = %"reversed");
- let _enter = span.enter();
- let body = hyper::body::to_bytes(req).await?;
- let reversed = body.iter().rev().cloned().collect::>();
- debug!(
- body = ?str::from_utf8(&body[..]),
- "reversed request body"
- );
- *response.body_mut() = Body::from(reversed);
- (
- span!(Level::INFO, "reversed", body = ?(&response.body())),
- response,
- )
- }
+ // Reverse the entire body before sending back to the client.
+ (&Method::POST, "/echo/reversed") => {
+ async move {
+ let body = hyper::body::to_bytes(req).await?;
+ let reversed = body.iter().rev().cloned().collect::>();
+ debug!(
+ body = ?str::from_utf8(&body[..]),
+ "reversed request body"
+ );
+ *response.body_mut() = Body::from(reversed);
+ info!(body = ?(&response.body()), "response");
+ Ok(response)
+ }
+ .instrument(span!(Level::TRACE, "response", response_kind = %"reversed"))
+ .await
+ }
- // The 404 Not Found route...
- _ => {
- *response.status_mut() = StatusCode::NOT_FOUND;
- (
- span!(
- Level::TRACE,
- "response",
+ // The 404 Not Found route...
+ _ => {
+ *response.status_mut() = StatusCode::NOT_FOUND;
+ info!(
body = ?(),
status = ?StatusCode::NOT_FOUND,
- ),
- response,
- )
+ "response",
+ );
+ Ok(response)
+ }
}
- };
- let f = async { resp }.instrument(rsp_span);
- Ok(f.await)
+ }
+ .instrument(span)
+ .await
}
#[tokio::main]
@@ -107,14 +104,13 @@ async fn main() -> Result<(), Box> {
let local_addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();
let server_span = span!(Level::TRACE, "server", %local_addr);
- let _enter = server_span.enter();
let service = make_service_fn(|_| async { Ok::<_, hyper::Error>(service_fn(echo)) });
let server = Server::bind(&local_addr)
.serve(service)
.instrument(server_span.clone());
- info!("listening...");
+ info!(parent: &server_span, "listening...");
server.await?;
Ok(())
diff --git a/examples/examples/tower-load.rs b/examples/examples/tower-load.rs
index 443f74ddf2..f201283275 100644
--- a/examples/examples/tower-load.rs
+++ b/examples/examples/tower-load.rs
@@ -185,11 +185,11 @@ impl Service for MakeSvc {
}
}
-struct AdminSvc {
- handle: Handle,
+struct AdminSvc {
+ handle: Handle,
}
-impl Clone for AdminSvc {
+impl Clone for AdminSvc {
fn clone(&self) -> Self {
Self {
handle: self.handle.clone(),
@@ -197,11 +197,8 @@ impl Clone for AdminSvc {
}
}
-impl<'a, S> Service<&'a AddrStream> for AdminSvc
-where
- S: tracing::Subscriber,
-{
- type Response = AdminSvc;
+impl<'a> Service<&'a AddrStream> for AdminSvc {
+ type Response = AdminSvc;
type Error = hyper::Error;
type Future = Ready>;
@@ -214,10 +211,7 @@ where
}
}
-impl Service> for AdminSvc
-where
- S: tracing::Subscriber + 'static,
-{
+impl Service> for AdminSvc {
type Response = Response;
type Error = Err;
type Future = Pin, Err>> + std::marker::Send>>;
@@ -252,10 +246,7 @@ where
}
}
-impl AdminSvc
-where
- S: tracing::Subscriber + 'static,
-{
+impl AdminSvc {
fn set_from(&self, bytes: Bytes) -> Result<(), String> {
use std::str;
let body = str::from_utf8(&bytes.as_ref()).map_err(|e| format!("{}", e))?;
diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml
index f0f695713a..f0ed555589 100644
--- a/tracing-appender/Cargo.toml
+++ b/tracing-appender/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "tracing-appender"
-version = "0.1.1"
+version = "0.2.0"
authors = [
"Zeki Sherif ",
"Tokio Contributors "
@@ -20,15 +20,15 @@ keywords = ["logging", "tracing", "file-appender", "non-blocking-writer"]
edition = "2018"
[dependencies]
-crossbeam-channel = "0.4.2"
+crossbeam-channel = "0.5.0"
chrono = "0.4.11"
[dependencies.tracing-subscriber]
path = "../tracing-subscriber"
-version = "0.2.7"
+version = "0.3"
default-features = false
features = ["fmt"]
[dev-dependencies]
-tracing = { path = "../tracing", version = "0.1" }
+tracing = { path = "../tracing", version = "0.2" }
tempdir = "0.3"
diff --git a/tracing-appender/README.md b/tracing-appender/README.md
index 5607549c98..4f1748070b 100644
--- a/tracing-appender/README.md
+++ b/tracing-appender/README.md
@@ -36,7 +36,7 @@ allows events and spans to be recorded in a non-blocking manner through a
dedicated logging thread. It also provides a [`RollingFileAppender`][file_appender]
that can be used with _or_ without the non-blocking writer.
-*Compiler support: [requires `rustc` 1.40+][msrv]*
+*Compiler support: [requires `rustc` 1.42+][msrv]*
[msrv]: #supported-rust-versions
@@ -146,7 +146,7 @@ fn main() {
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.40. The current Tracing version is not guaranteed to build on Rust
+version is 1.42. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index cd3ea6110e..7476f079bc 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -7,7 +7,7 @@
//! a dedicated logging thread. It also provides a [`RollingFileAppender`][file_appender] that can
//! be used with _or_ without the non-blocking writer.
//!
-//! *Compiler support: [requires `rustc` 1.40+][msrv]*
+//! *Compiler support: [requires `rustc` 1.42+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//! [file_appender]: ./rolling/struct.RollingFileAppender.html
@@ -111,7 +111,7 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.40. The current Tracing version is not guaranteed to build on
+//! version is 1.42. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
@@ -125,6 +125,7 @@
#![doc(html_root_url = "https://docs.rs/tracing-appender/0.1.1")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
+ html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index 2dae754a43..d351783e6d 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -7,8 +7,8 @@ name = "tracing-attributes"
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
-# - Create "v0.1.x" git tag.
-version = "0.1.11"
+# - Create "v0.2.x" git tag.
+version = "0.2.0"
authors = [
"Tokio Contributors ",
"Eliza Weisman ",
@@ -32,11 +32,6 @@ edition = "2018"
[lib]
proc-macro = true
-[features]
-
-# This feature flag is no longer necessary.
-async-await = []
-
[dependencies]
proc-macro2 = "1"
syn = { version = "1", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] }
@@ -44,9 +39,9 @@ quote = "1"
[dev-dependencies]
-tracing = { path = "../tracing", version = "0.1" }
+tracing = { path = "../tracing", version = "0.2" }
tokio-test = { version = "0.2.0" }
-tracing-core = { path = "../tracing-core", version = "0.1"}
+tracing-core = { path = "../tracing-core", version = "0.2"}
async-trait = "0.1"
[badges]
diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md
index a1dc2ee714..64f1527f26 100644
--- a/tracing-attributes/README.md
+++ b/tracing-attributes/README.md
@@ -37,7 +37,7 @@ structured, event-based diagnostic information. This crate provides the
Note that this macro is also re-exported by the main `tracing` crate.
-*Compiler support: [requires `rustc` 1.40+][msrv]*
+*Compiler support: [requires `rustc` 1.42+][msrv]*
[msrv]: #supported-rust-versions
@@ -69,7 +69,7 @@ pub fn my_function(my_arg: usize) {
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.40. The current Tracing version is not guaranteed to build on Rust
+version is 1.42. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index fd5f854a64..3ed6258e41 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -6,7 +6,7 @@
//!
//! Note that this macro is also re-exported by the main `tracing` crate.
//!
-//! *Compiler support: [requires `rustc` 1.40+][msrv]*
+//! *Compiler support: [requires `rustc` 1.42+][msrv]*
//!
//! [msrv]: #supported-rust-versions
//!
@@ -41,7 +41,7 @@
//! ## Supported Rust Versions
//!
//! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.40. The current Tracing version is not guaranteed to build on
+//! version is 1.42. The current Tracing version is not guaranteed to build on
//! Rust versions earlier than the minimum supported version.
//!
//! Tracing follows the same compiler support policies as the rest of the Tokio
@@ -55,6 +55,7 @@
#![doc(html_root_url = "https://docs.rs/tracing-attributes/0.1.11")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png",
+ html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico",
issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/"
)]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
@@ -468,7 +469,8 @@ fn gen_body(
quote_spanned!(block.span()=>
let __tracing_attr_span = #span;
let __tracing_attr_guard = __tracing_attr_span.enter();
- match { #block } {
+ let f = move || #return_type { #block };
+ match f() {
Ok(x) => Ok(x),
Err(e) => {
tracing::error!(error = %e);
diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index ac95e94d2e..f7e5f3b743 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -223,7 +223,7 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
async fn call_with_self(&self) {}
#[instrument(fields(Self=std::any::type_name::()))]
- async fn call_with_mut_self(self: &mut Self) {}
+ async fn call_with_mut_self(&mut self) {}
}
//let span = span::mock().named("call");
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index eb8b00218d..b2b61bde5f 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -31,6 +31,27 @@ fn test() {
handle.assert_finished();
}
+#[instrument(err)]
+fn err_early_return() -> Result {
+ u8::try_from(1234)?;
+ Ok(5)
+}
+
+#[test]
+fn test_early_return() {
+ let span = span::mock().named("err_early_return");
+ let (subscriber, handle) = subscriber::mock()
+ .new_span(span.clone())
+ .enter(span.clone())
+ .event(event::mock().at_level(Level::ERROR))
+ .exit(span.clone())
+ .drop_span(span)
+ .done()
+ .run_with_handle();
+ with_default(subscriber, || err_early_return().ok());
+ handle.assert_finished();
+}
+
#[instrument(err)]
async fn err_async(polls: usize) -> Result {
let future = PollN::new_ok(polls);
diff --git a/tracing-core/CHANGELOG.md b/tracing-core/CHANGELOG.md
index 40e22a6442..4d04e65229 100644
--- a/tracing-core/CHANGELOG.md
+++ b/tracing-core/CHANGELOG.md
@@ -1,3 +1,20 @@
+# 0.1.17 (September 28, 2020)
+
+### Fixed
+
+- Incorrect inlining of `Event::dispatch` and `Event::child_of`, which could
+ result in `dispatcher::get_default` being inlined at the callsite ([#994])
+
+### Added
+
+- `Copy` implementations for `Level` and `LevelFilter` ([#992])
+
+Thanks to new contributors @jyn514 and @TaKO8Ki for contributing to this
+release!
+
+[#994]: https://github.com/tokio-rs/tracing/pull/994
+[#992]: https://github.com/tokio-rs/tracing/pull/992
+
# 0.1.16 (September 8, 2020)
### Fixed
diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml
index 3f40649c0a..9e3ae1b51f 100644
--- a/tracing-core/Cargo.toml
+++ b/tracing-core/Cargo.toml
@@ -7,8 +7,8 @@ name = "tracing-core"
# - Cargo.toml
# - README.md
# - Update CHANGELOG.md.
-# - Create "v0.1.x" git tag.
-version = "0.1.16"
+# - Create "v0.2.x" git tag.
+version = "0.2.0"
authors = ["Tokio Contributors "]
license = "MIT"
readme = "README.md"
@@ -27,7 +27,8 @@ edition = "2018"
[features]
default = ["std"]
-std = ["lazy_static"]
+alloc = []
+std = ["lazy_static", "alloc"]
[badges]
maintenance = { status = "actively-developed" }
diff --git a/tracing-core/README.md b/tracing-core/README.md
index 6b7714563c..715af65370 100644
--- a/tracing-core/README.md
+++ b/tracing-core/README.md
@@ -16,9 +16,9 @@ Core primitives for application-level tracing.
[Documentation][docs-url] | [Chat][discord-url]
[crates-badge]: https://img.shields.io/crates/v/tracing-core.svg
-[crates-url]: https://crates.io/crates/tracing-core/0.1.16
+[crates-url]: https://crates.io/crates/tracing-core/0.1.17
[docs-badge]: https://docs.rs/tracing-core/badge.svg
-[docs-url]: https://docs.rs/tracing-core/0.1.16
+[docs-url]: https://docs.rs/tracing-core/0.1.17
[docs-master-badge]: https://img.shields.io/badge/docs-master-blue
[docs-master-url]: https://tracing-rs.netlify.com/tracing_core
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
@@ -53,7 +53,7 @@ The crate provides:
In addition, it defines the global callsite registry and per-thread current
dispatcher which other components of the tracing system rely on.
-*Compiler support: [requires `rustc` 1.40+][msrv]*
+*Compiler support: [requires `rustc` 1.42+][msrv]*
[msrv]: #supported-rust-versions
@@ -79,27 +79,27 @@ The following crate feature flags are available:
```toml
[dependencies]
- tracing-core = { version = "0.1.16", default-features = false }
+ tracing-core = { version = "0.1.17", default-features = false }
```
**Note**:`tracing-core`'s `no_std` support requires `liballoc`.
[`tracing`]: ../tracing
-[`span::Id`]: https://docs.rs/tracing-core/0.1.16/tracing_core/span/struct.Id.html
-[`Event`]: https://docs.rs/tracing-core/0.1.16/tracing_core/event/struct.Event.html
-[`Subscriber`]: https://docs.rs/tracing-core/0.1.16/tracing_core/subscriber/trait.Subscriber.html
-[`Metadata`]: https://docs.rs/tracing-core/0.1.16/tracing_core/metadata/struct.Metadata.html
-[`Callsite`]: https://docs.rs/tracing-core/0.1.16/tracing_core/callsite/trait.Callsite.html
-[`Field`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.Field.html
-[`FieldSet`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.FieldSet.html
-[`Value`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/trait.Value.html
-[`ValueSet`]: https://docs.rs/tracing-core/0.1.16/tracing_core/field/struct.ValueSet.html
-[`Dispatch`]: https://docs.rs/tracing-core/0.1.16/tracing_core/dispatcher/struct.Dispatch.html
+[`span::Id`]: https://docs.rs/tracing-core/0.1.17/tracing_core/span/struct.Id.html
+[`Event`]: https://docs.rs/tracing-core/0.1.17/tracing_core/event/struct.Event.html
+[`Subscriber`]: https://docs.rs/tracing-core/0.1.17/tracing_core/subscriber/trait.Subscriber.html
+[`Metadata`]: https://docs.rs/tracing-core/0.1.17/tracing_core/metadata/struct.Metadata.html
+[`Callsite`]: https://docs.rs/tracing-core/0.1.17/tracing_core/callsite/trait.Callsite.html
+[`Field`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.Field.html
+[`FieldSet`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.FieldSet.html
+[`Value`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/trait.Value.html
+[`ValueSet`]: https://docs.rs/tracing-core/0.1.17/tracing_core/field/struct.ValueSet.html
+[`Dispatch`]: https://docs.rs/tracing-core/0.1.17/tracing_core/dispatcher/struct.Dispatch.html
## Supported Rust Versions
Tracing is built against the latest stable release. The minimum supported
-version is 1.40. The current Tracing version is not guaranteed to build on Rust
+version is 1.42. The current Tracing version is not guaranteed to build on Rust
versions earlier than the minimum supported version.
Tracing follows the same compiler support policies as the rest of the Tokio
diff --git a/tracing-core/src/callsite.rs b/tracing-core/src/callsite.rs
index 5e48473e15..01fecb3a2b 100644
--- a/tracing-core/src/callsite.rs
+++ b/tracing-core/src/callsite.rs
@@ -1,39 +1,146 @@
//! Callsites represent the source locations from which spans or events
//! originate.
-use crate::stdlib::{
- fmt,
- hash::{Hash, Hasher},
- sync::Mutex,
- vec::Vec,
-};
use crate::{
dispatcher::{self, Dispatch},
metadata::{LevelFilter, Metadata},
subscriber::Interest,
};
+use core::{
+ fmt,
+ hash::{Hash, Hasher},
+ ptr,
+ sync::atomic::{AtomicPtr, Ordering},
+};
-lazy_static! {
- static ref REGISTRY: Mutex = Mutex::new(Registry {
- callsites: Vec::new(),
- dispatchers: Vec::new(),
- });
+type Callsites = LinkedList;
+
+/// Trait implemented by callsites.
+///
+/// These functions are only intended to be called by the callsite registry, which
+/// correctly handles determining the common interest between all subscribers.
+pub trait Callsite: Sync {
+ /// Sets the [`Interest`] for this callsite.
+ ///
+ /// [`Interest`]: super::subscriber::Interest
+ fn set_interest(&self, interest: Interest);
+
+ /// Returns the [metadata] associated with the callsite.
+ ///
+ /// [metadata]: super::metadata::Metadata
+ fn metadata(&self) -> &Metadata<'_>;
}
-struct Registry {
- callsites: Vec<&'static dyn Callsite>,
- dispatchers: Vec,
+/// Uniquely identifies a [`Callsite`]
+///
+/// Two `Identifier`s are equal if they both refer to the same callsite.
+///
+/// [`Callsite`]: super::callsite::Callsite
+#[derive(Clone)]
+pub struct Identifier(
+ /// **Warning**: The fields on this type are currently `pub` because it must
+ /// be able to be constructed statically by macros. However, when `const
+ /// fn`s are available on stable Rust, this will no longer be necessary.
+ /// Thus, these fields are *not* considered stable public API, and they may
+ /// change warning. Do not rely on any fields on `Identifier`. When
+ /// constructing new `Identifier`s, use the `identify_callsite!` macro or
+ /// the `Callsite::id` function instead.
+ // TODO: When `Callsite::id` is a const fn, this need no longer be `pub`.
+ #[doc(hidden)]
+ pub &'static dyn Callsite,
+);
+
+/// A registration with the callsite registry.
+///
+/// Every [`Callsite`] implementation must provide a `&'static Registration`
+/// when calling [`register`] to add itself to the global callsite registry.
+///
+/// [`Callsite`]: crate::callsite::Callsite
+/// [`register`]: crate::callsite::register
+pub struct Registration {
+ callsite: T,
+ next: AtomicPtr>,
}
-impl Registry {
- fn rebuild_callsite_interest(&self, callsite: &'static dyn Callsite) {
+pub(crate) use self::inner::register_dispatch;
+pub use self::inner::{rebuild_interest_cache, register};
+
+#[cfg(feature = "std")]
+mod inner {
+ use super::*;
+ use std::sync::RwLock;
+ use std::vec::Vec;
+
+ type Dispatchers = Vec;
+
+ struct Registry {
+ callsites: Callsites,
+ dispatchers: RwLock,
+ }
+
+ lazy_static! {
+ static ref REGISTRY: Registry = Registry {
+ callsites: LinkedList::new(),
+ dispatchers: RwLock::new(Vec::new()),
+ };
+ }
+
+ /// Clear and reregister interest on every [`Callsite`]
+ ///
+ /// This function is intended for runtime reconfiguration of filters on traces
+ /// when the filter recalculation is much less frequent than trace events are.
+ /// The alternative is to have the [`Subscriber`] that supports runtime
+ /// reconfiguration of filters always return [`Interest::sometimes()`] so that
+ /// [`enabled`] is evaluated for every event.
+ ///
+ /// This function will also re-compute the global maximum level as determined by
+ /// the [`max_level_hint`] method. If a [`Subscriber`]
+ /// implementation changes the value returned by its `max_level_hint`
+ /// implementation at runtime, then it **must** call this function after that
+ /// value changes, in order for the change to be reflected.
+ ///
+ /// [`max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
+ /// [`Callsite`]: crate::callsite::Callsite
+ /// [`enabled`]: crate::subscriber::Subscriber::enabled
+ /// [`Interest::sometimes()`]: crate::subscriber::Interest::sometimes
+ /// [`Subscriber`]: crate::subscriber::Subscriber
+ pub fn rebuild_interest_cache() {
+ let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
+ let callsites = ®ISTRY.callsites;
+ rebuild_interest(callsites, &mut dispatchers);
+ }
+
+ /// Register a new `Callsite` with the global registry.
+ ///
+ /// This should be called once per callsite after the callsite has been
+ /// constructed.
+ pub fn register(registration: &'static Registration) {
+ let dispatchers = REGISTRY.dispatchers.read().unwrap();
+ rebuild_callsite_interest(&dispatchers, registration.callsite);
+ REGISTRY.callsites.push(registration);
+ }
+
+ pub(crate) fn register_dispatch(dispatch: &Dispatch) {
+ let mut dispatchers = REGISTRY.dispatchers.write().unwrap();
+ let callsites = ®ISTRY.callsites;
+
+ dispatchers.push(dispatch.registrar());
+
+ rebuild_interest(callsites, &mut dispatchers);
+ }
+
+ fn rebuild_callsite_interest(
+ dispatchers: &[dispatcher::Registrar],
+ callsite: &'static dyn Callsite,
+ ) {
let meta = callsite.metadata();
// Iterate over the subscribers in the registry, and — if they are
// active — register the callsite with them.
- let mut interests = self
- .dispatchers
- .iter()
- .filter_map(|registrar| registrar.try_register(meta));
+ let mut interests = dispatchers.iter().filter_map(|registrar| {
+ registrar
+ .upgrade()
+ .map(|dispatch| dispatch.register_callsite(meta))
+ });
// Use the first subscriber's `Interest` as the base value.
let interest = if let Some(interest) = interests.next() {
@@ -47,9 +154,9 @@ impl Registry {
callsite.set_interest(interest)
}
- fn rebuild_interest(&mut self) {
+ fn rebuild_interest(callsites: &Callsites, dispatchers: &mut Vec) {
let mut max_level = LevelFilter::OFF;
- self.dispatchers.retain(|registrar| {
+ dispatchers.retain(|registrar| {
if let Some(dispatch) = registrar.upgrade() {
// If the subscriber did not provide a max level hint, assume
// that it may enable every level.
@@ -63,86 +170,64 @@ impl Registry {
}
});
- self.callsites.iter().for_each(|&callsite| {
- self.rebuild_callsite_interest(callsite);
- });
+ callsites.for_each(|reg| rebuild_callsite_interest(dispatchers, reg.callsite));
+
LevelFilter::set_max(max_level);
}
}
-/// Trait implemented by callsites.
-///
-/// These functions are only intended to be called by the callsite registry, which
-/// correctly handles determining the common interest between all subscribers.
-pub trait Callsite: Sync {
- /// Sets the [`Interest`] for this callsite.
+#[cfg(not(feature = "std"))]
+mod inner {
+ use super::*;
+ static REGISTRY: Callsites = LinkedList::new();
+
+ /// Clear and reregister interest on every [`Callsite`]
///
- /// [`Interest`]: ../subscriber/struct.Interest.html
- fn set_interest(&self, interest: Interest);
+ /// This function is intended for runtime reconfiguration of filters on traces
+ /// when the filter recalculation is much less frequent than trace events are.
+ /// The alternative is to have the [`Subscriber`] that supports runtime
+ /// reconfiguration of filters always return [`Interest::sometimes()`] so that
+ /// [`enabled`] is evaluated for every event.
+ ///
+ /// This function will also re-compute the global maximum level as determined by
+ /// the [`max_level_hint`] method. If a [`Subscriber`]
+ /// implementation changes the value returned by its `max_level_hint`
+ /// implementation at runtime, then it **must** call this function after that
+ /// value changes, in order for the change to be reflected.
+ ///
+ /// [`max_level_hint`]: crate::subscriber::Subscriber::max_level_hint
+ /// [`Callsite`]: crate::callsite::Callsite
+ /// [`enabled`]: crate::subscriber::Subscriber::enabled
+ /// [`Interest::sometimes()`]: crate::subscriber::Interest::sometimes
+ /// [`Subscriber`]: crate::subscriber::Subscriber
+ pub fn rebuild_interest_cache() {
+ register_dispatch(dispatcher::get_global());
+ }
- /// Returns the [metadata] associated with the callsite.
+ /// Register a new `Callsite` with the global registry.
///
- /// [metadata]: ../metadata/struct.Metadata.html
- fn metadata(&self) -> &Metadata<'_>;
-}
+ /// This should be called once per callsite after the callsite has been
+ /// constructed.
+ pub fn register(registration: &'static Registration) {
+ rebuild_callsite_interest(dispatcher::get_global(), registration.callsite);
+ REGISTRY.push(registration);
+ }
-/// Uniquely identifies a [`Callsite`]
-///
-/// Two `Identifier`s are equal if they both refer to the same callsite.
-///
-/// [`Callsite`]: ../callsite/trait.Callsite.html
-#[derive(Clone)]
-pub struct Identifier(
- /// **Warning**: The fields on this type are currently `pub` because it must
- /// be able to be constructed statically by macros. However, when `const
- /// fn`s are available on stable Rust, this will no longer be necessary.
- /// Thus, these fields are *not* considered stable public API, and they may
- /// change warning. Do not rely on any fields on `Identifier`. When
- /// constructing new `Identifier`s, use the `identify_callsite!` macro or
- /// the `Callsite::id` function instead.
- // TODO: When `Callsite::id` is a const fn, this need no longer be `pub`.
- #[doc(hidden)]
- pub &'static dyn Callsite,
-);
+ pub(crate) fn register_dispatch(dispatcher: &Dispatch) {
+ // If the subscriber did not provide a max level hint, assume
+ // that it may enable every level.
+ let level_hint = dispatcher.max_level_hint().unwrap_or(LevelFilter::TRACE);
-/// Clear and reregister interest on every [`Callsite`]
-///
-/// This function is intended for runtime reconfiguration of filters on traces
-/// when the filter recalculation is much less frequent than trace events are.
-/// The alternative is to have the [`Subscriber`] that supports runtime
-/// reconfiguration of filters always return [`Interest::sometimes()`] so that
-/// [`enabled`] is evaluated for every event.
-///
-/// This function will also re-compute the global maximum level as determined by
-/// the [`max_level_hint`] method. If a [`Subscriber`]
-/// implementation changes the value returned by its `max_level_hint`
-/// implementation at runtime, then it **must** call this function after that
-/// value changes, in order for the change to be reflected.
-///
-/// [`max_level_hint`]: ../subscriber/trait.Subscriber.html#method.max_level_hint
-/// [`Callsite`]: ../callsite/trait.Callsite.html
-/// [`enabled`]: ../subscriber/trait.Subscriber.html#tymethod.enabled
-/// [`Interest::sometimes()`]: ../subscriber/struct.Interest.html#method.sometimes
-/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
-pub fn rebuild_interest_cache() {
- let mut registry = REGISTRY.lock().unwrap();
- registry.rebuild_interest();
-}
+ REGISTRY.for_each(|reg| rebuild_callsite_interest(dispatcher, reg.callsite));
-/// Register a new `Callsite` with the global registry.
-///
-/// This should be called once per callsite after the callsite has been
-/// constructed.
-pub fn register(callsite: &'static dyn Callsite) {
- let mut registry = REGISTRY.lock().unwrap();
- registry.rebuild_callsite_interest(callsite);
- registry.callsites.push(callsite);
-}
+ LevelFilter::set_max(level_hint);
+ }
+
+ fn rebuild_callsite_interest(dispatcher: &Dispatch, callsite: &'static dyn Callsite) {
+ let meta = callsite.metadata();
-pub(crate) fn register_dispatch(dispatch: &Dispatch) {
- let mut registry = REGISTRY.lock().unwrap();
- registry.dispatchers.push(dispatch.registrar());
- registry.rebuild_interest();
+ callsite.set_interest(dispatcher.register_callsite(meta))
+ }
}
// ===== impl Identifier =====
@@ -169,3 +254,208 @@ impl Hash for Identifier {
(self.0 as *const dyn Callsite).hash(state)
}
}
+
+// ===== impl Registration =====
+
+impl Registration {
+ /// Construct a new `Registration` from some `&'static dyn Callsite`
+ pub const fn new(callsite: T) -> Self {
+ Self {
+ callsite,
+ next: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+}
+
+impl fmt::Debug for Registration {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Registration")
+ .field("callsite", &format_args!("{:p}", self.callsite))
+ .field(
+ "next",
+ &format_args!("{:p}", self.next.load(Ordering::Acquire)),
+ )
+ .finish()
+ }
+}
+
+// ===== impl LinkedList =====
+
+/// An intrusive atomic push-only linked list.
+struct LinkedList {
+ head: AtomicPtr>,
+}
+
+impl LinkedList {
+ const fn new() -> Self {
+ LinkedList {
+ head: AtomicPtr::new(ptr::null_mut()),
+ }
+ }
+}
+
+impl LinkedList {
+ fn for_each(&self, mut f: impl FnMut(&'static Registration)) {
+ let mut head = self.head.load(Ordering::Acquire);
+
+ while let Some(reg) = unsafe { head.as_ref() } {
+ f(reg);
+
+ head = reg.next.load(Ordering::Acquire);
+ }
+ }
+
+ fn push(&self, registration: &'static Registration) {
+ let mut head = self.head.load(Ordering::Acquire);
+
+ loop {
+ registration.next.store(head, Ordering::Release);
+
+ assert_ne!(
+ registration as *const _, head,
+ "Attempted to register a `Callsite` that already exists! \
+ This will cause an infinite loop when attempting to read from the \
+ callsite cache. This is likely a bug! You should only need to call \
+ `tracing-core::callsite::register` once per `Callsite`."
+ );
+
+ match self.head.compare_exchange(
+ head,
+ registration as *const _ as *mut _,
+ Ordering::AcqRel,
+ Ordering::Acquire,
+ ) {
+ Ok(_) => {
+ break;
+ }
+ Err(current) => head = current,
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ struct TestCallsite;
+ static CS1: TestCallsite = TestCallsite;
+ static CS2: TestCallsite = TestCallsite;
+
+ impl Callsite for TestCallsite {
+ fn set_interest(&self, _interest: Interest) {}
+ fn metadata(&self) -> &Metadata<'_> {
+ unimplemented!("not needed for this test")
+ }
+ }
+
+ #[test]
+ fn linked_list_push() {
+ static REG1: Registration = Registration::new(&CS1);
+ static REG2: Registration = Registration::new(&CS2);
+
+ let linked_list = LinkedList::new();
+
+ linked_list.push(®1);
+ linked_list.push(®2);
+
+ let mut i = 0;
+
+ linked_list.for_each(|reg| {
+ if i == 0 {
+ assert!(
+ ptr::eq(reg, ®2),
+ "Registration pointers need to match REG2"
+ );
+ } else {
+ assert!(
+ ptr::eq(reg, ®1),
+ "Registration pointers need to match REG1"
+ );
+ }
+
+ i += 1;
+ });
+ }
+
+ #[test]
+ fn linked_list_push_several() {
+ static REG1: Registration = Registration::new(&CS1);
+ static REG2: Registration = Registration::new(&CS2);
+ static REG3: Registration = Registration::new(&CS1);
+ static REG4: Registration = Registration::new(&CS2);
+
+ let linked_list = LinkedList::new();
+
+ fn expect<'a>(
+ callsites: &'a mut impl Iterator,
+ ) -> impl FnMut(&'static Registration) + 'a {
+ move |reg: &'static Registration| {
+ let ptr = callsites
+ .next()
+ .expect("list contained more than the expected number of registrations!");
+
+ assert!(
+ ptr::eq(reg, ptr),
+ "Registration pointers need to match ({:?} != {:?})",
+ reg,
+ ptr
+ );
+ }
+ }
+
+ linked_list.push(®1);
+ linked_list.push(®2);
+ let regs = [®2, ®1];
+ let mut callsites = regs.iter().copied();
+ linked_list.for_each(expect(&mut callsites));
+ assert!(
+ callsites.next().is_none(),
+ "some registrations were expected but not present: {:?}",
+ callsites
+ );
+
+ linked_list.push(®3);
+ let regs = [®3, ®2, ®1];
+ let mut callsites = regs.iter().copied();
+ linked_list.for_each(expect(&mut callsites));
+ assert!(
+ callsites.next().is_none(),
+ "some registrations were expected but not present: {:?}",
+ callsites
+ );
+
+ linked_list.push(®4);
+ let regs = [®4, ®3, ®2, ®1];
+ let mut callsites = regs.iter().copied();
+ linked_list.for_each(expect(&mut callsites));
+ assert!(
+ callsites.next().is_none(),
+ "some registrations were expected but not present: {:?}",
+ callsites
+ );
+ }
+
+ #[test]
+ #[should_panic]
+ fn linked_list_repeated() {
+ static REG1: Registration = Registration::new(&CS1);
+
+ let linked_list = LinkedList::new();
+
+ linked_list.push(®1);
+ // Pass in same reg and we should panic...
+ linked_list.push(®1);
+
+ linked_list.for_each(|_| {});
+ }
+
+ #[test]
+ fn linked_list_empty() {
+ let linked_list = LinkedList::new();
+
+ linked_list.for_each(|_| {
+ panic!("List should be empty");
+ });
+ }
+}
diff --git a/tracing-core/src/dispatcher.rs b/tracing-core/src/dispatcher.rs
index 658463584e..086e53d40b 100644
--- a/tracing-core/src/dispatcher.rs
+++ b/tracing-core/src/dispatcher.rs
@@ -38,9 +38,12 @@
//! # fn exit(&self, _: &Id) {}
//! # }
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
+//! # #[cfg(feature = "alloc")]
//! use dispatcher::Dispatch;
//!
+//! # #[cfg(feature = "alloc")]
//! let my_subscriber = FooSubscriber::new();
+//! # #[cfg(feature = "alloc")]
//! let my_dispatch = Dispatch::new(my_subscriber);
//! ```
//! Then, we can use [`with_default`] to set our `Dispatch` as the default for
@@ -61,8 +64,9 @@
//! # fn exit(&self, _: &Id) {}
//! # }
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
-//! # let my_subscriber = FooSubscriber::new();
-//! # let my_dispatch = dispatcher::Dispatch::new(my_subscriber);
+//! # let _my_subscriber = FooSubscriber::new();
+//! # #[cfg(feature = "std")]
+//! # let my_dispatch = dispatcher::Dispatch::new(_my_subscriber);
//! // no default subscriber
//!
//! # #[cfg(feature = "std")]
@@ -96,10 +100,13 @@
//! # fn exit(&self, _: &Id) {}
//! # }
//! # impl FooSubscriber { fn new() -> Self { FooSubscriber } }
+//! # #[cfg(feature = "std")]
//! # let my_subscriber = FooSubscriber::new();
+//! # #[cfg(feature = "std")]
//! # let my_dispatch = dispatcher::Dispatch::new(my_subscriber);
//! // no default subscriber
//!
+//! # #[cfg(feature = "std")]
//! dispatcher::set_global_default(my_dispatch)
//! // `set_global_default` will return an error if the global default
//! // subscriber has already been set.
@@ -113,11 +120,10 @@
//!
//!
//!
-//! Note:the thread-local scoped dispatcher
-//! (with_default) requires the
-//! Rust standard library. no_std users should use
-//! set_global_default
-//! instead.
+//!
+//! **Note**: the thread-local scoped dispatcher ([`with_default`]) requires the
+//! Rust standard library. `no_std` users should use [`set_global_default`] instead.
+//!
//!
/// Note: This function required the Rust standard library.
-/// no_std users should use
-/// set_global_default instead.
+///
+///
+/// `no_std` users should use [`set_global_default`] instead.
+///
///
-/// Note: This function required the Rust standard library.
-/// no_std users should use
-/// set_global_default instead.
-///
///
-/// [`set_global_default`]: ../fn.set_global_default.html
+/// **Note**: This function required the Rust standard library.
+/// `no_std` users should use [`set_global_default`] instead.
+///
+///
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[must_use = "Dropping the guard unregisters the dispatcher."]
@@ -276,14 +304,30 @@ pub fn set_default(dispatcher: &Dispatch) -> DefaultGuard {
/// executables that depend on the library try to set the default later.
///
///
-/// [span]: ../span/index.html
-/// [`Subscriber`]: ../subscriber/trait.Subscriber.html
-/// [`Event`]: ../event/struct.Event.html
+/// [span]: super::span
+/// [`Subscriber`]: super::subscriber::Subscriber
+/// [`Event`]: super::event::Event
pub fn set_global_default(dispatcher: Dispatch) -> Result<(), SetGlobalDefaultError> {
if GLOBAL_INIT.compare_and_swap(UNINITIALIZED, INITIALIZING, Ordering::SeqCst) == UNINITIALIZED
{
+ #[cfg(feature = "alloc")]
+ let subscriber = {
+ let subscriber = match dispatcher.subscriber {
+ Kind::Global(s) => s,
+ Kind::Scoped(s) => unsafe {
+ // safety: this leaks the subscriber onto the heap. the
+ // reference count will always be at least 1.
+ &*Arc::into_raw(s)
+ },
+ };
+ Kind::Global(subscriber)
+ };
+
+ #[cfg(not(feature = "alloc"))]
+ let subscriber = dispatcher.subscriber;
+
unsafe {
- GLOBAL_DISPATCH = Some(dispatcher);
+ GLOBAL_DISPATCH = Dispatch { subscriber };
}
GLOBAL_INIT.store(INITIALIZED, Ordering::SeqCst);
EXISTS.store(true, Ordering::Release);
@@ -325,16 +369,41 @@ impl error::Error for SetGlobalDefaultError {}
/// called while inside of another `get_default`, that closure will be provided
/// with `Dispatch::none` rather than the previously set dispatcher.
///
-/// [dispatcher]: ../dispatcher/struct.Dispatch.html
+/// [dispatcher]: super::dispatcher::Dispatch
#[cfg(feature = "std")]
pub fn get_default(mut f: F) -> T
where
F: FnMut(&Dispatch) -> T,
{
+ if SCOPED_COUNT.load(Ordering::Acquire) == 0 {
+ // fast path if no scoped dispatcher has been set; just use the global
+ // default.
+ return f(get_global());
+ }
+
+ // While this guard is active, additional calls to subscriber functions on
+ // the default dispatcher will not be able to access the dispatch context.
+ // Dropping the guard will allow the dispatch context to be re-entered.
+ struct Entered<'a>(&'a Cell);
+ impl<'a> Drop for Entered<'a> {
+ #[inline]
+ fn drop(&mut self) {
+ self.0.set(true);
+ }
+ }
+
CURRENT_STATE
.try_with(|state| {
- if let Some(entered) = state.enter() {
- return f(&*entered.current());
+ if state.can_enter.replace(false) {
+ let _guard = Entered(&state.can_enter);
+
+ let mut default = state.default.borrow_mut();
+
+ if default.is::() {
+ // don't redo this call on the next check
+ *default = get_global().clone();
+ }
+ return f(&*default);
}
f(&Dispatch::none())
@@ -348,7 +417,7 @@ where
/// called while inside of another `get_default`, that closure will be provided
/// with `Dispatch::none` rather than the previously set dispatcher.
///
-/// [dispatcher]: ../dispatcher/struct.Dispatch.html
+/// [dispatcher]: super::dispatcher::Dispatch
#[cfg(feature = "std")]
#[doc(hidden)]
#[inline(never)]
@@ -363,69 +432,149 @@ pub fn get_current(f: impl FnOnce(&Dispatch) -> T) -> Option {
/// Executes a closure with a reference to the current [dispatcher].
///
-/// [dispatcher]: ../dispatcher/struct.Dispatch.html
+/// [dispatcher]: super::dispatcher::Dispatch
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub fn get_current(f: impl FnOnce(&Dispatch) -> T) -> Option {
- let dispatch = get_global()?;
- Some(f(&dispatch))
+ Some(f(&get_global()))
}
/// Executes a closure with a reference to the current [dispatcher].
///
-/// [dispatcher]: ../dispatcher/struct.Dispatch.html
+/// [dispatcher]: super::dispatcher::Dispatch
#[cfg(not(feature = "std"))]
pub fn get_default(mut f: F) -> T
where
F: FnMut(&Dispatch) -> T,
{
- if let Some(d) = get_global() {
- f(d)
- } else {
- f(&Dispatch::none())
- }
+ f(get_global())
}
-fn get_global() -> Option<&'static Dispatch> {
- if GLOBAL_INIT.load(Ordering::SeqCst) != INITIALIZED {
- return None;
+#[inline(always)]
+pub(crate) fn get_global() -> &'static Dispatch {
+ if GLOBAL_INIT.load(Ordering::Acquire) != INITIALIZED {
+ return &NONE;
}
unsafe {
// This is safe given the invariant that setting the global dispatcher
// also sets `GLOBAL_INIT` to `INITIALIZED`.
- Some(GLOBAL_DISPATCH.as_ref().expect(
- "invariant violated: GLOBAL_DISPATCH must be initialized before GLOBAL_INIT is set",
- ))
+ &GLOBAL_DISPATCH
}
}
-pub(crate) struct Registrar(Weak);
+#[cfg(feature = "std")]
+pub(crate) struct Registrar(Kind>);
impl Dispatch {
/// Returns a new `Dispatch` that discards events and spans.
#[inline]
pub fn none() -> Self {
Dispatch {
- subscriber: Arc::new(NoSubscriber),
+ #[cfg(feature = "alloc")]
+ subscriber: Kind::Global(&NO_SUBSCRIBER),
+ #[cfg(not(feature = "alloc"))]
+ subscriber: &NO_SUBSCRIBER,
}
}
/// Returns a `Dispatch` that forwards to the given [`Subscriber`].
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ #[cfg(feature = "alloc")]
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
pub fn new(subscriber: S) -> Self
where
S: Subscriber + Send + Sync + 'static,
{
let me = Dispatch {
- subscriber: Arc::new(subscriber),
+ subscriber: Kind::Scoped(Arc::new(subscriber)),
+ };
+ crate::callsite::register_dispatch(&me);
+ me
+ }
+
+ /// Returns a `Dispatch` that forwards to the given static [`Subscriber`].
+ ///
+ /// Unlike [`Dispatch::new`], this function is always available on all
+ /// platforms, even when the `std` or `alloc` features are disabled.
+ ///
+ /// In order to use `from_static`, the `Subscriber` itself must be stored in
+ /// a static. For example:
+ ///
+ /// ```rust
+ /// struct MySubscriber {
+ /// // ...
+ /// }
+ ///
+ /// # use tracing_core::{span::{Id, Attributes, Record}, Event, Metadata};
+ /// impl tracing_core::Subscriber for MySubscriber {
+ /// // ...
+ /// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) }
+ /// # fn record(&self, _: &Id, _: &Record) {}
+ /// # fn event(&self, _: &Event) {}
+ /// # fn record_follows_from(&self, _: &Id, _: &Id) {}
+ /// # fn enabled(&self, _: &Metadata) -> bool { false }
+ /// # fn enter(&self, _: &Id) {}
+ /// # fn exit(&self, _: &Id) {}
+ /// }
+ ///
+ /// static SUBSCRIBER: MySubscriber = MySubscriber {
+ /// // ...
+ /// };
+ ///
+ /// fn main() {
+ /// use tracing_core::dispatcher::{self, Dispatch};
+ ///
+ /// let dispatch = Dispatch::from_static(&SUBSCRIBER);
+ ///
+ /// dispatcher::set_global_default(dispatch)
+ /// .expect("no global default subscriber should have been set previously!");
+ /// }
+ /// ```
+ ///
+ /// Constructing the subscriber in a static initializer may make some forms
+ /// of runtime configuration more challenging. If this is the case, users
+ /// with access to `liballoc` or the Rust standard library are encouraged to
+ /// use [`Dispatch::new`] rather than `from_static`. `no_std` users who
+ /// cannot allocate or do not have access to `liballoc` may want to consider
+ /// the [`lazy_static`] crate, or another library which allows lazy
+ /// initialization of statics.
+ ///
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`Dispatch::new`]: Dispatch::new
+ /// [`lazy_static`]: https://crates.io/crates/lazy_static
+ pub fn from_static(subscriber: &'static (dyn Subscriber + Send + Sync)) -> Self {
+ #[cfg(feature = "alloc")]
+ let me = Self {
+ subscriber: Kind::Global(subscriber),
};
- callsite::register_dispatch(&me);
+ #[cfg(not(feature = "alloc"))]
+ let me = Self { subscriber };
+ crate::callsite::register_dispatch(&me);
me
}
+ #[cfg(feature = "std")]
pub(crate) fn registrar(&self) -> Registrar {
- Registrar(Arc::downgrade(&self.subscriber))
+ Registrar(match self.subscriber {
+ Kind::Scoped(ref s) => Kind::Scoped(Arc::downgrade(s)),
+ Kind::Global(s) => Kind::Global(s),
+ })
+ }
+
+ #[inline(always)]
+ #[cfg(feature = "alloc")]
+ fn subscriber(&self) -> &(dyn Subscriber + Send + Sync) {
+ match self.subscriber {
+ Kind::Scoped(ref s) => Arc::deref(s),
+ Kind::Global(s) => s,
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(not(feature = "alloc"))]
+ fn subscriber(&self) -> &(dyn Subscriber + Send + Sync) {
+ self.subscriber
}
/// Registers a new callsite with this subscriber, returning whether or not
@@ -434,11 +583,11 @@ impl Dispatch {
/// This calls the [`register_callsite`] function on the [`Subscriber`]
/// that this `Dispatch` forwards to.
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`register_callsite`]: ../subscriber/trait.Subscriber.html#method.register_callsite
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`register_callsite`]: super::subscriber::Subscriber::register_callsite
#[inline]
pub fn register_callsite(&self, metadata: &'static Metadata<'static>) -> subscriber::Interest {
- self.subscriber.register_callsite(metadata)
+ self.subscriber().register_callsite(metadata)
}
/// Returns the highest [verbosity level][level] that this [`Subscriber`] will
@@ -448,13 +597,13 @@ impl Dispatch {
/// This calls the [`max_level_hint`] function on the [`Subscriber`]
/// that this `Dispatch` forwards to.
///
- /// [level]: ../struct.Level.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`register_callsite`]: ../subscriber/trait.Subscriber.html#method.max_level_hint
+ /// [level]: super::Level
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`register_callsite`]: super::subscriber::Subscriber::max_level_hint
// TODO(eliza): consider making this a public API?
#[inline]
pub(crate) fn max_level_hint(&self) -> Option {
- self.subscriber.max_level_hint()
+ self.subscriber().max_level_hint()
}
/// Record the construction of a new span, returning a new [ID] for the
@@ -463,12 +612,12 @@ impl Dispatch {
/// This calls the [`new_span`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [ID]: ../span/struct.Id.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
+ /// [ID]: super::span::Id
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`new_span`]: super::subscriber::Subscriber::new_span
#[inline]
pub fn new_span(&self, span: &span::Attributes<'_>) -> span::Id {
- self.subscriber.new_span(span)
+ self.subscriber().new_span(span)
}
/// Record a set of values on a span.
@@ -476,11 +625,11 @@ impl Dispatch {
/// This calls the [`record`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`record`]: ../subscriber/trait.Subscriber.html#method.record
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`record`]: super::subscriber::Subscriber::record
#[inline]
pub fn record(&self, span: &span::Id, values: &span::Record<'_>) {
- self.subscriber.record(span, values)
+ self.subscriber().record(span, values)
}
/// Adds an indication that `span` follows from the span with the id
@@ -489,11 +638,11 @@ impl Dispatch {
/// This calls the [`record_follows_from`] function on the [`Subscriber`]
/// that this `Dispatch` forwards to.
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`record_follows_from`]: ../subscriber/trait.Subscriber.html#method.record_follows_from
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`record_follows_from`]: super::subscriber::Subscriber::record_follows_from
#[inline]
pub fn record_follows_from(&self, span: &span::Id, follows: &span::Id) {
- self.subscriber.record_follows_from(span, follows)
+ self.subscriber().record_follows_from(span, follows)
}
/// Returns true if a span with the specified [metadata] would be
@@ -502,12 +651,12 @@ impl Dispatch {
/// This calls the [`enabled`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [metadata]: ../metadata/struct.Metadata.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`enabled`]: ../subscriber/trait.Subscriber.html#method.enabled
+ /// [metadata]: super::metadata::Metadata
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`enabled`]: super::subscriber::Subscriber::enabled
#[inline]
pub fn enabled(&self, metadata: &Metadata<'_>) -> bool {
- self.subscriber.enabled(metadata)
+ self.subscriber().enabled(metadata)
}
/// Records that an [`Event`] has occurred.
@@ -515,12 +664,12 @@ impl Dispatch {
/// This calls the [`event`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [`Event`]: ../event/struct.Event.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`event`]: ../subscriber/trait.Subscriber.html#method.event
+ /// [`Event`]: super::event::Event
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`event`]: super::subscriber::Subscriber::event
#[inline]
pub fn event(&self, event: &Event<'_>) {
- self.subscriber.event(event)
+ self.subscriber().event(event)
}
/// Records that a span has been can_enter.
@@ -528,11 +677,11 @@ impl Dispatch {
/// This calls the [`enter`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`enter`]: ../subscriber/trait.Subscriber.html#method.enter
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`enter`]: super::subscriber::Subscriber::enter
#[inline]
pub fn enter(&self, span: &span::Id) {
- self.subscriber.enter(span);
+ self.subscriber().enter(span);
}
/// Records that a span has been exited.
@@ -540,11 +689,11 @@ impl Dispatch {
/// This calls the [`exit`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`exit`]: ../subscriber/trait.Subscriber.html#method.exit
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`exit`]: super::subscriber::Subscriber::exit
#[inline]
pub fn exit(&self, span: &span::Id) {
- self.subscriber.exit(span);
+ self.subscriber().exit(span);
}
/// Notifies the subscriber that a [span ID] has been cloned.
@@ -557,13 +706,13 @@ impl Dispatch {
/// This calls the [`clone_span`] function on the `Subscriber` that this
/// `Dispatch` forwards to.
///
- /// [span ID]: ../span/struct.Id.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`clone_span`]: ../subscriber/trait.Subscriber.html#method.clone_span
- /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
+ /// [span ID]: super::span::Id
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`clone_span`]: super::subscriber::Subscriber::clone_span
+ /// [`new_span`]: super::subscriber::Subscriber::new_span
#[inline]
pub fn clone_span(&self, id: &span::Id) -> span::Id {
- self.subscriber.clone_span(&id)
+ self.subscriber().clone_span(&id)
}
/// Notifies the subscriber that a [span ID] has been dropped.
@@ -580,21 +729,22 @@ impl Dispatch {
///
⚠ ️Warning
///
///
- /// Deprecated: The try_close
- /// method is functionally identical, but returns true if the span is now closed.
+ ///
+ /// **Deprecated**: The [`try_close`] method is functionally identical, but returns `true` if the span is now closed.
/// It should be used instead of this method.
+ ///
///
///
- /// [span ID]: ../span/struct.Id.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`drop_span`]: ../subscriber/trait.Subscriber.html#method.drop_span
- /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
- /// [`try_close`]: #method.try_close
+ /// [span ID]: super::span::Id
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`drop_span`]: super::subscriber::Subscriber::drop_span
+ /// [`new_span`]: super::subscriber::Subscriber::new_span
+ /// [`try_close`]: Self::try_close
#[inline]
#[deprecated(since = "0.1.2", note = "use `Dispatch::try_close` instead")]
pub fn drop_span(&self, id: span::Id) {
#[allow(deprecated)]
- self.subscriber.drop_span(id);
+ self.subscriber().drop_span(id);
}
/// Notifies the subscriber that a [span ID] has been dropped, and returns
@@ -608,13 +758,13 @@ impl Dispatch {
/// This calls the [`try_close`] function on the [`Subscriber`] that this
/// `Dispatch` forwards to.
///
- /// [span ID]: ../span/struct.Id.html
- /// [`Subscriber`]: ../subscriber/trait.Subscriber.html
- /// [`try_close`]: ../subscriber/trait.Subscriber.html#method.try_close
- /// [`new_span`]: ../subscriber/trait.Subscriber.html#method.new_span
+ /// [span ID]: super::span::Id
+ /// [`Subscriber`]: super::subscriber::Subscriber
+ /// [`try_close`]: super::subscriber::Subscriber::try_close
+ /// [`new_span`]: super::subscriber::Subscriber::new_span
#[inline]
pub fn try_close(&self, id: span::Id) -> bool {
- self.subscriber.try_close(id)
+ self.subscriber().try_close(id)
}
/// Returns a type representing this subscriber's view of the current span.
@@ -622,24 +772,24 @@ impl Dispatch {
/// This calls the [`current`] function on the `Subscriber` that this
/// `Dispatch` forwards to.
///
- /// [`current`]: ../subscriber/trait.Subscriber.html#method.current
+ /// [`current`]: super::subscriber::Subscriber::current_span
#[inline]
pub fn current_span(&self) -> span::Current {
- self.subscriber.current_span()
+ self.subscriber().current_span()
}
/// Returns `true` if this `Dispatch` forwards to a `Subscriber` of type
/// `T`.
#[inline]
pub fn is(&self) -> bool {
- Subscriber::is::(&*self.subscriber)
+ Subscriber::is::(&*self.subscriber())
}
/// Returns some reference to the `Subscriber` this `Dispatch` forwards to
/// if it is of type `T`, or `None` if it isn't.
#[inline]
pub fn downcast_ref(&self) -> Option<&T> {
- Subscriber::downcast_ref(&*self.subscriber)
+ Subscriber::downcast_ref(&*self.subscriber())
}
}
@@ -656,6 +806,7 @@ impl fmt::Debug for Dispatch {
}
}
+#[cfg(feature = "std")]
impl From for Dispatch
where
S: Subscriber + Send + Sync + 'static,
@@ -692,16 +843,17 @@ impl Subscriber for NoSubscriber {
fn exit(&self, _span: &span::Id) {}
}
+#[cfg(feature = "std")]
impl Registrar {
- pub(crate) fn try_register(
- &self,
- metadata: &'static Metadata<'static>,
- ) -> Option {
- self.0.upgrade().map(|s| s.register_callsite(metadata))
- }
-
pub(crate) fn upgrade(&self) -> Option {
- self.0.upgrade().map(|subscriber| Dispatch { subscriber })
+ match self.0 {
+ Kind::Global(s) => Some(Dispatch {
+ subscriber: Kind::Global(s),
+ }),
+ Kind::Scoped(ref s) => s.upgrade().map(|s| Dispatch {
+ subscriber: Kind::Scoped(s),
+ }),
+ }
}
}
@@ -723,6 +875,7 @@ impl State {
})
.ok();
EXISTS.store(true, Ordering::Release);
+ SCOPED_COUNT.fetch_add(1, Ordering::Release);
DefaultGuard(prior)
}
@@ -745,10 +898,8 @@ impl<'a> Entered<'a> {
let mut default = self.0.default.borrow_mut();
if default.is::() {
- if let Some(global) = get_global() {
- // don't redo this call on the next check
- *default = global.clone();
- }
+ // don't redo this call on the next check
+ *default = get_global().clone();
}
default
@@ -769,6 +920,7 @@ impl<'a> Drop for Entered<'a> {
impl Drop for DefaultGuard {
#[inline]
fn drop(&mut self) {
+ SCOPED_COUNT.fetch_sub(1, Ordering::Release);
if let Some(dispatch) = self.0.take() {
// Replace the dispatcher and then drop the old one outside
// of the thread-local context. Dropping the dispatch may
@@ -783,9 +935,8 @@ impl Drop for DefaultGuard {
#[cfg(test)]
mod test {
+
use super::*;
- #[cfg(feature = "std")]
- use crate::stdlib::sync::atomic::{AtomicUsize, Ordering};
use crate::{
callsite::Callsite,
metadata::{Kind, Level, Metadata},
@@ -794,13 +945,13 @@ mod test {
#[test]
fn dispatch_is() {
- let dispatcher = Dispatch::new(NoSubscriber);
+ let dispatcher = Dispatch::from_static(&NO_SUBSCRIBER);
assert!(dispatcher.is::());
}
#[test]
fn dispatch_downcasts() {
- let dispatcher = Dispatch::new(NoSubscriber);
+ let dispatcher = Dispatch::from_static(&NO_SUBSCRIBER);
assert!(dispatcher.downcast_ref::().is_some());
}
diff --git a/tracing-core/src/event.rs b/tracing-core/src/event.rs
index 1c4b7a7936..b823fa8722 100644
--- a/tracing-core/src/event.rs
+++ b/tracing-core/src/event.rs
@@ -29,7 +29,6 @@ pub struct Event<'a> {
impl<'a> Event<'a> {
/// Constructs a new `Event` with the specified metadata and set of values,
/// and observes it with the current subscriber.
- #[inline]
pub fn dispatch(metadata: &'static Metadata<'static>, fields: &'a field::ValueSet<'_>) {
let event = Event::new(metadata, fields);
crate::dispatcher::get_default(|current| {
@@ -69,7 +68,6 @@ impl<'a> Event<'a> {
/// Constructs a new `Event` with the specified metadata and set of values,
/// and observes it with the current subscriber and an explicit parent.
- #[inline]
pub fn child_of(
parent: impl Into