From ce3c25720fff1f8b5368cff2534560cfa3855b23 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Mon, 24 Mar 2025 19:52:48 +0100 Subject: [PATCH 01/24] chore: fix Rust 1.85.0 lints and errors (#3240) We had some broken link formatting in the `tracing-journald` docs which clippy picked up (the text looked like a link definition, but wasn't meant to be). The incorrect links have now been corrected. They have to link to the `tracing-core` crate because `tracing-journald` doesn't depend on `tracing` directly. Fixes for a broken link in the `tracing-subscriber` main page and correcting the link to `Collect` from `tracing-log` (which also doesn't depend on `tracing` directly) were also included. --- tracing-journald/src/lib.rs | 16 +++++++++++----- tracing-subscriber/src/lib.rs | 1 + 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 3f0e561284..eef4837d6d 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -515,11 +515,17 @@ pub struct PriorityMappings { impl PriorityMappings { /// Returns the default priority mappings: /// - /// - [`tracing::Level::ERROR`]: [`Priority::Error`] (3) - /// - [`tracing::Level::WARN`]: [`Priority::Warning`] (4) - /// - [`tracing::Level::INFO`]: [`Priority::Notice`] (5) - /// - [`tracing::Level::DEBUG`]: [`Priority::Informational`] (6) - /// - [`tracing::Level::TRACE`]: [`Priority::Debug`] (7) + /// - [`tracing::Level::ERROR`][]: [`Priority::Error`] (3) + /// - [`tracing::Level::WARN`][]: [`Priority::Warning`] (4) + /// - [`tracing::Level::INFO`][]: [`Priority::Notice`] (5) + /// - [`tracing::Level::DEBUG`][]: [`Priority::Informational`] (6) + /// - [`tracing::Level::TRACE`][]: [`Priority::Debug`] (7) + /// + /// [`tracing::Level::ERROR`]: tracing_core::Level::ERROR + /// [`tracing::Level::WARN`]: tracing_core::Level::WARN + /// [`tracing::Level::INFO`]: tracing_core::Level::INFO + /// [`tracing::Level::DEBUG`]: tracing_core::Level::DEBUG + /// [`tracing::Level::TRACE`]: tracing_core::Level::TRACE pub fn new() -> PriorityMappings { Self { error: Priority::Error, diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 373f76310a..182e5d34b4 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -153,6 +153,7 @@ //! [`tracing`]: https://docs.rs/tracing/latest/tracing //! [`EnvFilter`]: filter::EnvFilter //! [`fmt`]: mod@fmt +//! [`tracing`]: https://crates.io/crates/tracing //! [`tracing-log`]: https://crates.io/crates/tracing-log //! [`smallvec`]: https://crates.io/crates/smallvec //! [`env_logger` crate]: https://crates.io/crates/env_logger From de5e1c00c66bc819916b6878bc7fc4def837b735 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Thu, 10 Apr 2025 18:42:47 +0200 Subject: [PATCH 02/24] chore: fix Rust 1.86.0 lints (#3253) There was a single case of the new [`clippy::double_ended_iterator_last`] lint which was triggered in `tracing-attributes` and needed to be fixed. There were also a number of cases of incorrectly indented lines, caught in [`clippy::doc_overindented_list_items`]. [`clippy::double_ended_iterator_last`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_ended_iterator_last [`clippy::doc_overindented_list_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_overindented_list_items --- tracing-attributes/src/expand.rs | 2 +- tracing-futures/src/lib.rs | 16 ++++++------- tracing-subscriber/src/filter/env/mod.rs | 14 +++++------ .../src/filter/layer_filters/mod.rs | 2 +- tracing-subscriber/src/fmt/fmt_layer.rs | 20 ++++++++-------- tracing-subscriber/src/fmt/mod.rs | 24 +++++++++---------- tracing/src/lib.rs | 2 +- 7 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index ec4bc28181..6481de758e 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -453,7 +453,7 @@ impl RecordType { if path .segments .iter() - .last() + .next_back() .map(|path_segment| { let ident = path_segment.ident.to_string(); Self::TYPES_FOR_VALUE.iter().any(|&t| t == ident) diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index 335e7aa5cc..9b92959dda 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -25,15 +25,15 @@ //! features with other crates in the asynchronous ecosystem: //! //! - `tokio`: Enables compatibility with the `tokio` crate, including -//! [`Instrument`] and [`WithSubscriber`] implementations for -//! `tokio::executor::Executor`, `tokio::runtime::Runtime`, and -//! `tokio::runtime::current_thread`. Enabled by default. +//! [`Instrument`] and [`WithSubscriber`] implementations for +//! `tokio::executor::Executor`, `tokio::runtime::Runtime`, and +//! `tokio::runtime::current_thread`. Enabled by default. //! - `tokio-executor`: Enables compatibility with the `tokio-executor` -//! crate, including [`Instrument`] and [`WithSubscriber`] -//! implementations for types implementing `tokio_executor::Executor`. -//! This is intended primarily for use in crates which depend on -//! `tokio-executor` rather than `tokio`; in general the `tokio` feature -//! should be used instead. +//! crate, including [`Instrument`] and [`WithSubscriber`] +//! implementations for types implementing `tokio_executor::Executor`. +//! This is intended primarily for use in crates which depend on +//! `tokio-executor` rather than `tokio`; in general the `tokio` feature +//! should be used instead. //! - `std-future`: Enables compatibility with `std::future::Future`. //! - `futures-01`: Enables compatibility with version 0.1.x of the [`futures`] //! crate. diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs index 813e32b447..314e065ca2 100644 --- a/tracing-subscriber/src/filter/env/mod.rs +++ b/tracing-subscriber/src/filter/env/mod.rs @@ -56,16 +56,16 @@ use tracing_core::{ /// Each component (`target`, `span`, `field`, `value`, and `level`) will be covered in turn. /// /// - `target` matches the event or span's target. In general, this is the module path and/or crate name. -/// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, -/// please refer to [`Metadata`]'s documentation. +/// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, +/// please refer to [`Metadata`]'s documentation. /// - `span` matches on the span's name. If a `span` directive is provided alongside a `target`, -/// the `span` directive will match on spans _within_ the `target`. +/// the `span` directive will match on spans _within_ the `target`. /// - `field` matches on [fields] within spans. Field names can also be supplied without a `value` -/// and will match on any [`Span`] or [`Event`] that has a field with that name. -/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. +/// and will match on any [`Span`] or [`Event`] that has a field with that name. +/// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. /// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool, -/// it will match _only_ on that value. Otherwise, this filter matches the -/// [`std::fmt::Debug`] output from the value. +/// it will match _only_ on that value. Otherwise, this filter matches the +/// [`std::fmt::Debug`] output from the value. /// - `level` sets a maximum verbosity level accepted by this directive. /// /// When a field value directive (`[{=}]=...`) matches a diff --git a/tracing-subscriber/src/filter/layer_filters/mod.rs b/tracing-subscriber/src/filter/layer_filters/mod.rs index f349d4ce6a..078051c783 100644 --- a/tracing-subscriber/src/filter/layer_filters/mod.rs +++ b/tracing-subscriber/src/filter/layer_filters/mod.rs @@ -135,7 +135,7 @@ impl FilterMap { /// 2. If all the bits are set, then every per-layer filter has decided it /// doesn't want to enable that span or event. In that case, the /// `Registry`'s `enabled` method will return `false`, so that -/// recording a span or event can be skipped entirely. +/// recording a span or event can be skipped entirely. #[derive(Debug)] pub(crate) struct FilterState { enabled: Cell, diff --git a/tracing-subscriber/src/fmt/fmt_layer.rs b/tracing-subscriber/src/fmt/fmt_layer.rs index 15351767e0..25c0f3c3a1 100644 --- a/tracing-subscriber/src/fmt/fmt_layer.rs +++ b/tracing-subscriber/src/fmt/fmt_layer.rs @@ -442,22 +442,22 @@ where /// The following options are available: /// /// - `FmtSpan::NONE`: No events will be synthesized when spans are - /// created, entered, exited, or closed. Data from spans will still be - /// included as the context for formatted events. This is the default. + /// created, entered, exited, or closed. Data from spans will still be + /// included as the context for formatted events. This is the default. /// - `FmtSpan::NEW`: An event will be synthesized when spans are created. /// - `FmtSpan::ENTER`: An event will be synthesized when spans are entered. /// - `FmtSpan::EXIT`: An event will be synthesized when spans are exited. /// - `FmtSpan::CLOSE`: An event will be synthesized when a span closes. If - /// [timestamps are enabled][time] for this formatter, the generated - /// event will contain fields with the span's _busy time_ (the total - /// time for which it was entered) and _idle time_ (the total time that - /// the span existed but was not entered). + /// [timestamps are enabled][time] for this formatter, the generated + /// event will contain fields with the span's _busy time_ (the total + /// time for which it was entered) and _idle time_ (the total time that + /// the span existed but was not entered). /// - `FmtSpan::ACTIVE`: Events will be synthesized when spans are entered - /// or exited. + /// or exited. /// - `FmtSpan::FULL`: Events will be synthesized whenever a span is - /// created, entered, exited, or closed. If timestamps are enabled, the - /// close event will contain the span's busy and idle time, as - /// described above. + /// created, entered, exited, or closed. If timestamps are enabled, the + /// close event will contain the span's busy and idle time, as + /// described above. /// /// The options can be enabled in any combination. For instance, the following /// will synthesize events whenever spans are created and closed: diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 6a80c0e346..ef04bd6a6b 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -36,9 +36,9 @@ //! //! For example: //! - Setting `RUST_LOG=debug` enables all `Span`s and `Event`s -//! set to the log level `DEBUG` or higher +//! set to the log level `DEBUG` or higher //! - Setting `RUST_LOG=my_crate=trace` enables `Span`s and `Event`s -//! in `my_crate` at all log levels +//! in `my_crate` at all log levels //! //! **Note**: This should **not** be called by libraries. Libraries should use //! [`tracing`] to publish `tracing` `Event`s. @@ -570,22 +570,22 @@ where /// The following options are available: /// /// - `FmtSpan::NONE`: No events will be synthesized when spans are - /// created, entered, exited, or closed. Data from spans will still be - /// included as the context for formatted events. This is the default. + /// created, entered, exited, or closed. Data from spans will still be + /// included as the context for formatted events. This is the default. /// - `FmtSpan::NEW`: An event will be synthesized when spans are created. /// - `FmtSpan::ENTER`: An event will be synthesized when spans are entered. /// - `FmtSpan::EXIT`: An event will be synthesized when spans are exited. /// - `FmtSpan::CLOSE`: An event will be synthesized when a span closes. If - /// [timestamps are enabled][time] for this formatter, the generated - /// event will contain fields with the span's _busy time_ (the total - /// time for which it was entered) and _idle time_ (the total time that - /// the span existed but was not entered). + /// [timestamps are enabled][time] for this formatter, the generated + /// event will contain fields with the span's _busy time_ (the total + /// time for which it was entered) and _idle time_ (the total time that + /// the span existed but was not entered). /// - `FmtSpan::ACTIVE`: An event will be synthesized when spans are entered - /// or exited. + /// or exited. /// - `FmtSpan::FULL`: Events will be synthesized whenever a span is - /// created, entered, exited, or closed. If timestamps are enabled, the - /// close event will contain the span's busy and idle time, as - /// described above. + /// created, entered, exited, or closed. If timestamps are enabled, the + /// close event will contain the span's busy and idle time, as + /// described above. /// /// The options can be enabled in any combination. For instance, the following /// will synthesize events whenever spans are created and closed: diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 8b6c7f0b95..9b8fdde16a 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -710,7 +710,7 @@ //! `tracing-subscriber`'s `FmtSubscriber`, you don't need to depend on //! `tracing-log` directly. //! - [`tracing-appender`] provides utilities for outputting tracing data, -//! including a file appender and non blocking writer. +//! including a file appender and non blocking writer. //! //! Additionally, there are also several third-party crates which are not //! maintained by the `tokio` project. These include: From a9096c4d3355f236892e40e7656deea158acbb39 Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Sat, 30 Nov 2024 11:52:38 +0100 Subject: [PATCH 03/24] Update README.md docs link (#3145) Fixes: #3140 Co-authored-by: Hayden Stainsby --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d98bcc624f..3c9180a8a1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ [docs-badge]: https://docs.rs/tracing/badge.svg [docs-url]: https://docs.rs/tracing [docs-master-badge]: https://img.shields.io/badge/docs-master-blue -[docs-master-url]: https://tracing-rs.netlify.com +[docs-master-url]: https://tracing.rs [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg [mit-url]: LICENSE [actions-badge]: https://github.com/tokio-rs/tracing/workflows/CI/badge.svg From 5995024b6d1e9bfd214462dde7498bbc288aed3e Mon Sep 17 00:00:00 2001 From: John Vandenberg Date: Sun, 1 Dec 2024 02:49:46 +0800 Subject: [PATCH 04/24] fix: Update incorrect tracing-futures feature docs (#2802) Co-authored-by: David Barsky Co-authored-by: Hayden Stainsby --- tracing-futures/src/lib.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index 9b92959dda..8425c8dd69 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -24,10 +24,11 @@ //! This crate provides a number of feature flags that enable compatibility //! features with other crates in the asynchronous ecosystem: //! -//! - `tokio`: Enables compatibility with the `tokio` crate, including +//! - `tokio`: Enables compatibility with the `tokio` 0.1 crate, including //! [`Instrument`] and [`WithSubscriber`] implementations for //! `tokio::executor::Executor`, `tokio::runtime::Runtime`, and -//! `tokio::runtime::current_thread`. Enabled by default. +//! `tokio::runtime::current_thread`. This is not needed for compatibility +//! with `tokio` v1. //! - `tokio-executor`: Enables compatibility with the `tokio-executor` //! crate, including [`Instrument`] and [`WithSubscriber`] //! implementations for types implementing `tokio_executor::Executor`. @@ -39,8 +40,6 @@ //! crate. //! - `futures-03`: Enables compatibility with version 0.3.x of the `futures` //! crate's `Spawn` and `LocalSpawn` traits. -//! - `tokio-alpha`: Enables compatibility with `tokio` 0.2's alpha releases, -//! including the `tokio` 0.2 `Executor` and `TypedExecutor` traits. //! - `std`: Depend on the Rust standard library. //! //! `no_std` users may disable this feature with `default-features = false`: @@ -50,8 +49,9 @@ //! tracing-futures = { version = "0.2.5", default-features = false } //! ``` //! -//! The `tokio`, `std-future` and `std` features are enabled by default. +//! The `std-future` and `std` features are enabled by default. //! +//! [`tracing`]: https://crates.io/crates/tracing //! [span]: tracing::span! //! [`Subscriber`]: tracing::subscriber //! [`futures`]: https://crates.io/crates/futures @@ -72,6 +72,7 @@ //! #![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/" )] #![warn( From 6a64d83a2a41cf7dea62ebb4c5e310c60c8cb2cf Mon Sep 17 00:00:00 2001 From: Rustin Date: Sun, 1 Dec 2024 18:04:50 +0800 Subject: [PATCH 05/24] chore: remove outdated release instructions (#3154) The outdated release instructions have been removed. Follow up of #2384. Signed-off-by: Rustin170506 --- CONTRIBUTING.md | 5 ++--- tracing-attributes/Cargo.toml | 5 +---- tracing-core/Cargo.toml | 4 +--- tracing-error/Cargo.toml | 5 +---- tracing/Cargo.toml | 5 +---- 5 files changed, 6 insertions(+), 18 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9254458ca..0f510d5379 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -214,7 +214,7 @@ be viewed by clicking the `details` link on the ### Building Documentation Tracing's documentation uses nightly-only RustDoc features and lints, like -`doc(cfg)` and `broken_intra_doc_lints`. These features are enabled by +`doc(cfg)` and `broken_intra_doc_lints`. These features are enabled by passing `--cfg docsrs` to RustDoc. Therefore, in order to build Tracing's documentation the same way it would be built by docs.rs, it's necessary to use the following command: @@ -430,8 +430,7 @@ When releasing a new version of a crate, follow these steps: 2. **Update Cargo metadata.** After releasing any path dependencies, update the `version` field in `Cargo.toml` to the new version, and the `documentation` field to the docs.rs URL of the new version. -3. **Update other documentation links.** Update the `#![doc(html_root_url)]` - attribute in the crate's `lib.rs` and the "Documentation" link in the crate's +3. **Update the "Documentation" link in the crate's `README.md` to point to the docs.rs URL of the new version. 4. **Update the changelog for the crate.** Each crate in the Tokio repository has its own `CHANGELOG.md` in that crate's subdirectory. Any changes to that diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 4acef99498..1bf85bd68d 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -2,10 +2,7 @@ name = "tracing-attributes" # When releasing to crates.io: # - Remove path dependencies -# - Update html_root_url. -# - Update doc url -# - Cargo.toml -# - README.md +# - Update doc url in README.md. # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. version = "0.1.28" diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index b99a70bb0c..5f7e41f973 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -3,9 +3,7 @@ name = "tracing-core" # When releasing to crates.io: # - Remove path dependencies # - Update html_root_url. -# - Update doc url -# - Cargo.toml -# - README.md +# - Update doc url in README.md. # - Update CHANGELOG.md. # - Create "v0.1.x" git tag. version = "0.1.33" diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml index 46cead89c3..c4710c6145 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -2,10 +2,7 @@ name = "tracing-error" # When releasing to crates.io: # - Remove path dependencies -# - Update html_root_url. -# - Update doc url -# - Cargo.toml -# - README.md +# - Update doc url in README.md. # - Update CHANGELOG.md. # - Create "v0.2.x" git tag version = "0.2.1" diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 9e9e703ff3..4237eb3e51 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -2,10 +2,7 @@ name = "tracing" # When releasing to crates.io: # - Remove path dependencies -# - Update html_root_url. -# - Update doc url -# - Cargo.toml -# - README.md +# - Update doc url in README.md. # - Update CHANGELOG.md. # - Create "v0.1.x" git tag version = "0.1.41" From 1d5431be97973567a0e4daee0216c75eebd1a679 Mon Sep 17 00:00:00 2001 From: ilsubyeega Date: Sun, 1 Dec 2024 20:28:17 +0900 Subject: [PATCH 06/24] chore: corrected typos in the example readme (#2718) ## Motivation I was checking the example from `/examples/` and saw that some of the module names were incorrect in README, so I fixed them. ## Solution Changed the incorrect name to the correct name. --- examples/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/README.md b/examples/README.md index 496fd5f75c..ba4060c2e3 100644 --- a/examples/README.md +++ b/examples/README.md @@ -53,11 +53,11 @@ This directory contains a collection of examples that demonstrate the use of the + `futures-proxy-server`: Demonstrates the use of `tracing-futures` by implementing a simple proxy server, based on [this example][tokio-proxy] from `tokio`. - + `async_fn`: Demonstrates how asynchronous functions can be + + `async-fn`: Demonstrates how asynchronous functions can be instrumented. + `echo`: Demonstrates a `tracing`-instrumented variant of Tokio's `echo` example. - **tracing-flame**: - + `infero-flame`: Demonstrates the use of `tracing-flame` to generate a flamegraph + + `inferno-flame`: Demonstrates the use of `tracing-flame` to generate a flamegraph from spans. - **tracing-tower**: + `tower-client`: Demonstrates the use of `tracing-tower` to instrument a From 12823c0ed05c24ab69f100581dd33520b9895bc6 Mon Sep 17 00:00:00 2001 From: porkbrain Date: Sun, 1 Dec 2024 13:29:50 +0100 Subject: [PATCH 07/24] docs: add link to a stream recording by @jonhoo (#3106) @jonhoo recorded a great resource about the crate's inner workings and included practical suggestions about patterns to follow when annotating one's code. I added the link to the YouTube video under the "Talks" header as that seemed appropriate enough. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3c9180a8a1..f7df0e4c25 100644 --- a/README.md +++ b/README.md @@ -475,6 +475,7 @@ Tracing. * [RustConf 2019 talk][rust-conf-2019-08-video] and [slides][rust-conf-2019-08-slides], August 2019 * [Are we observable yet? @ RustyDays talk][rusty-days-2020-08-video] and [slides][rusty-days-2020-08-slides], August 2020 * [Crabs with instruments!][tremorcon-2021-09], September 2021 +* [Decrusting the tracing crate by Jon Gjengset][decrusting-tracing-2024-02], February 2024 [bay-rust-2019-03]: https://www.youtube.com/watch?v=j_kXRg3zlec [rust-conf-2019-08-video]: https://www.youtube.com/watch?v=JjItsfqFIdo @@ -485,6 +486,7 @@ Tracing. [custom-logging-part-1]: https://burgers.io/custom-logging-in-rust-using-tracing [custom-logging-part-2]: https://burgers.io/custom-logging-in-rust-using-tracing-part-2 [tremorcon-2021-09]: https://www.youtube.com/watch?v=ZC7fyqshun8 +[decrusting-tracing-2024-02]: https://www.youtube.com/watch?v=21rtHinFA40 Help us expand this list! If you've written or spoken about Tracing, or know of resources that aren't listed, please open a pull request adding them. From 89ea4106aff5e415d97f8448d015fc996cb48525 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Mon, 2 Dec 2024 15:57:26 +0900 Subject: [PATCH 08/24] subscriber: skip padding when skipping `log.*` fields in `DefaultVisitor` (#2980) ## Motivation The current behaviour of `DefaultVisitor` is that it will write padding even if it is going to skip writing a value, which results in extraneous padding being added when values are skipped by the `tracing-log` integration. ## Solution With this change, `DefaultVisitor` will only insert padding if it is actually going to write a value. Closes: #2979 --- tracing-subscriber/src/fmt/format/mod.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index f19cdbccd0..2373fb9b46 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -1274,12 +1274,20 @@ impl field::Visit for DefaultVisitor<'_> { return; } + let name = field.name(); + + // Skip fields that are actually log metadata that have already been handled + #[cfg(feature = "tracing-log")] + if name.starts_with("log.") { + debug_assert_eq!(self.result, Ok(())); // no need to update self.result + return; + } + + // emit separating spaces if needed self.maybe_pad(); - self.result = match field.name() { + + self.result = match name { "message" => write!(self.writer, "{:?}", value), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => Ok(()), name if name.starts_with("r#") => write!( self.writer, "{}{}{:?}", From c47aeb567dbbb3157993a0058b98f165545d657d Mon Sep 17 00:00:00 2001 From: Matilda Smeds Date: Thu, 27 Mar 2025 12:16:29 +0100 Subject: [PATCH 09/24] tracing: add `record_all!` macro for recording multiple values in one call (#3227) ## Motivation Currently, Span.record_all() is part of the public API and accepts ValueSet as a parameter. However, constructing a ValueSet is both verbose and undocumented, making it not so practical. ## Solution To make recording multiple values easier, we introduce a new macro: record_all!, which wraps the Span.record_all() function. As we don't intend anyone to call Span.record_all() directly, we hide it from the documentation. We reference the new macro from Span.record() doc comment instead. The new record_all! macro supports optional formatting sigils % and ?, ensuring a consistent DevEx with the other value-recording macros. Co-authored-by: Hayden Stainsby --- tracing/src/macros.rs | 30 +++++++++++++++++++++++++++ tracing/src/span.rs | 6 ++++++ tracing/tests/span.rs | 47 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 156334a356..b5e8f37cef 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -130,6 +130,36 @@ macro_rules! span { }; } +/// Records multiple values on a span in a single call. As with recording +/// individual values, all fields must be declared when the span is created. +/// +/// This macro supports two optional sigils: +/// - `%` uses the Display implementation. +/// - `?` uses the Debug implementation. +/// +/// For more details, see the [top-level documentation][lib]. +/// +/// [lib]: tracing/#recording-fields +/// +/// # Examples +/// +/// ``` +/// # use tracing::{field, info_span, record_all}; +/// let span = info_span!("my span", field1 = field::Empty, field2 = field::Empty, field3 = field::Empty).entered(); +/// record_all!(span, field1 = ?"1", field2 = %"2", field3 = 3); +/// ``` +#[macro_export] +macro_rules! record_all { + ($span:expr, $($fields:tt)*) => { + if let Some(meta) = $span.metadata() { + $span.record_all(&$crate::valueset!( + meta.fields(), + $($fields)* + )); + } + }; +} + /// Constructs a span at the trace level. /// /// [Fields] and [attributes] are set using the same syntax as the [`span!`] diff --git a/tracing/src/span.rs b/tracing/src/span.rs index 0c8e74f7ad..46e9cf82c0 100644 --- a/tracing/src/span.rs +++ b/tracing/src/span.rs @@ -1182,6 +1182,11 @@ impl Span { /// span.record("parting", "you will be remembered"); /// ``` /// + ///
+ ///
+    /// **Note**: To record several values in just one call, see the [`record_all!`](crate::record_all!) macro.
+    /// 
+ /// /// [`field::Empty`]: super::field::Empty /// [`Metadata`]: super::Metadata pub fn record( @@ -1203,6 +1208,7 @@ impl Span { } /// Records all the fields in the provided `ValueSet`. + #[doc(hidden)] pub fn record_all(&self, values: &field::ValueSet<'_>) -> &Self { let record = Record::new(values); if let Some(ref inner) = self.inner { diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index 9f4e5c45b8..b9a1d148de 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -7,7 +7,8 @@ use std::thread; use tracing::{ error_span, - field::{debug, display}, + field::{debug, display, Empty}, + record_all, subscriber::with_default, Level, Span, }; @@ -612,6 +613,50 @@ fn record_new_values_for_fields() { handle.assert_finished(); } +/// Tests record_all! macro, which is a wrapper for Span.record_all(). +/// Placed here instead of tests/macros.rs, because it uses tracing_mock, which +/// requires std lib. Other macro tests exclude std lib to verify the macros do +/// not dependend on it. +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn record_all_macro_records_new_values_for_fields() { + let (subscriber, handle) = subscriber::mock() + .new_span( + expect::span() + .named("foo") + .with_fields(expect::field("bar")), + ) + .record( + expect::span().named("foo"), + expect::field("bar") + .with_value(&5) + .and(expect::field("baz").with_value(&"BAZ")) + .and(expect::field("qux").with_value(&display("qux"))) + .and(expect::field("quux").with_value(&debug("QuuX"))) + .only(), + ) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) + .drop_span(expect::span().named("foo")) + .only() + .run_with_handle(); + + with_default(subscriber, || { + let span = tracing::span!( + Level::TRACE, + "foo", + bar = 1, + baz = 2, + qux = Empty, + quux = Empty + ); + record_all!(span, bar = 5, baz = "BAZ", qux = %"qux", quux = ?"QuuX"); + span.in_scope(|| {}) + }); + + handle.assert_finished(); +} + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn new_span_with_target_and_log_level() { From 6017d2c81aa65e861dbf97c531a97501f863797c Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Wed, 30 Apr 2025 17:24:16 +0200 Subject: [PATCH 10/24] subscriber: increase `EnvFilter` test coverage (#3262) --- tracing-subscriber/src/filter/env/builder.rs | 25 +++ tracing-subscriber/tests/env_filter/main.rs | 207 +++++++++++++++++++ 2 files changed, 232 insertions(+) diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs index ce93687527..2c11edff2c 100644 --- a/tracing-subscriber/src/filter/env/builder.rs +++ b/tracing-subscriber/src/filter/env/builder.rs @@ -133,6 +133,11 @@ impl Builder { /// Returns a new [`EnvFilter`] from the directives in the given string, /// *ignoring* any that are invalid. + /// + /// If `parse_lossy` is called with an empty string, then the + /// [default directive] is used instead. + /// + /// [default directive]: Self::with_default_directive pub fn parse_lossy>(&self, dirs: S) -> EnvFilter { let directives = dirs .as_ref() @@ -150,6 +155,11 @@ impl Builder { /// Returns a new [`EnvFilter`] from the directives in the given string, /// or an error if any are invalid. + /// + /// If `parse` is called with an empty string, then the [default directive] + /// is used instead. + /// + /// [default directive]: Self::with_default_directive pub fn parse>(&self, dirs: S) -> Result { let dirs = dirs.as_ref(); if dirs.is_empty() { @@ -165,6 +175,11 @@ impl Builder { /// Returns a new [`EnvFilter`] from the directives in the configured /// environment variable, ignoring any directives that are invalid. + /// + /// If the environment variable is empty, then the [default directive] + /// is used instead. + /// + /// [default directive]: Self::with_default_directive pub fn from_env_lossy(&self) -> EnvFilter { let var = env::var(self.env_var_name()).unwrap_or_default(); self.parse_lossy(var) @@ -174,6 +189,11 @@ impl Builder { /// environment variable. If the environment variable is unset, no directive is added. /// /// An error is returned if the environment contains invalid directives. + /// + /// If the environment variable is empty, then the [default directive] + /// is used instead. + /// + /// [default directive]: Self::with_default_directive pub fn from_env(&self) -> Result { let var = env::var(self.env_var_name()).unwrap_or_default(); self.parse(var).map_err(Into::into) @@ -182,6 +202,11 @@ impl Builder { /// Returns a new [`EnvFilter`] from the directives in the configured /// environment variable, or an error if the environment variable is not set /// or contains invalid directives. + /// + /// If the environment variable is empty, then the [default directive] + /// is used instead. + /// + /// [default directive]: Self::with_default_directive pub fn try_from_env(&self) -> Result { let var = env::var(self.env_var_name())?; self.parse(var).map_err(Into::into) diff --git a/tracing-subscriber/tests/env_filter/main.rs b/tracing-subscriber/tests/env_filter/main.rs index 35919004a1..2f04a1652d 100644 --- a/tracing-subscriber/tests/env_filter/main.rs +++ b/tracing-subscriber/tests/env_filter/main.rs @@ -236,6 +236,213 @@ fn method_name_resolution() { filter.max_level_hint(); } +#[test] +fn parse_invalid_string() { + assert!(EnvFilter::builder().parse(",!").is_err()); +} + +#[test] +fn parse_empty_string_no_default_directive() { + let filter = EnvFilter::builder().parse("").expect("filter should parse"); + let (subscriber, finished) = subscriber::mock().only().run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::trace!("this should be disabled"); + tracing::debug!("this should be disabled"); + tracing::info!("this should be disabled"); + tracing::warn!("this should be disabled"); + tracing::error!("this should be disabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn parse_empty_string_with_default_directive() { + let filter = EnvFilter::builder() + .with_default_directive(LevelFilter::INFO.into()) + .parse("") + .expect("filter should parse"); + let (subscriber, finished) = subscriber::mock() + .event(expect::event().at_level(Level::INFO)) + .event(expect::event().at_level(Level::WARN)) + .event(expect::event().at_level(Level::ERROR)) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::trace!("this should be disabled"); + tracing::debug!("this should be disabled"); + tracing::info!("this shouldn't be disabled"); + tracing::warn!("this shouldn't be disabled"); + tracing::error!("this shouldn't be disabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn new_invalid_string() { + let filter = EnvFilter::new(",!"); + let (subscriber, finished) = subscriber::mock() + .event(expect::event().at_level(Level::ERROR)) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::trace!("this should be disabled"); + tracing::debug!("this should be disabled"); + tracing::info!("this should be disabled"); + tracing::warn!("this should be disabled"); + tracing::error!("this shouldn't be disabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn new_empty_string() { + let filter = EnvFilter::new(""); + let (subscriber, finished) = subscriber::mock() + .event(expect::event().at_level(Level::ERROR)) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::trace!("this should be disabled"); + tracing::debug!("this should be disabled"); + tracing::info!("this should be disabled"); + tracing::warn!("this should be disabled"); + tracing::error!("this shouldn't be disabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn more_specific_static_filter_more_verbose() { + let filter = EnvFilter::new("info,hello=debug"); + let (subscriber, finished) = subscriber::mock() + .event(expect::event().at_level(Level::INFO)) + .event(expect::event().at_level(Level::DEBUG).with_target("hello")) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::info!("should be enabled"); + tracing::debug!("should be disabled"); + tracing::debug!(target: "hello", "should be enabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn more_specific_static_filter_less_verbose() { + let filter = EnvFilter::new("info,hello=warn"); + let (subscriber, finished) = subscriber::mock() + .event(expect::event().at_level(Level::INFO)) + .event( + expect::event() + .at_level(Level::WARN) + .with_target("env_filter"), + ) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::info!("should be enabled"); + tracing::warn!("should be enabled"); + tracing::info!(target: "hello", "should be disabled"); + }); + + finished.assert_finished(); +} + +#[test] +fn more_specific_dynamic_filter_more_verbose() { + let filter = EnvFilter::new("info,[{hello=4}]=debug"); + let (subscriber, finished) = subscriber::mock() + .new_span(expect::span().at_level(Level::INFO)) + .drop_span("enabled info") + .new_span( + expect::span() + .at_level(Level::DEBUG) + .with_fields(expect::field("hello").with_value(&4_u64)), + ) + .drop_span("enabled debug") + .event(expect::event().with_fields(expect::msg("marker"))) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::info_span!("enabled info"); + tracing::debug_span!("disabled debug"); + tracing::debug_span!("enabled debug", hello = &4_u64); + + // .only() doesn't work when we don't enter/exit spans + tracing::info!("marker"); + }); + + finished.assert_finished(); +} + +/// This is a negative test. This functionality should work, but doesn't. +/// +/// If an improvement to `EnvFilter` fixes this test, then the `#[should_panic]` +/// can be removed and the test kept as it is. If the test requires some sort of +/// modification, then care should be taken. +/// +/// Fixing this test would resolve https://github.com/tokio-rs/tracing/issues/1388 +/// (and probably a few more issues as well). +#[test] +#[should_panic( + expected = "[more_specific_dynamic_filter_less_verbose] expected a new span \ + at level `Level(Warn)`,\n[more_specific_dynamic_filter_less_verbose] but \ + got one at level `Level(Info)` instead." +)] +fn more_specific_dynamic_filter_less_verbose() { + let filter = EnvFilter::new("info,[{hello=4}]=warn"); + let (subscriber, finished) = subscriber::mock() + .new_span(expect::span().at_level(Level::INFO)) + .drop_span("enabled info") + .new_span( + expect::span() + .at_level(Level::WARN) + .with_fields(expect::field("hello").with_value(&100_u64)), + ) + .drop_span("enabled hello=100 warn") + .new_span( + expect::span() + .at_level(Level::WARN) + .with_fields(expect::field("hello").with_value(&4_u64)), + ) + .drop_span("enabled hello=4 warn") + .event(expect::event().with_fields(expect::msg("marker"))) + .only() + .run_with_handle(); + let layer = subscriber.with(filter); + + with_default(layer, || { + tracing::info_span!("enabled info"); + tracing::warn_span!("enabled hello=100 warn", hello = &100_u64); + tracing::info_span!("disabled hello=4 info", hello = &4_u64); + tracing::warn_span!("enabled hello=4 warn", hello = &4_u64); + + // .only() doesn't work when we don't enter/exit spans + tracing::info!("marker"); + }); + + finished.assert_finished(); +} + // contains the same tests as the first half of this file // but using EnvFilter as a `Filter`, not as a `Layer` mod per_layer_filter { From c382f05d2b81b0f0e838c0857e7941beab882fb6 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Thu, 8 May 2025 11:51:12 +0200 Subject: [PATCH 11/24] subscriber: use state machine to parse `EnvFilter` directives (#3243) There is a report in #3174 that even in release mode, building the regex used to parse `EnvFilter` directives can take a relatively large amount of time (600us). This change replaces the `regex` based parsing of the directives with a state machine implementation that is faster and also easier to reason about. Fixes: #3174 --- tracing-subscriber/Cargo.toml | 5 +- .../src/filter/env/directive.rs | 201 ++++++++++-------- 2 files changed, 114 insertions(+), 92 deletions(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 5dc019f8cb..409d679e29 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -27,7 +27,7 @@ rust-version = "1.63.0" default = ["smallvec", "fmt", "ansi", "tracing-log", "std"] alloc = [] std = ["alloc", "tracing-core/std"] -env-filter = ["matchers", "regex", "once_cell", "tracing", "std", "thread_local"] +env-filter = ["matchers", "once_cell", "tracing", "std", "thread_local"] fmt = ["registry", "std"] ansi = ["fmt", "nu-ansi-term"] registry = ["sharded-slab", "thread_local", "std"] @@ -37,6 +37,8 @@ valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracin # formatters. local-time = ["time/local-offset"] nu-ansi-term = ["dep:nu-ansi-term"] +# For backwards compatibility only +regex = [] [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.33", default-features = false } @@ -44,7 +46,6 @@ tracing-core = { path = "../tracing-core", version = "0.1.33", default-features # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1.41", default-features = false } matchers = { optional = true, version = "0.1.0" } -regex = { optional = true, version = "1.6.0", default-features = false, features = ["std", "unicode-case", "unicode-perl"] } smallvec = { optional = true, version = "1.9.0" } once_cell = { optional = true, version = "1.13.0" } diff --git a/tracing-subscriber/src/filter/env/directive.rs b/tracing-subscriber/src/filter/env/directive.rs index d095065f88..7c348fd4ca 100644 --- a/tracing-subscriber/src/filter/env/directive.rs +++ b/tracing-subscriber/src/filter/env/directive.rs @@ -4,8 +4,6 @@ use crate::filter::{ env::{field, FieldMap}, level::LevelFilter, }; -use once_cell::sync::Lazy; -use regex::Regex; use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr}; use tracing_core::{span, Level, Metadata}; @@ -120,99 +118,122 @@ impl Directive { } pub(super) fn parse(from: &str, regex: bool) -> Result { - static DIRECTIVE_RE: Lazy = Lazy::new(|| { - Regex::new( - r"(?x) - ^(?P(?i:trace|debug|info|warn|error|off|[0-5]))$ | - # ^^^. - # `note: we match log level names case-insensitively - ^ - (?: # target name or span name - (?P[\w:-]+)|(?P\[[^\]]*\]) - ){1,2} - (?: # level or nothing - =(?P(?i:trace|debug|info|warn|error|off|[0-5]))? - # ^^^. - # `note: we match log level names case-insensitively - )? - $ - ", - ) - .unwrap() - }); - static SPAN_PART_RE: Lazy = - Lazy::new(|| Regex::new(r"(?P[^\]\{]+)?(?:\{(?P[^\}]*)\})?").unwrap()); - static FIELD_FILTER_RE: Lazy = - // TODO(eliza): this doesn't _currently_ handle value matchers that include comma - // characters. We should fix that. - Lazy::new(|| { - Regex::new( - r"(?x) - ( - # field name - [[:word:]][[[:word:]]\.]* - # value part (optional) - (?:=[^,]+)? - ) - # trailing comma or EOS - (?:,\s?|$) - ", - ) - .unwrap() - }); - - let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?; + let mut cur = Self { + level: LevelFilter::TRACE, + target: None, + in_span: None, + fields: Vec::new(), + }; + + #[derive(Debug)] + enum ParseState { + Start, + LevelOrTarget { start: usize }, + Span { span_start: usize }, + Field { field_start: usize }, + Fields, + Target, + Level { level_start: usize }, + Complete, + } - if let Some(level) = caps - .name("global_level") - .and_then(|s| s.as_str().parse().ok()) - { - return Ok(Directive { - level, - ..Default::default() - }); + use ParseState::*; + let mut state = Start; + for (i, c) in from.trim().char_indices() { + state = match (state, c) { + (Start, '[') => Span { span_start: i + 1 }, + (Start, c) if !['-', ':', '_'].contains(&c) && !c.is_alphanumeric() => { + return Err(ParseError::new()) + } + (Start, _) => LevelOrTarget { start: i }, + (LevelOrTarget { start }, '=') => { + cur.target = Some(from[start..i].to_owned()); + Level { level_start: i + 1 } + } + (LevelOrTarget { start }, '[') => { + cur.target = Some(from[start..i].to_owned()); + Span { span_start: i + 1 } + } + (LevelOrTarget { start }, ',') => { + let (level, target) = match &from[start..] { + "" => (LevelFilter::TRACE, None), + level_or_target => match LevelFilter::from_str(level_or_target) { + Ok(level) => (level, None), + Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())), + }, + }; + + cur.level = level; + cur.target = target; + Complete + } + (state @ LevelOrTarget { .. }, _) => state, + (Target, '=') => Level { level_start: i + 1 }, + (Span { span_start }, ']') => { + cur.in_span = Some(from[span_start..i].to_owned()); + Target + } + (Span { span_start }, '{') => { + cur.in_span = match &from[span_start..i] { + "" => None, + _ => Some(from[span_start..i].to_owned()), + }; + Field { field_start: i + 1 } + } + (state @ Span { .. }, _) => state, + (Field { field_start }, '}') => { + cur.fields.push(match &from[field_start..i] { + "" => return Err(ParseError::new()), + field => field::Match::parse(field, regex)?, + }); + Fields + } + (Field { field_start }, ',') => { + cur.fields.push(match &from[field_start..i] { + "" => return Err(ParseError::new()), + field => field::Match::parse(field, regex)?, + }); + Field { field_start: i + 1 } + } + (state @ Field { .. }, _) => state, + (Fields, ']') => Target, + (Level { level_start }, ',') => { + cur.level = match &from[level_start..i] { + "" => LevelFilter::TRACE, + level => LevelFilter::from_str(level)?, + }; + Complete + } + (state @ Level { .. }, _) => state, + _ => return Err(ParseError::new()), + }; } - let target = caps.name("target").and_then(|c| { - let s = c.as_str(); - if s.parse::().is_ok() { - None - } else { - Some(s.to_owned()) + match state { + LevelOrTarget { start } => { + let (level, target) = match &from[start..] { + "" => (LevelFilter::TRACE, None), + level_or_target => match LevelFilter::from_str(level_or_target) { + Ok(level) => (level, None), + // Setting the target without the level enables every level for that target + Err(_) => (LevelFilter::TRACE, Some(level_or_target.to_owned())), + }, + }; + + cur.level = level; + cur.target = target; } - }); - - let (in_span, fields) = caps - .name("span") - .and_then(|cap| { - let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']'); - let caps = SPAN_PART_RE.captures(cap)?; - let span = caps.name("name").map(|c| c.as_str().to_owned()); - let fields = caps - .name("fields") - .map(|c| { - FIELD_FILTER_RE - .find_iter(c.as_str()) - .map(|c| field::Match::parse(c.as_str(), regex)) - .collect::, _>>() - }) - .unwrap_or_else(|| Ok(Vec::new())); - Some((span, fields)) - }) - .unwrap_or_else(|| (None, Ok(Vec::new()))); - - let level = caps - .name("level") - .and_then(|l| l.as_str().parse().ok()) - // Setting the target without the level enables every level for that target - .unwrap_or(LevelFilter::TRACE); + Level { level_start } => { + cur.level = match &from[level_start..] { + "" => LevelFilter::TRACE, + level => LevelFilter::from_str(level)?, + }; + } + Target | Complete => {} + _ => return Err(ParseError::new()), + }; - Ok(Self { - level, - target, - in_span, - fields: fields?, - }) + Ok(cur) } } From f5444f5dd1a6c23bb4e6144fe66d752dd255603a Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Tue, 13 May 2025 03:51:29 -0700 Subject: [PATCH 12/24] macros: Remove 'r#' prefix from raw identifiers in field names (#3130) * macros: Add test involving raw identifier * macros: Remove 'r#' prefix from raw identifiers in field names --- tracing/src/lib.rs | 63 +++++++++++++++++++++++++++++++++++++++++- tracing/src/macros.rs | 9 ++++-- tracing/tests/event.rs | 12 ++++++++ 3 files changed, 80 insertions(+), 4 deletions(-) diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 9b8fdde16a..eba6c7b9d8 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -987,10 +987,11 @@ pub mod subscriber; pub mod __macro_support { pub use crate::callsite::Callsite; use crate::{subscriber::Interest, Metadata}; + use core::{fmt, str}; // Re-export the `core` functions that are used in macros. This allows // a crate to be named `core` and avoid name clashes. // See here: https://github.com/tokio-rs/tracing/issues/2761 - pub use core::{concat, file, format_args, iter::Iterator, line, option::Option}; + pub use core::{concat, file, format_args, iter::Iterator, line, option::Option, stringify}; /// Callsite implementation used by macro-generated code. /// @@ -1065,6 +1066,66 @@ pub mod __macro_support { .build(), ); } + + /// Implementation detail used for constructing FieldSet names from raw + /// identifiers. In `info!(..., r#type = "...")` the macro would end up + /// constructing a name equivalent to `FieldName(*b"type")`. + pub struct FieldName([u8; N]); + + impl FieldName { + /// Convert `"prefix.r#keyword.suffix"` to `b"prefix.keyword.suffix"`. + pub const fn new(input: &str) -> Self { + let input = input.as_bytes(); + let mut output = [0u8; N]; + let mut read = 0; + let mut write = 0; + while read < input.len() { + if read + 1 < input.len() && input[read] == b'r' && input[read + 1] == b'#' { + read += 2; + } + output[write] = input[read]; + read += 1; + write += 1; + } + assert!(write == N); + Self(output) + } + + pub const fn as_str(&self) -> &str { + // SAFETY: Because of the private visibility of self.0, it must have + // been computed by Self::new. So these bytes are all of the bytes + // of some original valid UTF-8 string, but with "r#" substrings + // removed, which cannot have produced invalid UTF-8. + unsafe { str::from_utf8_unchecked(self.0.as_slice()) } + } + } + + impl FieldName<0> { + /// For `"prefix.r#keyword.suffix"` compute `"prefix.keyword.suffix".len()`. + pub const fn len(input: &str) -> usize { + // Count occurrences of "r#" + let mut raw = 0; + + let mut i = 0; + while i < input.len() { + if input.as_bytes()[i] == b'#' { + raw += 1; + } + i += 1; + } + + input.len() - 2 * raw + } + } + + impl fmt::Debug for FieldName { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_tuple("FieldName") + .field(&self.as_str()) + .finish() + } + } } #[cfg(feature = "log")] diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index b5e8f37cef..75d2cc359a 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -3096,9 +3096,12 @@ macro_rules! level_to_log { #[doc(hidden)] #[macro_export] macro_rules! __tracing_stringify { - ($($t:tt)*) => { - stringify!($($t)*) - }; + ($($k:ident).+) => {{ + const NAME: $crate::__macro_support::FieldName<{ + $crate::__macro_support::FieldName::len($crate::__macro_support::stringify!($($k).+)) + }> = $crate::__macro_support::FieldName::new($crate::__macro_support::stringify!($($k).+)); + NAME.as_str() + }}; } #[cfg(not(feature = "log"))] diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs index 25b5bbe0c0..25c44f862b 100644 --- a/tracing/tests/event.rs +++ b/tracing/tests/event.rs @@ -618,3 +618,15 @@ fn keyword_ident_in_field_name() { with_default(subscriber, || error!(crate = "tracing", "message")); handle.assert_finished(); } + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn raw_ident_in_field_name() { + let (subscriber, handle) = subscriber::mock() + .event(expect::event().with_fields(expect::field("this.type").with_value(&"Value"))) + .only() + .run_with_handle(); + + with_default(subscriber, || error!(this.r#type = "Value")); + handle.assert_finished(); +} From 8f57bf9c4471d48ed323539136f2a959f5284e68 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Thu, 15 May 2025 01:20:18 -0700 Subject: [PATCH 13/24] examples: add env-filter-explorer example (#3233) This example demonstrates how to use the `tracing-subscriber` crate's `EnvFilter` type to filter log messages based on their metadata. The example provides a text area where users can input an environment filter string, and displays the log messages that would be captured by that filter. --- examples/Cargo.toml | 6 + examples/examples/env-filter-explorer.rs | 251 +++++++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 examples/examples/env-filter-explorer.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 1443644503..9665d31a1c 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -50,6 +50,12 @@ tempfile = "3" snafu = "0.6.10" thiserror = "1.0.31" +# env-filter-explorer example +ansi-to-tui = "7.0.0" +ratatui = "0.29.0" +crossterm = "0.28.1" +tui-textarea = "0.7.0" + # valuable examples valuable = { version = "0.1.0", features = ["derive"] } diff --git a/examples/examples/env-filter-explorer.rs b/examples/examples/env-filter-explorer.rs new file mode 100644 index 0000000000..f788aaa57c --- /dev/null +++ b/examples/examples/env-filter-explorer.rs @@ -0,0 +1,251 @@ +use std::{ + io::{self}, + sync::{Arc, Mutex}, +}; + +use ansi_to_tui::IntoText; +use crossterm::event; +use ratatui::{ + buffer::Buffer, + layout::{Constraint, Layout, Rect}, + style::Stylize, + widgets::{Block, Widget}, + DefaultTerminal, Frame, +}; +use tracing_subscriber::{filter::ParseError, fmt::MakeWriter, EnvFilter}; +use tui_textarea::{Input, Key, TextArea}; + +/// A list of preset filters to make it easier to explore the filter syntax. +/// +/// The UI allows you to select a preset filter with the up/down arrow keys. +const PRESET_FILTERS: &[&str] = &[ + "trace", + "debug", + "info", + "warn", + "error", + "[with_fields]", + "[with_fields{foo}]", + "[with_fields{bar}]", + "[with_fields{foo=42}]", + "[with_fields{bar=bar}]", + "[with_fields{foo=99}]", + "[with_fields{bar=nope}]", + "[with_fields{nonexistent}]", + "other_crate=info", + "other_crate=debug", + "trace,other_crate=warn", + "warn,other_crate=info", +]; + +fn main() -> io::Result<()> { + let terminal = ratatui::init(); + let result = App::new().run(terminal); + ratatui::restore(); + result +} + +struct App { + filter: TextArea<'static>, + preset_index: usize, + exit: bool, + log_widget: Result, +} + +impl App { + /// Creates a new instance of the application, ready to run + fn new() -> Self { + let mut filter = TextArea::new(vec![PRESET_FILTERS[0].to_string()]); + let title = "Env Filter Explorer. to quit, / to select preset"; + filter.set_block(Block::bordered().title(title)); + Self { + filter, + preset_index: 0, + exit: false, + log_widget: Ok(LogWidget::default()), + } + } + + /// The application's main loop until the user exits. + fn run(mut self, mut terminal: DefaultTerminal) -> io::Result<()> { + while !self.exit { + self.log_widget = self.evaluate_filter(); + terminal.draw(|frame| self.render(frame))?; + self.handle_event()?; + } + Ok(()) + } + + /// Render the application with a filter input area and a log output area. + fn render(&self, frame: &mut Frame) { + let layout = Layout::vertical([Constraint::Length(3), Constraint::Fill(1)]); + let [filter_area, main_area] = layout.areas(frame.area()); + frame.render_widget(&self.filter, filter_area); + match &self.log_widget { + Ok(log_widget) => frame.render_widget(log_widget, main_area), + Err(error) => frame.render_widget(error.to_string().red(), main_area), + } + } + + /// Handles a single terminal event (e.g. mouse, keyboard, resize). + fn handle_event(&mut self) -> io::Result<()> { + let event = event::read()?; + let input = Input::from(event); + match input.key { + Key::Enter => return Ok(()), // ignore new lines + Key::Esc => self.exit = true, + Key::Up => self.select_previous_preset(), + Key::Down => self.select_next_preset(), + _ => self.add_input(input), + } + Ok(()) + } + + /// Selects the previous preset filter in the list. + fn select_previous_preset(&mut self) { + self.select_preset(self.preset_index.saturating_sub(1)); + } + + /// Selects the next preset filter in the list. + fn select_next_preset(&mut self) { + self.select_preset((self.preset_index + 1).min(PRESET_FILTERS.len() - 1)); + } + + /// Selects a preset filter by index and updates the filter text area. + fn select_preset(&mut self, index: usize) { + self.preset_index = index; + self.filter.select_all(); + self.filter.delete_line_by_head(); + self.filter.insert_str(PRESET_FILTERS[self.preset_index]); + } + + /// Handles normal keyboard input by adding it to the filter text area. + fn add_input(&mut self, input: Input) { + self.filter.input(input); + } + + /// Evaluates the current filter and returns a log widget with the filtered logs or an error. + fn evaluate_filter(&mut self) -> Result { + let filter = self.filter.lines()[0].to_string(); + let env_filter = EnvFilter::builder().parse(filter)?; + let log_widget = LogWidget::default(); + let subscriber = tracing_subscriber::fmt() + .with_env_filter(env_filter) + .with_writer(log_widget.clone()) + .finish(); + tracing::subscriber::with_default(subscriber, || { + simulate_logging(); + other_crate_span(); + }); + Ok(log_widget) + } +} + +/// A writer that collects logs into a buffer and can be displayed as a widget. +#[derive(Clone, Default, Debug)] +struct LogWidget { + buffer: Arc>>, +} + +impl io::Write for LogWidget { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buffer.lock().unwrap().write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.buffer.lock().unwrap().flush() + } +} + +impl<'a> MakeWriter<'a> for LogWidget { + type Writer = Self; + + fn make_writer(&'a self) -> Self::Writer { + self.clone() + } +} + +impl Widget for &LogWidget { + /// Displays the logs that have been collected in the buffer. + /// + /// If the buffer is empty, it displays "No matching logs". + fn render(self, area: Rect, buf: &mut Buffer) { + let buffer = self.buffer.lock().unwrap(); + let string = String::from_utf8_lossy(&buffer).to_string(); + if string.is_empty() { + "No matching logs".render(area, buf); + return; + } + string + .into_text() // convert a string with ANSI escape codes into ratatui Text + .unwrap_or_else(|err| format!("Error parsing output: {err}").into()) + .render(area, buf); + } +} + +#[tracing::instrument] +fn simulate_logging() { + tracing::info!("This is an info message"); + tracing::error!("This is an error message"); + tracing::warn!("This is a warning message"); + tracing::debug!("This is a debug message"); + tracing::trace!("This is a trace message"); + + with_fields(42, "bar"); + with_fields(99, "nope"); + + trace_span(); + debug_span(); + info_span(); + warn_span(); + error_span(); +} + +#[tracing::instrument] +fn with_fields(foo: u32, bar: &'static str) { + tracing::info!(foo, bar, "This is an info message with fields"); +} + +#[tracing::instrument(level = "trace")] +fn trace_span() { + tracing::error!("Error message inside a span with trace level"); + tracing::info!("Info message inside a span with trace level"); + tracing::trace!("Trace message inside a span with trace level"); +} + +#[tracing::instrument] +fn debug_span() { + tracing::error!("Error message inside a span with debug level"); + tracing::info!("Info message inside a span with debug level"); + tracing::debug!("Debug message inside a span with debug level"); +} + +#[tracing::instrument] +fn info_span() { + tracing::error!("Error message inside a span with info level"); + tracing::info!("Info message inside a span with info level"); + tracing::debug!("Debug message inside a span with info level"); +} + +#[tracing::instrument] +fn warn_span() { + tracing::error!("Error message inside a span with warn level"); + tracing::info!("Info message inside a span with warn level"); + tracing::debug!("Debug message inside a span with warn level"); +} + +#[tracing::instrument] +fn error_span() { + tracing::error!("Error message inside a span with error level"); + tracing::info!("Info message inside a span with error level"); + tracing::debug!("Debug message inside a span with error level"); +} + +#[tracing::instrument(target = "other_crate")] +fn other_crate_span() { + tracing::error!(target: "other_crate", "An error message from another crate"); + tracing::warn!(target: "other_crate", "A warning message from another crate"); + tracing::info!(target: "other_crate", "An info message from another crate"); + tracing::debug!(target: "other_crate", "A debug message from another crate"); + tracing::trace!(target: "other_crate", "A trace message from another crate"); +} From da14f9b3179d1f3e6927a3ff4f5e711893e957e6 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Tue, 27 May 2025 10:28:28 +0200 Subject: [PATCH 14/24] subscriber: update matchers to 0.2 (#3033) Update the version of the `matchers` crate to 0.2. This requires also adding a direct dependency on `regex-automata` to enable the `std` feature. --- .github/workflows/CI.yml | 4 ++-- README.md | 4 ++-- tracing-attributes/Cargo.toml | 2 +- tracing-attributes/README.md | 4 ++-- tracing-attributes/src/lib.rs | 4 ++-- tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 6 +++--- tracing-core/src/lib.rs | 4 ++-- tracing-error/Cargo.toml | 2 +- tracing-error/README.md | 4 ++-- tracing-error/src/lib.rs | 4 ++-- tracing-flame/Cargo.toml | 2 +- tracing-flame/README.md | 4 ++-- tracing-flame/src/lib.rs | 4 ++-- tracing-futures/Cargo.toml | 2 +- tracing-futures/README.md | 4 ++-- tracing-futures/src/lib.rs | 4 ++-- tracing-journald/Cargo.toml | 2 +- tracing-journald/README.md | 6 +++--- tracing-journald/src/lib.rs | 4 ++-- tracing-log/Cargo.toml | 2 +- tracing-log/README.md | 4 ++-- tracing-log/src/lib.rs | 4 ++-- tracing-macros/Cargo.toml | 2 +- tracing-mock/Cargo.toml | 2 +- tracing-mock/README.md | 4 ++-- tracing-serde/Cargo.toml | 2 +- tracing-serde/README.md | 4 ++-- tracing-serde/src/lib.rs | 4 ++-- tracing-subscriber/Cargo.toml | 8 +++++--- tracing-subscriber/README.md | 4 ++-- tracing-subscriber/src/filter/env/field.rs | 5 +++-- tracing-subscriber/src/fmt/mod.rs | 2 +- tracing-subscriber/src/lib.rs | 4 ++-- tracing-tower/Cargo.toml | 2 +- tracing/Cargo.toml | 2 +- tracing/README.md | 4 ++-- tracing/src/lib.rs | 4 ++-- 38 files changed, 69 insertions(+), 66 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index dc14a813d9..43842398cf 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -121,7 +121,7 @@ jobs: shell: bash check-msrv: - # Run `cargo check` on our minimum supported Rust version (1.63.0). This + # Run `cargo check` on our minimum supported Rust version (1.65.0). This # checks with minimal versions; maximal versions are checked above. name: "cargo check (+MSRV -Zminimal-versions)" needs: check @@ -143,7 +143,7 @@ jobs: - tracing-tower - tracing toolchain: - - 1.63.0 + - 1.65.0 - stable steps: - uses: actions/checkout@v4 diff --git a/README.md b/README.md index f7df0e4c25..5b117d4fc8 100644 --- a/README.md +++ b/README.md @@ -252,7 +252,7 @@ attachment that `Future::instrument` does. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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 @@ -396,7 +396,7 @@ are not maintained by the `tokio` project. These include: - [`tracing-elastic-apm`] provides a layer for reporting traces to [Elastic APM]. - [`tracing-etw`] provides a layer for emitting Windows [ETW] events. - [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry]. -- [`tracing-forest`] provides a subscriber that preserves contextual coherence by +- [`tracing-forest`] provides a subscriber that preserves contextual coherence by grouping together logs from the same spans during writing. - [`tracing-loki`] provides a layer for shipping logs to [Grafana Loki]. - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format. diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 1bf85bd68d..0abf6e0b9e 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -25,7 +25,7 @@ keywords = ["logging", "tracing", "macro", "instrument", "log"] license = "MIT" readme = "README.md" edition = "2018" -rust-version = "1.63.0" +rust-version = "1.65.0" [lib] proc-macro = true diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md index dd0c7ed535..b8227aeb61 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.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][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.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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 adfabacddc..67d7c7c21e 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.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][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.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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/Cargo.toml b/tracing-core/Cargo.toml index 5f7e41f973..4d80fb4a82 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -22,7 +22,7 @@ categories = [ ] keywords = ["logging", "tracing", "profiling"] edition = "2018" -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["std", "valuable?/std"] diff --git a/tracing-core/README.md b/tracing-core/README.md index 32f67cd019..873306f32c 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -53,12 +53,12 @@ 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.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions ## Usage - + Application authors will typically not use this crate directly. Instead, they will use the [`tracing`] crate, which provides a much more fully-featured API. However, this crate's API will change very infrequently, so it may be used @@ -99,7 +99,7 @@ The following crate feature flags are available: ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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/lib.rs b/tracing-core/src/lib.rs index 4286d88c33..8075578eb0 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -23,7 +23,7 @@ //! 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.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -92,7 +92,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-error/Cargo.toml b/tracing-error/Cargo.toml index c4710c6145..9c2733e879 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -29,7 +29,7 @@ keywords = [ "backtrace" ] edition = "2018" -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["traced-error"] diff --git a/tracing-error/README.md b/tracing-error/README.md index c4ca0393e8..3cab824e51 100644 --- a/tracing-error/README.md +++ b/tracing-error/README.md @@ -48,7 +48,7 @@ The crate provides the following: **Note**: This crate is currently experimental. -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions @@ -186,7 +186,7 @@ fn main() { ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-error/src/lib.rs b/tracing-error/src/lib.rs index 138aa92adf..d362c3a0e9 100644 --- a/tracing-error/src/lib.rs +++ b/tracing-error/src/lib.rs @@ -18,7 +18,7 @@ //! //! **Note**: This crate is currently experimental. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -167,7 +167,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-flame/Cargo.toml b/tracing-flame/Cargo.toml index 81ea126cfa..26faecfda9 100644 --- a/tracing-flame/Cargo.toml +++ b/tracing-flame/Cargo.toml @@ -19,7 +19,7 @@ categories = [ "asynchronous", ] keywords = ["tracing", "subscriber", "flamegraph", "profiling"] -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["smallvec"] diff --git a/tracing-flame/README.md b/tracing-flame/README.md index 7722a0b371..e6a6367e14 100644 --- a/tracing-flame/README.md +++ b/tracing-flame/README.md @@ -26,7 +26,7 @@ flamegraph/flamechart. Flamegraphs/flamecharts are useful for identifying perfor bottlenecks in an application. For more details, see Brendan Gregg's [post] on flamegraphs. -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions [post]: http://www.brendangregg.com/flamegraphs.html @@ -106,7 +106,7 @@ _flamechart_, which _does not_ sort or collapse identical stack frames. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-flame/src/lib.rs b/tracing-flame/src/lib.rs index 6ae2e19a3e..9f10ac6bf2 100644 --- a/tracing-flame/src/lib.rs +++ b/tracing-flame/src/lib.rs @@ -10,7 +10,7 @@ //! issues bottlenecks in an application. For more details, see Brendan Gregg's [post] //! on flamegraphs. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! [post]: http://www.brendangregg.com/flamegraphs.html @@ -95,7 +95,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-futures/Cargo.toml b/tracing-futures/Cargo.toml index 0dac3d4c78..2b68577b22 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -16,7 +16,7 @@ categories = [ ] keywords = ["logging", "profiling", "tracing", "futures", "async"] license = "MIT" -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["std-future", "std"] diff --git a/tracing-futures/README.md b/tracing-futures/README.md index bc1c833b73..4b68e65f8a 100644 --- a/tracing-futures/README.md +++ b/tracing-futures/README.md @@ -51,14 +51,14 @@ The crate provides the following traits: [`Subscriber`]: https://docs.rs/tracing/latest/tracing/subscriber/index.html [`tracing`]: https://crates.io/crates/tracing -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-futures/src/lib.rs b/tracing-futures/src/lib.rs index 8425c8dd69..e706f4a8b5 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -15,7 +15,7 @@ //! * [`WithSubscriber`] allows a `tracing` [`Subscriber`] to be attached to a //! future, sink, stream, or executor. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -59,7 +59,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-journald/Cargo.toml b/tracing-journald/Cargo.toml index d3bbef4bd0..328b069436 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -13,7 +13,7 @@ categories = [ "development-tools::profiling", ] keywords = ["tracing", "journald"] -rust-version = "1.63.0" +rust-version = "1.65.0" [dependencies] libc = "0.2.126" diff --git a/tracing-journald/README.md b/tracing-journald/README.md index 133c2095e4..98a5bb5307 100644 --- a/tracing-journald/README.md +++ b/tracing-journald/README.md @@ -27,8 +27,8 @@ scoped, structured, and async-aware diagnostics. `tracing-journald` provides a [`tracing-subscriber::Layer`][layer] implementation for logging `tracing` spans and events to [`systemd-journald`][journald], on Linux distributions that use `systemd`. - -*Compiler support: [requires `rustc` 1.63+][msrv]* + +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions [`tracing`]: https://crates.io/crates/tracing @@ -38,7 +38,7 @@ and events to [`systemd-journald`][journald], on Linux distributions that use ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-journald/src/lib.rs b/tracing-journald/src/lib.rs index eef4837d6d..0118c00d80 100644 --- a/tracing-journald/src/lib.rs +++ b/tracing-journald/src/lib.rs @@ -11,7 +11,7 @@ //! and events to [`systemd-journald`][journald], on Linux distributions that //! use `systemd`. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! [`tracing`]: https://crates.io/crates/tracing @@ -20,7 +20,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-log/Cargo.toml b/tracing-log/Cargo.toml index df2f612fea..4c1be56869 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -15,7 +15,7 @@ categories = [ keywords = ["logging", "tracing", "log"] license = "MIT" readme = "README.md" -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["log-tracer", "std"] diff --git a/tracing-log/README.md b/tracing-log/README.md index 2e009b4e00..993aa8ea76 100644 --- a/tracing-log/README.md +++ b/tracing-log/README.md @@ -54,14 +54,14 @@ This crate provides: [`tracing::Subscriber`]: https://docs.rs/tracing/latest/tracing/trait.Subscriber.html [`tracing::Event`]: https://docs.rs/tracing/latest/tracing/struct.Event.html -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-log/src/lib.rs b/tracing-log/src/lib.rs index 36b74c7aac..199a51d89b 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -14,7 +14,7 @@ //! - [`LogTracer`], a [`log::Log`] implementation that consumes [`log::Record`]s //! and outputs them as [`tracing::Event`]. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -75,7 +75,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-macros/Cargo.toml b/tracing-macros/Cargo.toml index 703ca6fbee..94a82543c5 100644 --- a/tracing-macros/Cargo.toml +++ b/tracing-macros/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.63.0" +rust-version = "1.65.0" [dependencies] tracing = { path = "../tracing", version = "0.1.35" } diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index f2e24c4011..ab6fb79ca7 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -26,7 +26,7 @@ keywords = [ "testing" ] edition = "2018" -rust-version = "1.63.0" +rust-version = "1.65.0" [dependencies] tracing = { path = "../tracing", version = "0.1.41", features = ["std", "attributes"], default-features = false } diff --git a/tracing-mock/README.md b/tracing-mock/README.md index 185c7a3cff..e2a168862c 100644 --- a/tracing-mock/README.md +++ b/tracing-mock/README.md @@ -35,7 +35,7 @@ structured, event-based diagnostic information. `tracing-mock` provides tools for making assertions about what `tracing` diagnostics are emitted by code under test. -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions [`tracing`]: https://github.com/tokio-rs/tracing @@ -154,7 +154,7 @@ handle.assert_finished(); ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-serde/Cargo.toml b/tracing-serde/Cargo.toml index af3dd8c8ca..58caeeb5af 100644 --- a/tracing-serde/Cargo.toml +++ b/tracing-serde/Cargo.toml @@ -16,7 +16,7 @@ categories = [ "encoding", ] keywords = ["logging", "tracing", "serialization"] -rust-version = "1.63.0" +rust-version = "1.65.0" [features] valuable = ["valuable_crate", "valuable-serde", "tracing-core/valuable"] diff --git a/tracing-serde/README.md b/tracing-serde/README.md index 8ede6f9806..0fdbe81cf5 100644 --- a/tracing-serde/README.md +++ b/tracing-serde/README.md @@ -36,7 +36,7 @@ and tracing data to monitor your services in production. The `tracing` crate provides the APIs necessary for instrumenting libraries and applications to emit trace data. -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions @@ -97,7 +97,7 @@ trace data. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-serde/src/lib.rs b/tracing-serde/src/lib.rs index a7a930c191..bb49ce594d 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -32,7 +32,7 @@ //! The `tracing` crate provides the APIs necessary for instrumenting //! libraries and applications to emit trace data. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -148,7 +148,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 409d679e29..ab34614642 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -20,14 +20,14 @@ categories = [ "asynchronous", ] keywords = ["logging", "tracing", "metrics", "subscriber"] -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["smallvec", "fmt", "ansi", "tracing-log", "std"] alloc = [] std = ["alloc", "tracing-core/std"] -env-filter = ["matchers", "once_cell", "tracing", "std", "thread_local"] +env-filter = ["matchers", "once_cell", "tracing", "std", "thread_local", "dep:regex-automata"] fmt = ["registry", "std"] ansi = ["fmt", "nu-ansi-term"] registry = ["sharded-slab", "thread_local", "std"] @@ -45,7 +45,9 @@ tracing-core = { path = "../tracing-core", version = "0.1.33", default-features # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1.41", default-features = false } -matchers = { optional = true, version = "0.1.0" } +matchers = { optional = true, version = "0.2.0" } +# required to have matchers::BuildError implement std::error::Error +regex-automata = { optional = true, version = "0.4", default-features = false, features = ["std"] } smallvec = { optional = true, version = "1.9.0" } once_cell = { optional = true, version = "1.13.0" } diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index a5ee156360..037d403fe0 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -42,14 +42,14 @@ application authors using `tracing` to instrument their applications. [discord-url]: https://discord.gg/EeF3cQw [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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-subscriber/src/filter/env/field.rs b/tracing-subscriber/src/filter/env/field.rs index 7ff05ea565..49b73b1d46 100644 --- a/tracing-subscriber/src/filter/env/field.rs +++ b/tracing-subscriber/src/filter/env/field.rs @@ -234,7 +234,8 @@ impl ValueMatch { /// This returns an error if the string didn't contain a valid `bool`, /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular /// expression. - fn parse_regex(s: &str) -> Result { + #[allow(clippy::result_large_err)] + fn parse_regex(s: &str) -> Result { s.parse::() .map(ValueMatch::Bool) .or_else(|_| s.parse::().map(ValueMatch::U64)) @@ -279,7 +280,7 @@ impl fmt::Display for ValueMatch { // === impl MatchPattern === impl FromStr for MatchPattern { - type Err = matchers::Error; + type Err = matchers::BuildError; fn from_str(s: &str) -> Result { let matcher = s.parse::()?; Ok(Self { diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index ef04bd6a6b..94ebd49c3f 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -16,7 +16,7 @@ //! tracing-subscriber = "0.3" //! ``` //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: super#supported-rust-versions //! diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 182e5d34b4..ccf7a222ca 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -10,7 +10,7 @@ //! `tracing-subscriber` is intended for use by both `Subscriber` authors and //! application authors using `tracing` to instrument their applications. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -138,7 +138,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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-tower/Cargo.toml b/tracing-tower/Cargo.toml index 38e265d224..f90beb92ba 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.63.0" +rust-version = "1.65.0" [features] default = ["tower-layer", "tower-make"] diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 4237eb3e51..b1bb54af47 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -22,7 +22,7 @@ categories = [ ] keywords = ["logging", "tracing", "metrics", "async"] edition = "2018" -rust-version = "1.63.0" +rust-version = "1.65.0" [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.33", default-features = false } diff --git a/tracing/README.md b/tracing/README.md index 0bd420863e..fa8d0acaad 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -47,7 +47,7 @@ data as well as textual messages. The `tracing` crate provides the APIs necessary for instrumenting libraries and applications to emit trace data. -*Compiler support: [requires `rustc` 1.63+][msrv]* +*Compiler support: [requires `rustc` 1.65+][msrv]* [msrv]: #supported-rust-versions @@ -447,7 +447,7 @@ undergoing active development. They may be less stable than `tracing` and ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.63. The current Tracing version is not guaranteed to build on Rust +version is 1.65. 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/src/lib.rs b/tracing/src/lib.rs index eba6c7b9d8..07f1a5d19c 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -19,7 +19,7 @@ //! The `tracing` crate provides the APIs necessary for instrumenting libraries //! and applications to emit trace data. //! -//! *Compiler support: [requires `rustc` 1.63+][msrv]* +//! *Compiler support: [requires `rustc` 1.65+][msrv]* //! //! [msrv]: #supported-rust-versions //! # Core Concepts @@ -873,7 +873,7 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.63. The current Tracing version is not guaranteed to build on +//! version is 1.65. 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 From 5671f343ccfbc84a12a47dc02ac2d44175ec46ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20M=C3=BCller?= Date: Tue, 27 May 2025 11:12:33 -0700 Subject: [PATCH 15/24] subscriber: enable TestWriter to write to stderr (#3187) It can be useful to have a TestWriter that does not log to stdout but stderr instead. For example, that allows for potentially easier filtering of tracing output (because the remaining output of, say, cargo test goes to stdout) or to mirror behavior of env_logger, which by default logs to stderr. Introduce the TestWriter::with_stderr() constructor to enable such usage. The default is left unchanged. Co-authored-by: David Barsky --- tracing-subscriber/src/fmt/writer.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs index a20626a240..2925b0de7a 100644 --- a/tracing-subscriber/src/fmt/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -494,8 +494,10 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// /// `TestWriter` is used by [`fmt::Subscriber`] or [`fmt::Layer`] to enable capturing support. /// -/// `cargo test` can only capture output from the standard library's [`print!`] macro. See -/// [`libtest`'s output capturing][capturing] for more details about output capturing. +/// `cargo test` can only capture output from the standard library's [`print!`] and [`eprint!`] +/// macros. See [`libtest`'s output capturing][capturing] and +/// [rust-lang/rust#90785](https://github.com/rust-lang/rust/issues/90785) for more details about +/// output capturing. /// /// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using /// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable. @@ -509,7 +511,9 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// [`print!`]: std::print! #[derive(Default, Debug)] pub struct TestWriter { - _p: (), + /// Whether or not to use `stderr` instead of the default `stdout` as + /// the underlying stream to write to. + use_stderr: bool, } /// A writer that erases the specific [`io::Write`] and [`MakeWriter`] types being used. @@ -708,12 +712,23 @@ impl TestWriter { pub fn new() -> Self { Self::default() } + + /// Returns a new `TestWriter` that writes to `stderr` instead of `stdout`. + pub fn with_stderr() -> Self { + Self { + use_stderr: true, + } + } } impl io::Write for TestWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let out_str = String::from_utf8_lossy(buf); - print!("{}", out_str); + if self.use_stderr { + eprint!("{}", out_str) + } else { + print!("{}", out_str) + } Ok(buf.len()) } From 8b0735559d24d88ccd597d4d59cd1f9bbee620ad Mon Sep 17 00:00:00 2001 From: Marios Date: Wed, 28 May 2025 00:16:54 +0300 Subject: [PATCH 16/24] Fix typo in README.md (#3062) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b117d4fc8..d4690f225e 100644 --- a/README.md +++ b/README.md @@ -370,7 +370,7 @@ are not maintained by the `tokio` project. These include: - [`tracing-actix`] provides `tracing` integration for the `actix` actor framework. - [`axum-insights`] provides `tracing` integration and Application insights export for the `axum` web framework. -- [`tracing-gelf`] implements a subscriber for exporting traces in Greylog +- [`tracing-gelf`] implements a subscriber for exporting traces in Graylog GELF format. - [`tracing-coz`] provides integration with the [coz] causal profiler (Linux-only). From d7d390c0408fe14018af97ef4e120d00f5e95302 Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Tue, 27 May 2025 23:28:39 +0200 Subject: [PATCH 17/24] Fix typos in subscriber docs (#2831) --- tracing-subscriber/src/fmt/writer.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs index 2925b0de7a..52a20d694f 100644 --- a/tracing-subscriber/src/fmt/writer.rs +++ b/tracing-subscriber/src/fmt/writer.rs @@ -229,7 +229,7 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stderr` only if the span or - /// // event's level is >= WARN (WARN and ERROR). + /// // event's level is <= WARN (WARN and ERROR). /// let mk_writer = std::io::stderr.with_max_level(Level::WARN); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); @@ -293,7 +293,7 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stdout` only if the span or - /// // event's level is <= DEBUG (DEBUG and TRACE). + /// // event's level is >= DEBUG (DEBUG and TRACE). /// let mk_writer = std::io::stdout.with_min_level(Level::DEBUG); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); @@ -467,7 +467,7 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// - /// // Produces a writer that writes to `stderr` if the level is >= WARN, + /// // Produces a writer that writes to `stderr` if the level is <= WARN, /// // or returns `OptionalWriter::none()` otherwise. /// let stderr = std::io::stderr.with_max_level(Level::WARN); /// From ebd2009bb628316e4f93549b14e3da961e2e3b9f Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Tue, 27 May 2025 23:41:34 +0200 Subject: [PATCH 18/24] chore: Bump thiserror to v2 (#3172) --- examples/Cargo.toml | 2 +- tracing-appender/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 9665d31a1c..c88f03e8eb 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -48,7 +48,7 @@ tempfile = "3" # fmt examples snafu = "0.6.10" -thiserror = "1.0.31" +thiserror = "2" # env-filter-explorer example ansi-to-tui = "7.0.0" diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml index e696f89f9b..7a133c6ea4 100644 --- a/tracing-appender/Cargo.toml +++ b/tracing-appender/Cargo.toml @@ -24,7 +24,7 @@ rust-version = "1.63.0" crossbeam-channel = "0.5.6" time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] } parking_lot = { optional = true, version = "0.12.1" } -thiserror = "1" +thiserror = "2" [dependencies.tracing-subscriber] path = "../tracing-subscriber" From 4410778faada8084f01ea85e9e6f25a1d87af306 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Tue, 27 May 2025 21:57:21 +0000 Subject: [PATCH 19/24] Update nu-ansi-term to 0.50 (#3049) --- examples/Cargo.toml | 2 +- tracing-subscriber/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index c88f03e8eb..543406a322 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -38,7 +38,7 @@ bytes = "1" argh = "0.1.8" # sloggish example -nu-ansi-term = "0.46.0" +nu-ansi-term = "0.50.0" humantime = "2.1.0" log = "0.4.17" diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index ab34614642..1ea08eb70b 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -53,7 +53,7 @@ once_cell = { optional = true, version = "1.13.0" } # fmt tracing-log = { path = "../tracing-log", version = "0.2.0", optional = true, default-features = false, features = ["log-tracer", "std"] } -nu-ansi-term = { version = "0.46.0", optional = true } +nu-ansi-term = { version = "0.50.0", optional = true } time = { version = "0.3.2", features = ["formatting"], optional = true } # only required by the json feature From addc9c2675328aead4694fdf95ed68c926caa5a8 Mon Sep 17 00:00:00 2001 From: Heath Stewart Date: Wed, 28 May 2025 07:44:19 +0000 Subject: [PATCH 20/24] attributes: Globally qualify attribute paths (#3126) Avoid ambiguities with any user-defined `tracing` modules by globally qualifying types used in the attribute-generated code e.g., `::tracing::Level`. --- tracing-attributes/src/attr.rs | 12 ++++++------ tracing-attributes/src/expand.rs | 18 +++++++++--------- tracing-attributes/tests/instrument.rs | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 12525ec9e6..f3d91418e7 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -378,7 +378,7 @@ impl ToTokens for Field { // `instrument` produce empty field values, so changing it now // is a breaking change. agh. let name = &self.name; - tokens.extend(quote!(#name = tracing::field::Empty)) + tokens.extend(quote!(#name = ::tracing::field::Empty)) } else { self.kind.to_tokens(tokens); self.name.to_tokens(tokens); @@ -454,11 +454,11 @@ impl Parse for Level { impl ToTokens for Level { fn to_tokens(&self, tokens: &mut TokenStream) { match self { - Level::Trace => tokens.extend(quote!(tracing::Level::TRACE)), - Level::Debug => tokens.extend(quote!(tracing::Level::DEBUG)), - Level::Info => tokens.extend(quote!(tracing::Level::INFO)), - Level::Warn => tokens.extend(quote!(tracing::Level::WARN)), - Level::Error => tokens.extend(quote!(tracing::Level::ERROR)), + Level::Trace => tokens.extend(quote!(::tracing::Level::TRACE)), + Level::Debug => tokens.extend(quote!(::tracing::Level::DEBUG)), + Level::Info => tokens.extend(quote!(::tracing::Level::INFO)), + Level::Warn => tokens.extend(quote!(::tracing::Level::WARN)), + Level::Error => tokens.extend(quote!(::tracing::Level::ERROR)), Level::Path(ref pat) => tokens.extend(quote!(#pat)), } } diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 6481de758e..69db03cbeb 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -199,7 +199,7 @@ fn gen_block( }) .map(|(user_name, (real_name, record_type))| match record_type { RecordType::Value => quote!(#user_name = #real_name), - RecordType::Debug => quote!(#user_name = tracing::field::debug(&#real_name)), + RecordType::Debug => quote!(#user_name = ::tracing::field::debug(&#real_name)), }) .collect(); @@ -223,7 +223,7 @@ fn gen_block( let custom_fields = &args.fields; - quote!(tracing::span!( + quote!(::tracing::span!( target: #target, #(parent: #parent,)* #level, @@ -241,10 +241,10 @@ fn gen_block( let level_tokens = event_args.level(Level::Error); match event_args.mode { FormatMode::Default | FormatMode::Display => Some(quote!( - tracing::event!(target: #target, #level_tokens, error = %e) + ::tracing::event!(target: #target, #level_tokens, error = %e) )), FormatMode::Debug => Some(quote!( - tracing::event!(target: #target, #level_tokens, error = ?e) + ::tracing::event!(target: #target, #level_tokens, error = ?e) )), } } @@ -256,10 +256,10 @@ fn gen_block( let level_tokens = event_args.level(args_level); match event_args.mode { FormatMode::Display => Some(quote!( - tracing::event!(target: #target, #level_tokens, return = %x) + ::tracing::event!(target: #target, #level_tokens, return = %x) )), FormatMode::Default | FormatMode::Debug => Some(quote!( - tracing::event!(target: #target, #level_tokens, return = ?x) + ::tracing::event!(target: #target, #level_tokens, return = ?x) )), } } @@ -320,7 +320,7 @@ fn gen_block( let __tracing_instrument_future = #mk_fut; if !__tracing_attr_span.is_disabled() { #follows_from - tracing::Instrument::instrument( + ::tracing::Instrument::instrument( __tracing_instrument_future, __tracing_attr_span ) @@ -344,7 +344,7 @@ fn gen_block( // regression in case the level is enabled. let __tracing_attr_span; let __tracing_attr_guard; - if tracing::level_enabled!(#level) || tracing::if_log_enabled!(#level, {true} else {false}) { + if ::tracing::level_enabled!(#level) || ::tracing::if_log_enabled!(#level, {true} else {false}) { __tracing_attr_span = #span; #follows_from __tracing_attr_guard = __tracing_attr_span.enter(); @@ -724,7 +724,7 @@ impl<'block> AsyncInfo<'block> { let async_attrs = &async_expr.attrs; if pinned_box { quote! { - Box::pin(#(#async_attrs) * async move { #instrumented_block }) + ::std::boxed::Box::pin(#(#async_attrs) * async move { #instrumented_block }) } } else { quote! { diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index 402be8572a..4c1b1ecf1f 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -324,3 +324,21 @@ fn target_name_ident() { handle.assert_finished(); } + +#[test] +fn user_tracing_module() { + use ::tracing::field::Empty; + + // Reproduces https://github.com/tokio-rs/tracing/issues/3119 + #[instrument(fields(f = Empty))] + fn my_fn() { + assert_eq!("test", tracing::my_other_fn()); + } + + mod tracing { + #[allow(dead_code)] + pub fn my_other_fn() -> &'static str { + "test" + } + } +} From 5366628cc208aaf9469301da84f1f176a613c4ea Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Fri, 30 May 2025 10:43:38 +0200 Subject: [PATCH 21/24] chore: fix doc issues (#3292) There are a few warnings when building the docs causing failures on CI. This change fixes those and adds `unsound_local_offset` as an unexpeced cfg. --- Cargo.toml | 2 +- tracing-appender/src/lib.rs | 12 ++++++------ tracing-appender/src/non_blocking.rs | 20 +++++++------------- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1624740e6c..282f93bb75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,4 +22,4 @@ members = [ # This will be ignored with Rust older than 1.74, but for now that's okay; # we're only using it to fix check-cfg issues that first appeared in Rust 1.80. [workspace.lints.rust] -unexpected_cfgs = { level = "warn", check-cfg = ["cfg(flaky_tests)", "cfg(tracing_unstable)"] } +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(flaky_tests)", "cfg(tracing_unstable)", "cfg(unsound_local_offset)"] } diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs index 66ad227ee6..7ee4ae2d85 100644 --- a/tracing-appender/src/lib.rs +++ b/tracing-appender/src/lib.rs @@ -21,9 +21,9 @@ //! ``` //! //! This crate can be used in a few ways to record spans/events: -//! - Using a [`RollingFileAppender`][rolling_struct] to perform writes to a log file. This will block on writes. -//! - Using *any* type implementing [`std::io::Write`][write] in a non-blocking fashion. -//! - Using a combination of [`NonBlocking`][non_blocking] and [`RollingFileAppender`][rolling_struct] to allow writes to a log file +//! - Using a [`RollingFileAppender`] to perform writes to a log file. This will block on writes. +//! - Using *any* type implementing [`std::io::Write`] in a non-blocking fashion. +//! - Using a combination of [`NonBlocking`] and [`RollingFileAppender`] to allow writes to a log file //! without blocking. //! //! ## File Appender @@ -48,7 +48,7 @@ //! # } //! ``` //! -//! The file appender implements [`std::io::Write`][write]. To be used with +//! The file appender implements [`std::io::Write`]. To be used with //! [`tracing_subscriber::FmtSubscriber`][fmt_subscriber], it must be combined with a //! [`MakeWriter`][make_writer] implementation to be able to record tracing spans/event. //! @@ -68,12 +68,12 @@ //! .init(); //! # } //! ``` -//! **Note:** `_guard` is a [`WorkerGuard`][guard] which is returned by [`tracing_appender::non_blocking`][non_blocking] +//! **Note:** `_guard` is a [`WorkerGuard`] which is returned by [`tracing_appender::non_blocking`][non_blocking] //! to ensure buffered logs are flushed to their output in the case of abrupt terminations of a process. //! See [`WorkerGuard` module][guard] for more details. //! //! The example below demonstrates the construction of a [`tracing_appender::non_blocking`][non_blocking] -//! writer constructed with a [`std::io::Write`][write]: +//! writer constructed with a [`std::io::Write`]: //! //! ```rust //! use std::io::Error; diff --git a/tracing-appender/src/non_blocking.rs b/tracing-appender/src/non_blocking.rs index fe43ecc0af..50d204a4bb 100644 --- a/tracing-appender/src/non_blocking.rs +++ b/tracing-appender/src/non_blocking.rs @@ -31,9 +31,7 @@ //! Unintentional drops of `WorkerGuard` remove the guarantee that logs will be flushed //! during a program's termination, in a panic or otherwise. //! -//! See [`WorkerGuard`][worker_guard] for examples of using the guard. -//! -//! [worker_guard]: WorkerGuard +//! See [`WorkerGuard`] for examples of using the guard. //! //! # Examples //! @@ -60,12 +58,11 @@ use tracing_subscriber::fmt::MakeWriter; /// The default maximum number of buffered log lines. /// -/// If [`NonBlocking`][non-blocking] is lossy, it will drop spans/events at capacity. -/// If [`NonBlocking`][non-blocking] is _not_ lossy, -/// backpressure will be exerted on senders, causing them to block their -/// respective threads until there is available capacity. +/// If [`NonBlocking`] is lossy, it will drop spans/events at capacity. +/// If [`NonBlocking`] is _not_ lossy, backpressure will be exerted on +/// senders, causing them to block their respective threads until there +/// is available capacity. /// -/// [non-blocking]: NonBlocking /// Recommended to be a power of 2. pub const DEFAULT_BUFFERED_LINES_LIMIT: usize = 128_000; @@ -116,11 +113,10 @@ pub struct WorkerGuard { /// `NonBlocking` moves the writing out of an application's data path by sending spans and events /// to a dedicated logging thread. /// -/// This struct implements [`MakeWriter`][make_writer] from the `tracing-subscriber` +/// This struct implements [`MakeWriter`] from the `tracing-subscriber` /// crate. Therefore, it can be used with the [`tracing_subscriber::fmt`][fmt] module /// or with any other subscriber/layer implementation that uses the `MakeWriter` trait. /// -/// [make_writer]: tracing_subscriber::fmt::MakeWriter /// [fmt]: mod@tracing_subscriber::fmt #[derive(Clone, Debug)] pub struct NonBlocking { @@ -184,9 +180,7 @@ impl NonBlocking { } } -/// A builder for [`NonBlocking`][non-blocking]. -/// -/// [non-blocking]: NonBlocking +/// A builder for [`NonBlocking`]. #[derive(Debug)] pub struct NonBlockingBuilder { buffered_lines_limit: usize, From 0c5af12912fe15389b731653d61eeafd4722ad42 Mon Sep 17 00:00:00 2001 From: Rebecca Turner Date: Fri, 30 May 2025 02:23:44 -0700 Subject: [PATCH 22/24] Let `dead_code` lint work on `#[instrument]`ed functions (#3108) Closes #1366 --- tracing-attributes/src/expand.rs | 49 ++++++++++----- tracing-attributes/src/lib.rs | 15 ++++- tracing-attributes/tests/async_fn.rs | 5 ++ tracing-attributes/tests/dead_code.rs | 10 +++ tracing-attributes/tests/err.rs | 1 + tracing-attributes/tests/instrument.rs | 2 + .../tests/ui/async_instrument.rs | 1 - .../tests/ui/async_instrument.stderr | 63 ++++++++++--------- 8 files changed, 99 insertions(+), 47 deletions(-) create mode 100644 tracing-attributes/tests/dead_code.rs diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 69db03cbeb..947d4fe3ca 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -1,6 +1,7 @@ use std::iter; use proc_macro2::TokenStream; +use quote::TokenStreamExt; use quote::{quote, quote_spanned, ToTokens}; use syn::visit_mut::VisitMut; use syn::{ @@ -29,6 +30,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( inner_attrs, vis, sig, + brace_token, block, } = input; @@ -44,9 +46,12 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( syn::Generics { params: gen_params, where_clause, - .. + lt_token, + gt_token, }, - .. + fn_token, + paren_token, + variadic, } = sig; let warnings = args.warnings(); @@ -65,9 +70,14 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( // exactly that way for it to do its magic. let fake_return_edge = quote_spanned! {return_span=> #[allow( - unknown_lints, unreachable_code, clippy::diverging_sub_expression, - clippy::let_unit_value, clippy::unreachable, clippy::let_with_type_underscore, - clippy::empty_loop + unknown_lints, + unreachable_code, + clippy::diverging_sub_expression, + clippy::empty_loop, + clippy::let_unit_value, + clippy::let_with_type_underscore, + clippy::needless_return, + clippy::unreachable )] if false { let __tracing_attr_fake_return: #return_type = loop {}; @@ -90,16 +100,27 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( self_type, ); - quote!( + let mut result = quote!( #(#outer_attrs) * - #vis #constness #asyncness #unsafety #abi fn #ident<#gen_params>(#params) #output - #where_clause - { - #(#inner_attrs) * - #warnings - #body - } - ) + #vis #constness #asyncness #unsafety #abi #fn_token #ident + #lt_token #gen_params #gt_token + ); + + paren_token.surround(&mut result, |tokens| { + params.to_tokens(tokens); + variadic.to_tokens(tokens); + }); + + output.to_tokens(&mut result); + where_clause.to_tokens(&mut result); + + brace_token.surround(&mut result, |tokens| { + tokens.append_all(inner_attrs); + warnings.to_tokens(tokens); + body.to_tokens(tokens); + }); + + result } /// Instrument a block diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 67d7c7c21e..10146867d0 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -83,8 +83,10 @@ extern crate proc_macro; use proc_macro2::TokenStream; +use quote::TokenStreamExt; use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream}; +use syn::token::Brace; use syn::{Attribute, ItemFn, Signature, Visibility}; mod attr; @@ -628,6 +630,7 @@ struct MaybeItemFn { inner_attrs: Vec, vis: Visibility, sig: Signature, + brace_token: Brace, block: TokenStream, } @@ -638,6 +641,7 @@ impl MaybeItemFn { inner_attrs: &self.inner_attrs, vis: &self.vis, sig: &self.sig, + brace_token: &self.brace_token, block: &self.block, } } @@ -651,12 +655,15 @@ impl Parse for MaybeItemFn { let vis: Visibility = input.parse()?; let sig: Signature = input.parse()?; let inner_attrs = input.call(Attribute::parse_inner)?; - let block: TokenStream = input.parse()?; + let block; + let brace_token = syn::braced!(block in input); + let block: TokenStream = block.call(|buffer| buffer.parse())?; Ok(Self { outer_attrs, inner_attrs, vis, sig, + brace_token, block, }) } @@ -674,12 +681,15 @@ impl From for MaybeItemFn { let (outer_attrs, inner_attrs) = attrs .into_iter() .partition(|attr| attr.style == syn::AttrStyle::Outer); + let mut block_tokens = TokenStream::new(); + block_tokens.append_all(block.stmts); Self { outer_attrs, inner_attrs, vis, sig, - block: block.to_token_stream(), + brace_token: block.brace_token, + block: block_tokens, } } } @@ -692,5 +702,6 @@ struct MaybeItemFnRef<'a, B: ToTokens> { inner_attrs: &'a Vec, vis: &'a Visibility, sig: &'a Signature, + brace_token: &'a Brace, block: &'a B, } diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs index 365a72cb4e..c672ed62f2 100644 --- a/tracing-attributes/tests/async_fn.rs +++ b/tracing-attributes/tests/async_fn.rs @@ -30,14 +30,17 @@ async fn test_ret_impl_trait_err(n: i32) -> Result, &' } #[instrument] +#[allow(dead_code)] async fn test_async_fn_empty() {} #[instrument] +#[allow(dead_code)] async unsafe fn test_async_unsafe_fn_empty() {} // Reproduces a compile error when an instrumented function body contains inner // attributes (https://github.com/tokio-rs/tracing/issues/2294). #[deny(unused_variables)] +#[allow(dead_code, clippy::mixed_attributes_style)] #[instrument] async fn repro_async_2294() { #![allow(unused_variables)] @@ -50,6 +53,7 @@ async fn repro_async_2294() { // with the rustfmt-generated formatting, the lint will not be triggered! #[rustfmt::skip] #[deny(clippy::suspicious_else_formatting)] +#[allow(dead_code)] async fn repro_1613(var: bool) { println!( "{}", @@ -61,6 +65,7 @@ async fn repro_1613(var: bool) { // and https://github.com/rust-lang/rust-clippy/issues/7760 #[instrument] #[deny(clippy::suspicious_else_formatting)] +#[allow(dead_code)] async fn repro_1613_2() { // hello world // else diff --git a/tracing-attributes/tests/dead_code.rs b/tracing-attributes/tests/dead_code.rs new file mode 100644 index 0000000000..6a3e05d4a2 --- /dev/null +++ b/tracing-attributes/tests/dead_code.rs @@ -0,0 +1,10 @@ +use tracing_attributes::instrument; + +#[deny(unfulfilled_lint_expectations)] +#[expect(dead_code)] +#[instrument] +fn unused() {} + +#[expect(dead_code)] +#[instrument] +async fn unused_async() {} diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs index b2c5339c56..7cc7ada0dd 100644 --- a/tracing-attributes/tests/err.rs +++ b/tracing-attributes/tests/err.rs @@ -15,6 +15,7 @@ fn err() -> Result { } #[instrument(err)] +#[allow(dead_code)] fn err_suspicious_else() -> Result { {} u8::try_from(1234) diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index 4c1b1ecf1f..193044d780 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -6,6 +6,7 @@ use tracing_mock::*; // Reproduces a compile error when an instrumented function body contains inner // attributes (https://github.com/tokio-rs/tracing/issues/2294). #[deny(unused_variables)] +#[allow(dead_code, clippy::mixed_attributes_style)] #[instrument] fn repro_2294() { #![allow(unused_variables)] @@ -331,6 +332,7 @@ fn user_tracing_module() { // Reproduces https://github.com/tokio-rs/tracing/issues/3119 #[instrument(fields(f = Empty))] + #[allow(dead_code)] fn my_fn() { assert_eq!("test", tracing::my_other_fn()); } diff --git a/tracing-attributes/tests/ui/async_instrument.rs b/tracing-attributes/tests/ui/async_instrument.rs index 5b245746a6..03b3e18afb 100644 --- a/tracing-attributes/tests/ui/async_instrument.rs +++ b/tracing-attributes/tests/ui/async_instrument.rs @@ -10,7 +10,6 @@ async fn simple_mismatch() -> String { "" } -// FIXME: this span is still pretty poor #[tracing::instrument] async fn opaque_unsatisfied() -> impl std::fmt::Display { ("",) diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr index e0aa0d6294..4debb83002 100644 --- a/tracing-attributes/tests/ui/async_instrument.stderr +++ b/tracing-attributes/tests/ui/async_instrument.stderr @@ -25,77 +25,80 @@ note: return type inferred to be `String` here | ^^^^^^ error[E0277]: `(&str,)` doesn't implement `std::fmt::Display` - --> tests/ui/async_instrument.rs:14:1 + --> tests/ui/async_instrument.rs:14:57 | -14 | #[tracing::instrument] - | ^^^^^^^^^^^^^^^^^^^^^^ - | | - | `(&str,)` cannot be formatted with the default formatter - | return type was inferred to be `(&str,)` here +14 | async fn opaque_unsatisfied() -> impl std::fmt::Display { + | _________________________________________________________- +15 | | ("",) +16 | | } + | | ^ + | | | + | |_`(&str,)` cannot be formatted with the default formatter + | return type was inferred to be `(&str,)` here | = help: the trait `std::fmt::Display` is not implemented for `(&str,)` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead - = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `(&str,)` doesn't implement `std::fmt::Display` - --> tests/ui/async_instrument.rs:15:34 + --> tests/ui/async_instrument.rs:14:34 | -15 | async fn opaque_unsatisfied() -> impl std::fmt::Display { +14 | async fn opaque_unsatisfied() -> impl std::fmt::Display { | ^^^^^^^^^^^^^^^^^^^^^^ `(&str,)` cannot be formatted with the default formatter | = help: the trait `std::fmt::Display` is not implemented for `(&str,)` = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead error[E0308]: mismatched types - --> tests/ui/async_instrument.rs:23:5 + --> tests/ui/async_instrument.rs:22:5 | -23 | "" +22 | "" | ^^ expected `Wrapper<_>`, found `&str` | = note: expected struct `Wrapper<_>` found reference `&'static str` note: return type inferred to be `Wrapper<_>` here - --> tests/ui/async_instrument.rs:22:36 + --> tests/ui/async_instrument.rs:21:36 | -22 | async fn mismatch_with_opaque() -> Wrapper { +21 | async fn mismatch_with_opaque() -> Wrapper { | ^^^^^^^ help: try wrapping the expression in `Wrapper` | -23 | Wrapper("") +22 | Wrapper("") | ++++++++ + error[E0308]: mismatched types - --> tests/ui/async_instrument.rs:29:16 + --> tests/ui/async_instrument.rs:28:16 | -29 | return ""; +28 | return ""; | ^^ expected `()`, found `&str` | note: return type inferred to be `()` here - --> tests/ui/async_instrument.rs:27:10 + --> tests/ui/async_instrument.rs:26:10 | -27 | async fn early_return_unit() { +26 | async fn early_return_unit() { | ^^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> tests/ui/async_instrument.rs:36:16 + --> tests/ui/async_instrument.rs:35:16 | -36 | return ""; +35 | return ""; | ^^- help: try using a conversion method: `.to_string()` | | | expected `String`, found `&str` | note: return type inferred to be `String` here - --> tests/ui/async_instrument.rs:34:28 + --> tests/ui/async_instrument.rs:33:28 | -34 | async fn early_return() -> String { +33 | async fn early_return() -> String { | ^^^^^^ error[E0308]: mismatched types - --> tests/ui/async_instrument.rs:42:35 - | -42 | async fn extra_semicolon() -> i32 { - | ___________________________________^ -43 | | 1; - | | - help: remove this semicolon to return this value -44 | | } - | |_^ expected `i32`, found `()` + --> tests/ui/async_instrument.rs:40:1 + | +40 | #[tracing::instrument] + | ^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `()` +41 | async fn extra_semicolon() -> i32 { +42 | 1; + | - help: remove this semicolon to return this value + | + = note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info) From 4fbd37738daaceaa33d06203716c0c58cbef839a Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Fri, 30 May 2025 11:35:40 +0200 Subject: [PATCH 23/24] fix: Do not compare references to pointers to compare pointers (#3236) `self` and `other` are references, and the `ptr::eq()` call intends to determine if they designate the same object. Putting them behind another level of reference will always return `false`, as those short-lived references will be compared instead. --- tracing-core/src/metadata.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 6ba9dbecff..281b62582e 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -445,7 +445,7 @@ impl Eq for Metadata<'_> {} impl PartialEq for Metadata<'_> { #[inline] fn eq(&self, other: &Self) -> bool { - if core::ptr::eq(&self, &other) { + if core::ptr::eq(self, other) { true } else if cfg!(not(debug_assertions)) { // In a well-behaving application, two `Metadata` can be assumed to From 49bde06f2c10ba906aaa095b40d5e397d6c73c6a Mon Sep 17 00:00:00 2001 From: Nick Caplinger Date: Fri, 30 May 2025 09:06:36 -0500 Subject: [PATCH 24/24] appender: introduce weekly rotation (#3218) ## Motivation While configuring tracing-appender, I wanted to specify a weekly log rotation interval. I was unable to do so, as the largest rotation interval was daily. ## Solution Before my introduction of weekly log rotation, rounding the current `OffsetDateTime` was straightforward: we could simply keep the current date and truncate part or all of the time component. However, we cannot simply truncate the time with weekly rotation; the date must now be modified. To round the date, we roll logs at 00:00 UTC on Sunday. This gives us consistent date-times that only change weekly. --- tracing-appender/src/rolling.rs | 185 +++++++++++++++++++++++++++++--- 1 file changed, 172 insertions(+), 13 deletions(-) diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs index 80b47b5463..fca1c585e8 100644 --- a/tracing-appender/src/rolling.rs +++ b/tracing-appender/src/rolling.rs @@ -362,7 +362,7 @@ pub fn hourly( /// } /// ``` /// -/// This will result in a log file located at `/some/path/rolling.log.yyyy-MM-dd-HH`. +/// This will result in a log file located at `/some/path/rolling.log.yyyy-MM-dd`. pub fn daily( directory: impl AsRef, file_name_prefix: impl AsRef, @@ -370,6 +370,42 @@ pub fn daily( RollingFileAppender::new(Rotation::DAILY, directory, file_name_prefix) } +/// Creates a weekly-rotating file appender. The logs will rotate every Sunday at midnight UTC. +/// +/// The appender returned by `rolling::weekly` can be used with `non_blocking` to create +/// a non-blocking, weekly file appender. +/// +/// A `RollingFileAppender` has a fixed rotation whose frequency is +/// defined by [`Rotation`][self::Rotation]. The `directory` and +/// `file_name_prefix` arguments determine the location and file name's _prefix_ +/// of the log file. `RollingFileAppender` automatically appends the current date in UTC. +/// +/// # Examples +/// +/// ``` rust +/// # #[clippy::allow(needless_doctest_main)] +/// fn main () { +/// # fn doc() { +/// let appender = tracing_appender::rolling::weekly("/some/path", "rolling.log"); +/// let (non_blocking_appender, _guard) = tracing_appender::non_blocking(appender); +/// +/// let subscriber = tracing_subscriber::fmt().with_writer(non_blocking_appender); +/// +/// tracing::subscriber::with_default(subscriber.finish(), || { +/// tracing::event!(tracing::Level::INFO, "Hello"); +/// }); +/// # } +/// } +/// ``` +/// +/// This will result in a log file located at `/some/path/rolling.log.yyyy-MM-dd`. +pub fn weekly( + directory: impl AsRef, + file_name_prefix: impl AsRef, +) -> RollingFileAppender { + RollingFileAppender::new(Rotation::WEEKLY, directory, file_name_prefix) +} + /// Creates a non-rolling file appender. /// /// The appender returned by `rolling::never` can be used with `non_blocking` to create @@ -429,6 +465,14 @@ pub fn never(directory: impl AsRef, file_name: impl AsRef) -> Rollin /// # } /// ``` /// +/// ### Weekly Rotation +/// ```rust +/// # fn docs() { +/// use tracing_appender::rolling::Rotation; +/// let rotation = tracing_appender::rolling::Rotation::WEEKLY; +/// # } +/// ``` +/// /// ### No Rotation /// ```rust /// # fn docs() { @@ -444,31 +488,40 @@ enum RotationKind { Minutely, Hourly, Daily, + Weekly, Never, } impl Rotation { - /// Provides an minutely rotation + /// Provides a minutely rotation. pub const MINUTELY: Self = Self(RotationKind::Minutely); - /// Provides an hourly rotation + /// Provides an hourly rotation. pub const HOURLY: Self = Self(RotationKind::Hourly); - /// Provides a daily rotation + /// Provides a daily rotation. pub const DAILY: Self = Self(RotationKind::Daily); + /// Provides a weekly rotation that rotates every Sunday at midnight UTC. + pub const WEEKLY: Self = Self(RotationKind::Weekly); /// Provides a rotation that never rotates. pub const NEVER: Self = Self(RotationKind::Never); + /// Determines the next date that we should round to or `None` if `self` uses [`Rotation::NEVER`]. pub(crate) fn next_date(&self, current_date: &OffsetDateTime) -> Option { let unrounded_next_date = match *self { Rotation::MINUTELY => *current_date + Duration::minutes(1), Rotation::HOURLY => *current_date + Duration::hours(1), Rotation::DAILY => *current_date + Duration::days(1), + Rotation::WEEKLY => *current_date + Duration::weeks(1), Rotation::NEVER => return None, }; - Some(self.round_date(&unrounded_next_date)) + Some(self.round_date(unrounded_next_date)) } - // note that this method will panic if passed a `Rotation::NEVER`. - pub(crate) fn round_date(&self, date: &OffsetDateTime) -> OffsetDateTime { + /// Rounds the date towards the past using the [`Rotation`] interval. + /// + /// # Panics + /// + /// This method will panic if `self`` uses [`Rotation::NEVER`]. + pub(crate) fn round_date(&self, date: OffsetDateTime) -> OffsetDateTime { match *self { Rotation::MINUTELY => { let time = Time::from_hms(date.hour(), date.minute(), 0) @@ -485,6 +538,14 @@ impl Rotation { .expect("Invalid time; this is a bug in tracing-appender"); date.replace_time(time) } + Rotation::WEEKLY => { + let zero_time = Time::from_hms(0, 0, 0) + .expect("Invalid time; this is a bug in tracing-appender"); + + let days_since_sunday = date.weekday().number_days_from_sunday(); + let date = date - Duration::days(days_since_sunday.into()); + date.replace_time(zero_time) + } // Rotation::NEVER is impossible to round. Rotation::NEVER => { unreachable!("Rotation::NEVER is impossible to round.") @@ -497,6 +558,7 @@ impl Rotation { Rotation::MINUTELY => format_description::parse("[year]-[month]-[day]-[hour]-[minute]"), Rotation::HOURLY => format_description::parse("[year]-[month]-[day]-[hour]"), Rotation::DAILY => format_description::parse("[year]-[month]-[day]"), + Rotation::WEEKLY => format_description::parse("[year]-[month]-[day]"), Rotation::NEVER => format_description::parse("[year]-[month]-[day]"), } .expect("Unable to create a formatter; this is a bug in tracing-appender") @@ -548,10 +610,17 @@ impl Inner { Ok((inner, writer)) } + /// Returns the full filename for the provided date, using [`Rotation`] to round accordingly. pub(crate) fn join_date(&self, date: &OffsetDateTime) -> String { - let date = date - .format(&self.date_format) - .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender"); + let date = if let Rotation::NEVER = self.rotation { + date.format(&self.date_format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") + } else { + self.rotation + .round_date(*date) + .format(&self.date_format) + .expect("Unable to format OffsetDateTime; this is a bug in tracing-appender") + }; match ( &self.rotation, @@ -748,7 +817,7 @@ mod test { #[test] fn write_minutely_log() { - test_appender(Rotation::HOURLY, "minutely.log"); + test_appender(Rotation::MINUTELY, "minutely.log"); } #[test] @@ -761,6 +830,11 @@ mod test { test_appender(Rotation::DAILY, "daily.log"); } + #[test] + fn write_weekly_log() { + test_appender(Rotation::WEEKLY, "weekly.log"); + } + #[test] fn write_never_log() { test_appender(Rotation::NEVER, "never.log"); @@ -778,24 +852,109 @@ mod test { let next = Rotation::HOURLY.next_date(&now).unwrap(); assert_eq!((now + Duration::HOUR).hour(), next.hour()); - // daily-basis + // per-day basis let now = OffsetDateTime::now_utc(); let next = Rotation::DAILY.next_date(&now).unwrap(); assert_eq!((now + Duration::DAY).day(), next.day()); + // per-week basis + let now = OffsetDateTime::now_utc(); + let now_rounded = Rotation::WEEKLY.round_date(now); + let next = Rotation::WEEKLY.next_date(&now).unwrap(); + assert!(now_rounded < next); + // never let now = OffsetDateTime::now_utc(); let next = Rotation::NEVER.next_date(&now); assert!(next.is_none()); } + #[test] + fn test_join_date() { + struct TestCase { + expected: &'static str, + rotation: Rotation, + prefix: Option<&'static str>, + suffix: Option<&'static str>, + now: OffsetDateTime, + } + + let format = format_description::parse( + "[year]-[month]-[day] [hour]:[minute]:[second] [offset_hour \ + sign:mandatory]:[offset_minute]:[offset_second]", + ) + .unwrap(); + let directory = tempfile::tempdir().expect("failed to create tempdir"); + + let test_cases = vec![ + TestCase { + expected: "my_prefix.2025-02-16.log", + rotation: Rotation::WEEKLY, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-02-17 10:01:00 +00:00:00", &format).unwrap(), + }, + // Make sure weekly rotation rounds to the preceding year when appropriate + TestCase { + expected: "my_prefix.2024-12-29.log", + rotation: Rotation::WEEKLY, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-01-01 10:01:00 +00:00:00", &format).unwrap(), + }, + TestCase { + expected: "my_prefix.2025-02-17.log", + rotation: Rotation::DAILY, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-02-17 10:01:00 +00:00:00", &format).unwrap(), + }, + TestCase { + expected: "my_prefix.2025-02-17-10.log", + rotation: Rotation::HOURLY, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-02-17 10:01:00 +00:00:00", &format).unwrap(), + }, + TestCase { + expected: "my_prefix.2025-02-17-10-01.log", + rotation: Rotation::MINUTELY, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-02-17 10:01:00 +00:00:00", &format).unwrap(), + }, + TestCase { + expected: "my_prefix.log", + rotation: Rotation::NEVER, + prefix: Some("my_prefix"), + suffix: Some("log"), + now: OffsetDateTime::parse("2025-02-17 10:01:00 +00:00:00", &format).unwrap(), + }, + ]; + + for test_case in test_cases { + let (inner, _) = Inner::new( + test_case.now, + test_case.rotation.clone(), + directory.path(), + test_case.prefix.map(ToString::to_string), + test_case.suffix.map(ToString::to_string), + None, + ) + .unwrap(); + let path = inner.join_date(&test_case.now); + + assert_eq!(path, test_case.expected); + } + } + #[test] #[should_panic( expected = "internal error: entered unreachable code: Rotation::NEVER is impossible to round." )] fn test_never_date_rounding() { let now = OffsetDateTime::now_utc(); - let _ = Rotation::NEVER.round_date(&now); + let _ = Rotation::NEVER.round_date(now); } #[test]