From 2ace0b56c144019ffd9f4503336526630ce580b7 Mon Sep 17 00:00:00 2001 From: rwa Date: Wed, 8 Feb 2023 19:25:22 +0100 Subject: [PATCH 001/101] docs: fix warning styles in html-in-header (#2449) ## Motivation The documentation under https://tracing.rs/tracing/ is missing CSS styles. Fixes #2444 ## Solution Used the `--html-in-header` rustdoc flag instead of `--extend-css`. Converted the css file into an html file. --- assets/{warning.css => warning-css.html} | 14 ++++++++------ netlify.toml | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) rename assets/{warning.css => warning-css.html} (84%) diff --git a/assets/warning.css b/assets/warning-css.html similarity index 84% rename from assets/warning.css rename to assets/warning-css.html index 50a79b4900..5d92b1bc9d 100644 --- a/assets/warning.css +++ b/assets/warning-css.html @@ -1,4 +1,5 @@ -#tracing-warning-header { + diff --git a/netlify.toml b/netlify.toml index e82b88878f..a59c675e27 100644 --- a/netlify.toml +++ b/netlify.toml @@ -12,7 +12,7 @@ --cfg docsrs \ --html-before-content /opt/build/repo/assets/warning.html \ --html-in-header /opt/build/repo/assets/noindex.html \ - --extend-css /opt/build/repo/assets/warning.css \ + --html-in-header /opt/build/repo/assets/warning-css.html \ """ [[redirects]] From 52fbf75818900dd9132776434c5f4392a5b11318 Mon Sep 17 00:00:00 2001 From: Scott Robinson Date: Thu, 9 Feb 2023 05:26:13 +1100 Subject: [PATCH 002/101] attributes: document default level for `err` (#2433) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation The current description for the default level of the `err` return value event _strongly implies_ it's the same as the span. However, the implementation actually defaults to `ERROR`. ## Solution This PR documents that, so future generations don't have to chase down the truth, like I did. 😉 --- tracing-attributes/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 51e94f80e3..4b92520deb 100644 --- a/tracing-attributes/src/lib.rs +++ b/tracing-attributes/src/lib.rs @@ -270,6 +270,8 @@ mod expand; /// } /// ``` /// +/// The level of the error value event defaults to `ERROR`. +/// /// Similarly, overriding the level of the `err` event : /// /// ``` From a0126b2e2d465e8e6d514acdf128fcef5b863d27 Mon Sep 17 00:00:00 2001 From: Arif Driessen Date: Wed, 8 Feb 2023 19:26:50 +0100 Subject: [PATCH 003/101] docs: fix typo in `panic_hook` example (#2453) --- examples/examples/panic_hook.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples/panic_hook.rs b/examples/examples/panic_hook.rs index 1c86f6d060..5bfd77355c 100644 --- a/examples/examples/panic_hook.rs +++ b/examples/examples/panic_hook.rs @@ -30,7 +30,7 @@ fn main() { // On nightly Rust, where the `PanicInfo` type also exposes a // `message()` method returning just the message, we could record // just the message instead of the entire `fmt::Display` - // implementation, avoiding the duplciated location + // implementation, avoiding the duplicated location tracing::error!( message = %panic, panic.file = location.file(), From 666537c7d7bd03b1b3d9d317662c79be10b7b1bc Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 4 Apr 2023 19:18:53 -0400 Subject: [PATCH 004/101] chore: move tracing-opentelemetry to its own repo (#2523) This PR removes tracing-opentelemetry to a dedicated repo located at https://github.com/tokio-rs/tracing-opentelemetry. (Note that at time of writing this PR, the new repo has not be made public). We're moving tracing-opentelemetry to a dedicated repository for the following reasons: 1. opentelemetry's MSRV is higher than that of `tracing`'s. 2. more importantly, the main `tracing` repo is getting a bit unweildy and it feels unreasonable to maintain backports for crates that integrate with the larger tracing ecosystem. (https://github.com/tokio-rs/tracing-opentelemetry does not have the examples present in this repo; this will occur in a PR that will be linked from _this_ PR.) --- .github/CODEOWNERS | 3 - .github/workflows/CI.yml | 8 - Cargo.toml | 1 - README.md | 6 - examples/Cargo.toml | 5 - examples/README.md | 6 - .../examples/opentelemetry-remote-context.rs | 46 - examples/examples/opentelemetry.rs | 49 - tracing-opentelemetry/CHANGELOG.md | 167 -- tracing-opentelemetry/Cargo.toml | 55 - tracing-opentelemetry/LICENSE | 25 - tracing-opentelemetry/README.md | 132 -- tracing-opentelemetry/benches/trace.rs | 126 -- tracing-opentelemetry/src/lib.rs | 144 -- tracing-opentelemetry/src/metrics.rs | 366 ----- tracing-opentelemetry/src/span_ext.rs | 170 -- tracing-opentelemetry/src/subscriber.rs | 1413 ----------------- tracing-opentelemetry/src/tracer.rs | 233 --- .../tests/metrics_publishing.rs | 282 ---- .../tests/trace_state_propagation.rs | 171 -- tracing-opentelemetry/trace.png | Bin 87211 -> 0 bytes tracing-subscriber/src/filter/targets.rs | 2 +- 22 files changed, 1 insertion(+), 3409 deletions(-) delete mode 100644 examples/examples/opentelemetry-remote-context.rs delete mode 100644 examples/examples/opentelemetry.rs delete mode 100644 tracing-opentelemetry/CHANGELOG.md delete mode 100644 tracing-opentelemetry/Cargo.toml delete mode 100644 tracing-opentelemetry/LICENSE delete mode 100644 tracing-opentelemetry/README.md delete mode 100644 tracing-opentelemetry/benches/trace.rs delete mode 100644 tracing-opentelemetry/src/lib.rs delete mode 100644 tracing-opentelemetry/src/metrics.rs delete mode 100644 tracing-opentelemetry/src/span_ext.rs delete mode 100644 tracing-opentelemetry/src/subscriber.rs delete mode 100644 tracing-opentelemetry/src/tracer.rs delete mode 100644 tracing-opentelemetry/tests/metrics_publishing.rs delete mode 100644 tracing-opentelemetry/tests/trace_state_propagation.rs delete mode 100644 tracing-opentelemetry/trace.png diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 191b2b16fc..83ac15158d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -11,8 +11,5 @@ # David contributed the Registry implementation. /tracing-subscriber/registry @davidbarsky @hawkw @tokio-rs/tracing -# Julian contributed the OpenTelemetry implementation. -/tracing-opentelemetry/ @jtescher @tokio-rs/tracing - # Zeki contributed the TracingAppender implementation /tracing-appender/ @zekisherif @tokio-rs/tracing diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 52ce13bffe..98dbf24b17 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -85,13 +85,11 @@ jobs: - tracing-macros - tracing-serde - tracing-tower - - tracing-opentelemetry - tracing - tracing-subscriber steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - - name: install cargo-hack uses: taiki-e/install-action@cargo-hack - name: cargo hack check @@ -143,7 +141,6 @@ jobs: - tracing-serde - tracing-subscriber - tracing-tower - - tracing-opentelemetry - tracing toolchain: - 1.49.0 @@ -155,15 +152,11 @@ jobs: toolchain: 1.49.0 - subcrate: tracing-subscriber toolchain: 1.49.0 - - subcrate: tracing-opentelemetry - toolchain: 1.49.0 include: - subcrate: tracing-appender toolchain: 1.53.0 - subcrate: tracing-subscriber toolchain: 1.50.0 - - subcrate: tracing-opentelemetry - toolchain: 1.56.0 steps: - uses: actions/checkout@v3 - name: install Rust nightly @@ -266,7 +259,6 @@ jobs: - tracing-journald - tracing-log - tracing-macros - - tracing-opentelemetry - tracing-serde - tracing-subscriber - tracing-tower diff --git a/Cargo.toml b/Cargo.toml index 35d6147776..d272eef50a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ members = [ "tracing-log", "tracing-macros", "tracing-mock", - "tracing-opentelemetry", "tracing-subscriber", "tracing-serde", "tracing-appender", diff --git a/README.md b/README.md index 246c9f964b..e366458c46 100644 --- a/README.md +++ b/README.md @@ -307,11 +307,6 @@ The crates included as part of Tracing are: * [`tracing-log`]: Compatibility with the `log` crate (unstable). -* [`tracing-opentelemetry`]: Provides a layer that connects spans from multiple - systems into a trace and emits them to [OpenTelemetry]-compatible distributed - tracing systems for processing and visualization. - ([crates.io][otel-crates]|[docs][otel-docs]) - * [`tracing-serde`]: A compatibility layer for serializing trace data with `serde` (unstable). @@ -339,7 +334,6 @@ The crates included as part of Tracing are: [`tracing-macros`]: tracing-macros [`tracing-attributes`]: tracing-attributes [`tracing-log`]: tracing-log -[`tracing-opentelemetry`]: tracing-opentelemetry [`tracing-serde`]: tracing-serde [`tracing-subscriber`]: tracing-subscriber [`tracing-tower`]: tracing-tower diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 4d33691a3a..42e366d6c5 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -21,7 +21,6 @@ tracing-futures = { version = "0.3", path = "../tracing-futures", features = ["f tracing-attributes = { path = "../tracing-attributes", version = "0.2"} tracing-log = { path = "../tracing-log", version = "0.2", features = ["env_logger"] } tracing-serde = { path = "../tracing-serde" } -tracing-opentelemetry = { path = "../tracing-opentelemetry" } tracing-appender = { path = "../tracing-appender" } tracing-journald = { path = "../tracing-journald" } @@ -51,10 +50,6 @@ log = "0.4.17" inferno = "0.11.6" tempfile = "3.3.0" -# opentelemetry example -opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] } -opentelemetry-jaeger = "0.17.0" - # fmt examples snafu = "0.6.10" thiserror = "1.0.31" diff --git a/examples/README.md b/examples/README.md index 30f2ac7f87..c589edbbf7 100644 --- a/examples/README.md +++ b/examples/README.md @@ -65,12 +65,6 @@ This directory contains a collection of examples that demonstrate the use of the unstructured logs from dependencies as `tracing` events, by instrumenting [this example][echo] from `hyper`, and using `tracing-log` to record logs emitted by `hyper`. -- **tracing-opentelemetry**: - + `opentelemetry`: Demonstrates how `tracing-opentelemetry` can be used to - export and visualize `tracing` span data. - + `opentelemetry-remote-context`: Demonstrates how `tracing-opentelemetry` - can be used to extract and inject remote context when traces span multiple - systems. [tasks]: (https://docs.rs/tokio/0.2.21/tokio/task/index.html) [tokio-proxy]: https://github.com/tokio-rs/tokio/blob/v0.1.x/tokio/examples/proxy.rs diff --git a/examples/examples/opentelemetry-remote-context.rs b/examples/examples/opentelemetry-remote-context.rs deleted file mode 100644 index d882ee9cbb..0000000000 --- a/examples/examples/opentelemetry-remote-context.rs +++ /dev/null @@ -1,46 +0,0 @@ -use opentelemetry::sdk::propagation::TraceContextPropagator; -use opentelemetry::{global, Context}; -use std::collections::HashMap; -use tracing::span; -use tracing_opentelemetry::OpenTelemetrySpanExt; -use tracing_subscriber::subscribe::CollectExt; -use tracing_subscriber::Registry; - -fn make_request(_cx: Context) { - // perform external request after injecting context - // e.g. if there are request headers that impl `opentelemetry::propagation::Injector` - // then `propagator.inject_context(cx, request.headers_mut())` -} - -fn build_example_carrier() -> HashMap { - let mut carrier = HashMap::new(); - carrier.insert( - "traceparent".to_string(), - "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(), - ); - - carrier -} - -fn main() { - // Set a format for propagating context. This MUST be provided, as the default is a no-op. - global::set_text_map_propagator(TraceContextPropagator::new()); - let subscriber = Registry::default().with(tracing_opentelemetry::subscriber()); - - tracing::collect::with_default(subscriber, || { - // Extract context from request headers - let parent_context = global::get_text_map_propagator(|propagator| { - propagator.extract(&build_example_carrier()) - }); - - // Generate tracing span as usual - let app_root = span!(tracing::Level::INFO, "app_start"); - - // Assign parent trace from external context - app_root.set_parent(parent_context); - - // To include tracing context in client requests from _this_ app, - // use `context` to extract the current OpenTelemetry context. - make_request(app_root.context()); - }); -} diff --git a/examples/examples/opentelemetry.rs b/examples/examples/opentelemetry.rs deleted file mode 100644 index 12e96d3f15..0000000000 --- a/examples/examples/opentelemetry.rs +++ /dev/null @@ -1,49 +0,0 @@ -use opentelemetry::global; -use std::{error::Error, thread, time::Duration}; -use tracing::{span, trace, warn}; -use tracing_attributes::instrument; -use tracing_subscriber::prelude::*; - -#[instrument] -#[inline] -fn expensive_work() -> &'static str { - span!(tracing::Level::INFO, "expensive_step_1") - .in_scope(|| thread::sleep(Duration::from_millis(25))); - span!(tracing::Level::INFO, "expensive_step_2") - .in_scope(|| thread::sleep(Duration::from_millis(25))); - - "success" -} - -fn main() -> Result<(), Box> { - // Install an otel pipeline with a simple span processor that exports data one at a time when - // spans end. See the `install_batch` option on each exporter's pipeline builder to see how to - // export in batches. - let tracer = opentelemetry_jaeger::new_agent_pipeline() - .with_service_name("report_example") - .install_simple()?; - let opentelemetry = tracing_opentelemetry::subscriber().with_tracer(tracer); - tracing_subscriber::registry() - .with(opentelemetry) - .try_init()?; - - { - let root = span!(tracing::Level::INFO, "app_start", work_units = 2); - let _enter = root.enter(); - - let work_result = expensive_work(); - - span!(tracing::Level::INFO, "faster_work") - .in_scope(|| thread::sleep(Duration::from_millis(10))); - - warn!("About to exit!"); - trace!("status: {}", work_result); - } // Once this scope is closed, all spans inside are closed as well - - // Shut down the current tracer provider. This will invoke the shutdown - // method on all span processors. span processors should export remaining - // spans before return. - global::shutdown_tracer_provider(); - - Ok(()) -} diff --git a/tracing-opentelemetry/CHANGELOG.md b/tracing-opentelemetry/CHANGELOG.md deleted file mode 100644 index 9cfb38def4..0000000000 --- a/tracing-opentelemetry/CHANGELOG.md +++ /dev/null @@ -1,167 +0,0 @@ -# 0.16.0 (January 30, 2022) - -### Breaking Changes - -- Upgrade to `v0.17.0` of `opentelemetry` (#1853) - For list of breaking changes in OpenTelemetry, see the - [v0.17.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0170). -# 0.15.0 (August 7, 2021) - -### Breaking Changes - -- Upgrade to `v0.16.0` of `opentelemetry` (#1497) - For list of breaking changes in OpenTelemetry, see the - [v0.16.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0160). - -# 0.14.0 (July 9, 2021) - -### Breaking Changes - -- Upgrade to `v0.15.0` of `opentelemetry` (#1441) - For list of breaking changes in OpenTelemetry, see the - [v0.15.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0150). - -# 0.13.0 (May 15, 2021) - -### Breaking Changes - -- Upgrade to `v0.14.0` of `opentelemetry` (#1394) - For list of breaking changes in OpenTelemetry, see the - [v0.14.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0140). - -# 0.12.0 (March 31, 2021) - -### Breaking Changes - -- Upgrade to `v0.13.0` of `opentelemetry` (#1322) - For list of breaking changes in OpenTelemetry, see the - [v0.13.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0130). - -### Changed - -- Improve performance when tracked inactivity is disabled (#1315) - -# 0.11.0 (January 25, 2021) - -### Breaking Changes - -- Upgrade to `v0.12.0` of `opentelemetry` (#1200) - For list of breaking changes in OpenTelemetry, see the - [v0.12.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/main/opentelemetry/CHANGELOG.md#v0120). - -# 0.10.0 (December 30, 2020) - -### Breaking Changes - -- Upgrade to `v0.11.0` of `opentelemetry` (#1161) - For list of breaking changes in OpenTelemetry, see the - [v0.11.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0110). -- Update `OpenTelemetrySpanExt::set_parent` to take a context by value as it is - now stored and propagated. (#1161) -- Rename `PreSampledTracer::sampled_span_context` to - `PreSampledTracer::sampled_context` as it now returns a full otel context. (#1161) - -# 0.9.0 (November 13, 2020) - -### Added - -- Track busy/idle timings as attributes via `with_tracked_inactivity` (#1096) - -### Breaking Changes - -- Upgrade to `v0.10.0` of `opentelemetry` (#1049) - For list of breaking changes in OpenTelemetry, see the - [v0.10.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/opentelemetry/CHANGELOG.md#v0100). - -# 0.8.0 (October 13, 2020) - -### Added - -- Implement additional record types (bool, i64, u64) (#1007) - -### Breaking changes - -- Add `PreSampledTracer` interface, removes need to specify sampler (#962) - -### Fixed - -- Connect external traces (#956) -- Assign default ids if missing (#1027) - -# 0.7.0 (August 14, 2020) - -### Breaking Changes - -- Upgrade to `v0.8.0` of `opentelemetry` (#932) - For list of breaking changes in OpenTelemetry, see the - [v0.8.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v080). - -# 0.6.0 (August 4, 2020) - -### Breaking Changes - -- Upgrade to `v0.7.0` of `opentelemetry` (#867) - For list of breaking changes in OpenTelemetry, see the - [v0.7.0 changelog](https://github.com/open-telemetry/opentelemetry-rust/blob/master/CHANGELOG.md#v070). - -# 0.5.0 (June 2, 2020) - -### Added - -- Support `tracing-log` special values (#735) -- Support `Span::follows_from` creating otel span links (#723) -- Dynamic otel span names via `otel.name` field (#732) - -### Breaking Changes - -- Upgrade to `v0.6.0` of `opentelemetry` (#745) - -### Fixed - -- Filter out invalid parent contexts when building span contexts (#743) - -# 0.4.0 (May 12, 2020) - -### Added - -- `tracing_opentelemetry::layer()` method to construct a default layer. -- `OpenTelemetryLayer::with_sampler` method to configure the opentelemetry - sampling behavior. -- `OpenTelemetryLayer::new` method to configure both the tracer and sampler. - -### Breaking Changes - -- `OpenTelemetrySpanExt::set_parent` now accepts a reference to an extracted - parent `Context` instead of a `SpanContext` to match propagators. -- `OpenTelemetrySpanExt::context` now returns a `Context` instead of a - `SpanContext` to match propagators. -- `OpenTelemetryLayer::with_tracer` now takes `&self` as a parameter -- Upgrade to `v0.5.0` of `opentelemetry`. - -### Fixed - -- Fixes bug where child spans were always marked as sampled - -# 0.3.1 (April 19, 2020) - -### Added - -- Change span status code to unknown on error event - -# 0.3.0 (April 5, 2020) - -### Added - -- Span extension for injecting and extracting `opentelemetry` span contexts - into `tracing` spans - -### Removed - -- Disabled the `metrics` feature of the opentelemetry as it is unused. - -# 0.2.0 (February 7, 2020) - -### Changed - -- Update `tracing-subscriber` to 0.2.0 stable -- Update to `opentelemetry` 0.2.0 diff --git a/tracing-opentelemetry/Cargo.toml b/tracing-opentelemetry/Cargo.toml deleted file mode 100644 index 868f0a2d91..0000000000 --- a/tracing-opentelemetry/Cargo.toml +++ /dev/null @@ -1,55 +0,0 @@ -[package] -name = "tracing-opentelemetry" -version = "0.16.0" -authors = [ - "Julian Tescher ", - "Tokio Contributors " -] -description = "OpenTelemetry integration for tracing" -homepage = "https://github.com/tokio-rs/tracing/tree/master/tracing-opentelemetry" -repository = "https://github.com/tokio-rs/tracing" -readme = "README.md" -categories = [ - "development-tools::debugging", - "development-tools::profiling", - "asynchronous", -] -keywords = ["tracing", "opentelemetry", "jaeger", "zipkin", "async"] -license = "MIT" -edition = "2018" -rust-version = "1.56.0" - -[features] -default = ["tracing-log", "metrics"] -# Enables support for exporting OpenTelemetry metrics -metrics = ["opentelemetry/metrics"] - -[dependencies] -opentelemetry = { version = "0.18.0", default-features = false, features = ["trace"] } -tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] } -tracing-core = { path = "../tracing-core", version = "0.2" } -tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry", "std"] } -tracing-log = { path = "../tracing-log", version = "0.2", default-features = false, optional = true } -once_cell = "1.13.0" - -# Fix minimal-versions; opentelemetry specifies async-trait = "0.1" which breaks -async-trait = "0.1.20" - -[dev-dependencies] -async-trait = "0.1.56" -criterion = { version = "0.3.6", default_features = false } -opentelemetry-jaeger = "0.17.0" -futures-util = { version = "0.3.21", default-features = false } -tokio = { version = "1.20.0", features = ["full"] } -tokio-stream = "0.1.9" - -[lib] -bench = false - -[[bench]] -name = "trace" -harness = false - -[package.metadata.docs.rs] -all-features = true -rustdoc-args = ["--cfg", "docsrs"] diff --git a/tracing-opentelemetry/LICENSE b/tracing-opentelemetry/LICENSE deleted file mode 100644 index cdb28b4b56..0000000000 --- a/tracing-opentelemetry/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2019 Tokio Contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/tracing-opentelemetry/README.md b/tracing-opentelemetry/README.md deleted file mode 100644 index 796b2386cf..0000000000 --- a/tracing-opentelemetry/README.md +++ /dev/null @@ -1,132 +0,0 @@ -![Tracing — Structured, application-level diagnostics][splash] - -[splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg - -# Tracing OpenTelemetry - -Utilities for adding [OpenTelemetry] interoperability to [`tracing`]. - -[![Crates.io][crates-badge]][crates-url] -[![Documentation][docs-badge]][docs-url] -[![Documentation (master)][docs-master-badge]][docs-master-url] -[![MIT licensed][mit-badge]][mit-url] -[![Build Status][actions-badge]][actions-url] -[![Discord chat][discord-badge]][discord-url] -![maintenance status][maint-badge] - -[Documentation][docs-url] | [Chat][discord-url] - -[crates-badge]: https://img.shields.io/crates/v/tracing-opentelemetry.svg -[crates-url]: https://crates.io/crates/tracing-opentelemetry/0.16.0 -[docs-badge]: https://docs.rs/tracing-opentelemetry/badge.svg -[docs-url]: https://docs.rs/tracing-opentelemetry/0.16.0/tracing_opentelemetry -[docs-master-badge]: https://img.shields.io/badge/docs-master-blue -[docs-master-url]: https://tracing-rs.netlify.com/tracing_opentelemetry -[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 -[actions-url]:https://github.com/tokio-rs/tracing/actions?query=workflow%3ACI -[discord-badge]: https://img.shields.io/discord/500028886025895936?logo=discord&label=discord&logoColor=white -[discord-url]: https://discord.gg/EeF3cQw -[maint-badge]: https://img.shields.io/badge/maintenance-actively--developed-brightgreen.svg - -## Overview - -[`tracing`] is a framework for instrumenting Rust programs to collect -structured, event-based diagnostic information. This crate provides a -subscriber that connects spans from multiple systems into a trace and -emits them to [OpenTelemetry]-compatible distributed tracing systems -for processing and visualization. - -The crate provides the following types: - -* [`OpenTelemetrySubscriber`] adds OpenTelemetry context to all `tracing` [span]s. -* [`OpenTelemetrySpanExt`] allows OpenTelemetry parent trace information to be - injected and extracted from a `tracing` [span]. - -[`OpenTelemetrySubscriber`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/struct.OpenTelemetrySubscriber.html -[`OpenTelemetrySpanExt`]: https://docs.rs/tracing-opentelemetry/latest/tracing_opentelemetry/trait.OpenTelemetrySpanExt.html -[span]: https://docs.rs/tracing/latest/tracing/span/index.html -[`tracing`]: https://crates.io/crates/tracing -[OpenTelemetry]: https://opentelemetry.io/ - -*Compiler support: [requires `rustc` 1.56+][msrv]* - -[msrv]: #supported-rust-versions - -## Examples - -### Basic Usage - -```rust -use opentelemetry::exporter::trace::stdout; -use tracing::{error, span}; -use tracing_subscriber::subscriber::SubscriberExt; -use tracing_subscriber::Registry; - -fn main() { - // Install a new OpenTelemetry trace pipeline - let (tracer, _uninstall) = stdout::new_pipeline().install(); - - // Create a tracing subscriber with the configured tracer - let telemetry = tracing_opentelemetry::subscriber().with_tracer(tracer); - - // Use the tracing subscriber `Registry`, or any other subscriber - // that impls `LookupSpan` - let collector = Registry::default().with(telemetry); - - // Trace executed code - tracing::collect::with_default(collector, || { - // Spans will be sent to the configured OpenTelemetry exporter - let root = span!(tracing::Level::TRACE, "app_start", work_units = 2); - let _enter = root.enter(); - - error!("This event will be logged in the root span."); - }); -} -``` - -### Visualization example - -```console -# Run a supported collector like jaeger in the background -$ docker run -d -p6831:6831/udp -p6832:6832/udp -p16686:16686 jaegertracing/all-in-one:latest - -# Run example to produce spans (from parent examples directory) -$ cargo run --example opentelemetry - -# View spans (see the image below) -$ firefox http://localhost:16686/ -``` - -![Jaeger UI](trace.png) - -## Feature Flags - - - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that - exports OpenTelemetry metrics from specifically-named events. This enables - the `metrics` feature flag on the `opentelemetry` crate. - -## Supported Rust Versions - -Tracing Opentelemetry is built against the latest stable release. The minimum -supported version is 1.56. 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 -project. The current stable Rust compiler and the three most recent minor -versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler -version is not considered a semver breaking change as long as doing so complies -with this policy. - -## License - -This project is licensed under the [MIT license](LICENSE). - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in Tracing by you, shall be licensed as MIT, without any additional -terms or conditions. diff --git a/tracing-opentelemetry/benches/trace.rs b/tracing-opentelemetry/benches/trace.rs deleted file mode 100644 index 13d12554fe..0000000000 --- a/tracing-opentelemetry/benches/trace.rs +++ /dev/null @@ -1,126 +0,0 @@ -use criterion::{criterion_group, criterion_main, Criterion}; -use opentelemetry::{ - sdk::trace::{Tracer, TracerProvider}, - trace::{SpanBuilder, Tracer as _, TracerProvider as _}, - Context, -}; -use std::time::SystemTime; -use tracing::trace_span; -use tracing_subscriber::prelude::*; - -fn many_children(c: &mut Criterion) { - let mut group = c.benchmark_group("otel_many_children"); - - group.bench_function("spec_baseline", |b| { - let provider = TracerProvider::default(); - let tracer = provider.tracer("bench"); - b.iter(|| { - fn dummy(tracer: &Tracer, cx: &Context) { - for _ in 0..99 { - tracer.start_with_context("child", cx); - } - } - - tracer.in_span("parent", |cx| dummy(&tracer, &cx)); - }); - }); - - { - let _subscriber = tracing_subscriber::registry() - .with(RegistryAccessCollector) - .set_default(); - group.bench_function("no_data_baseline", |b| b.iter(tracing_harness)); - } - - { - let _subscriber = tracing_subscriber::registry() - .with(OtelDataCollector) - .set_default(); - group.bench_function("data_only_baseline", |b| b.iter(tracing_harness)); - } - - { - let provider = TracerProvider::default(); - let tracer = provider.tracer("bench"); - let otel_layer = tracing_opentelemetry::subscriber() - .with_tracer(tracer) - .with_tracked_inactivity(false); - let _subscriber = tracing_subscriber::registry() - .with(otel_layer) - .set_default(); - - group.bench_function("full", |b| b.iter(tracing_harness)); - } -} - -struct NoDataSpan; -struct RegistryAccessCollector; - -impl tracing_subscriber::Subscribe for RegistryAccessCollector -where - C: tracing_core::Collect + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - _attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::subscribe::Context<'_, C>, - ) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions.insert(NoDataSpan); - } - - fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::subscribe::Context<'_, C>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - extensions.remove::(); - } -} - -struct OtelDataCollector; - -impl tracing_subscriber::Subscribe for OtelDataCollector -where - C: tracing_core::Collect + for<'span> tracing_subscriber::registry::LookupSpan<'span>, -{ - fn on_new_span( - &self, - attrs: &tracing_core::span::Attributes<'_>, - id: &tracing::span::Id, - ctx: tracing_subscriber::subscribe::Context<'_, C>, - ) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions.insert( - SpanBuilder::from_name(attrs.metadata().name()).with_start_time(SystemTime::now()), - ); - } - - fn on_close(&self, id: tracing::span::Id, ctx: tracing_subscriber::subscribe::Context<'_, C>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(builder) = extensions.remove::() { - builder.with_end_time(SystemTime::now()); - } - } -} - -fn tracing_harness() { - fn dummy() { - for _ in 0..99 { - let child = trace_span!("child"); - let _enter = child.enter(); - } - } - - let parent = trace_span!("parent"); - let _enter = parent.enter(); - - dummy(); -} - -criterion_group!(benches, many_children); -criterion_main!(benches); diff --git a/tracing-opentelemetry/src/lib.rs b/tracing-opentelemetry/src/lib.rs deleted file mode 100644 index 61a496e535..0000000000 --- a/tracing-opentelemetry/src/lib.rs +++ /dev/null @@ -1,144 +0,0 @@ -//! # Tracing OpenTelemetry -//! -//! [`tracing`] is a framework for instrumenting Rust programs to collect -//! structured, event-based diagnostic information. This crate provides a layer -//! that connects spans from multiple systems into a trace and emits them to -//! [OpenTelemetry]-compatible distributed tracing systems for processing and -//! visualization. -//! -//! [OpenTelemetry]: https://opentelemetry.io -//! [`tracing`]: https://github.com/tokio-rs/tracing -//! -//! *Compiler support: [requires `rustc` 1.56+][msrv]* -//! -//! [msrv]: #supported-rust-versions -//! -//! ### Special Fields -//! -//! Fields with an `otel.` prefix are reserved for this crate and have specific -//! meaning. They are treated as ordinary fields by other layers. The current -//! special fields are: -//! -//! * `otel.name`: Override the span name sent to OpenTelemetry exporters. -//! Setting this field is useful if you want to display non-static information -//! in your span name. -//! * `otel.kind`: Set the span kind to one of the supported OpenTelemetry [span kinds]. -//! * `otel.status_code`: Set the span status code to one of the supported OpenTelemetry [span status codes]. -//! * `otel.status_message`: Set the span status message. -//! -//! [span kinds]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.SpanKind.html -//! [span status codes]: https://docs.rs/opentelemetry/latest/opentelemetry/trace/enum.StatusCode.html -//! -//! ### Semantic Conventions -//! -//! OpenTelemetry defines conventional names for attributes of common -//! operations. These names can be assigned directly as fields, e.g. -//! `trace_span!("request", "otel.kind" = %SpanKind::Client, "http.url" = ..)`, and they -//! will be passed through to your configured OpenTelemetry exporter. You can -//! find the full list of the operations and their expected field names in the -//! [semantic conventions] spec. -//! -//! [semantic conventions]: https://github.com/open-telemetry/opentelemetry-specification/tree/master/specification/trace/semantic_conventions -//! -//! ### Stability Status -//! -//! The OpenTelemetry specification is currently in beta so some breaking -//! changes may still occur on the path to 1.0. You can follow the changes via -//! the [spec repository] to track progress toward stabilization. -//! -//! [spec repository]: https://github.com/open-telemetry/opentelemetry-specification -//! -//! ## Examples -//! -//! ``` -//! use opentelemetry::sdk::export::trace::stdout; -//! use tracing::{error, span}; -//! use tracing_subscriber::subscribe::CollectExt; -//! use tracing_subscriber::Registry; -//! -//! // Create a new OpenTelemetry pipeline -//! let tracer = stdout::new_pipeline().install_simple(); -//! -//! // Create a tracing layer with the configured tracer -//! let telemetry = tracing_opentelemetry::subscriber().with_tracer(tracer); -//! -//! // Use the tracing subscriber `Registry`, or any other subscriber -//! // that impls `LookupSpan` -//! let subscriber = Registry::default().with(telemetry); -//! -//! // Trace executed code -//! tracing::collect::with_default(subscriber, || { -//! // Spans will be sent to the configured OpenTelemetry exporter -//! let root = span!(tracing::Level::TRACE, "app_start", work_units = 2); -//! let _enter = root.enter(); -//! -//! error!("This event will be logged in the root span."); -//! }); -//! ``` -//! -//! ## Feature Flags -//! -//! - `metrics`: Enables the [`MetricsSubscriber`] type, a [subscriber] that -//! exports OpenTelemetry metrics from specifically-named events. This enables -//! the `metrics` feature flag on the `opentelemetry` crate. *Enabled by -//! default*. -//! -//! ## Supported Rust Versions -//! -//! Tracing is built against the latest stable release. The minimum supported -//! version is 1.56. 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 -//! project. The current stable Rust compiler and the three most recent minor -//! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum -//! supported compiler version is not considered a semver breaking change as -//! long as doing so complies with this policy. -//! -//! [subscriber]: tracing_subscriber::subscribe -#![deny(unreachable_pub)] -#![cfg_attr(test, deny(warnings))] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", - html_favicon_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/favicon.ico", - issue_tracker_base_url = "https://github.com/tokio-rs/tracing/issues/" -)] -#![cfg_attr( - docsrs, - // Allows displaying cfgs/feature flags in the documentation. - feature(doc_cfg, doc_auto_cfg), - // Allows adding traits to RustDoc's list of "notable traits" - feature(doc_notable_trait), - // Fail the docs build if any intra-docs links are broken - deny(rustdoc::broken_intra_doc_links), -)] - -/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry metrics. -#[cfg(feature = "metrics")] -mod metrics; -/// Span extension which enables OpenTelemetry context management. -mod span_ext; -/// Implementation of the trace::Subscriber trait; publishes OpenTelemetry traces. -mod subscriber; -/// Protocols for OpenTelemetry Tracers that are compatible with Tracing -mod tracer; - -#[cfg(feature = "metrics")] -pub use metrics::MetricsSubscriber; -pub use span_ext::OpenTelemetrySpanExt; -pub use subscriber::{subscriber, OpenTelemetrySubscriber}; -pub use tracer::PreSampledTracer; - -/// Per-span OpenTelemetry data tracked by this crate. -/// -/// Useful for implementing [PreSampledTracer] in alternate otel SDKs. -#[derive(Debug, Clone)] -pub struct OtelData { - /// The parent otel `Context` for the current tracing span. - pub parent_cx: opentelemetry::Context, - - /// The otel span data recorded during the current tracing span. - pub builder: opentelemetry::trace::SpanBuilder, -} diff --git a/tracing-opentelemetry/src/metrics.rs b/tracing-opentelemetry/src/metrics.rs deleted file mode 100644 index 9a87eea769..0000000000 --- a/tracing-opentelemetry/src/metrics.rs +++ /dev/null @@ -1,366 +0,0 @@ -use std::{collections::HashMap, fmt, sync::RwLock}; -use tracing::{field::Visit, Collect}; -use tracing_core::Field; - -use opentelemetry::{ - metrics::{Counter, Histogram, Meter, MeterProvider, UpDownCounter}, - sdk::metrics::controllers::BasicController, - Context as OtelContext, -}; -use tracing_subscriber::{registry::LookupSpan, subscribe::Context, Subscribe}; - -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; - -const METRIC_PREFIX_MONOTONIC_COUNTER: &str = "monotonic_counter."; -const METRIC_PREFIX_COUNTER: &str = "counter."; -const METRIC_PREFIX_HISTOGRAM: &str = "histogram."; -const I64_MAX: u64 = i64::MAX as u64; - -#[derive(Default)] -pub(crate) struct Instruments { - u64_counter: MetricsMap>, - f64_counter: MetricsMap>, - i64_up_down_counter: MetricsMap>, - f64_up_down_counter: MetricsMap>, - u64_histogram: MetricsMap>, - i64_histogram: MetricsMap>, - f64_histogram: MetricsMap>, -} - -type MetricsMap = RwLock>; - -#[derive(Copy, Clone, Debug)] -pub(crate) enum InstrumentType { - CounterU64(u64), - CounterF64(f64), - UpDownCounterI64(i64), - UpDownCounterF64(f64), - HistogramU64(u64), - HistogramI64(i64), - HistogramF64(f64), -} - -impl Instruments { - pub(crate) fn update_metric( - &self, - cx: &OtelContext, - meter: &Meter, - instrument_type: InstrumentType, - metric_name: &'static str, - ) { - fn update_or_insert( - map: &MetricsMap, - name: &'static str, - insert: impl FnOnce() -> T, - update: impl FnOnce(&T), - ) { - { - let lock = map.read().unwrap(); - if let Some(metric) = lock.get(name) { - update(metric); - return; - } - } - - // that metric did not already exist, so we have to acquire a write lock to - // create it. - let mut lock = map.write().unwrap(); - // handle the case where the entry was created while we were waiting to - // acquire the write lock - let metric = lock.entry(name).or_insert_with(insert); - update(metric) - } - - match instrument_type { - InstrumentType::CounterU64(value) => { - update_or_insert( - &self.u64_counter, - metric_name, - || meter.u64_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::CounterF64(value) => { - update_or_insert( - &self.f64_counter, - metric_name, - || meter.f64_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::UpDownCounterI64(value) => { - update_or_insert( - &self.i64_up_down_counter, - metric_name, - || meter.i64_up_down_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::UpDownCounterF64(value) => { - update_or_insert( - &self.f64_up_down_counter, - metric_name, - || meter.f64_up_down_counter(metric_name).init(), - |ctr| ctr.add(cx, value, &[]), - ); - } - InstrumentType::HistogramU64(value) => { - update_or_insert( - &self.u64_histogram, - metric_name, - || meter.u64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - InstrumentType::HistogramI64(value) => { - update_or_insert( - &self.i64_histogram, - metric_name, - || meter.i64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - InstrumentType::HistogramF64(value) => { - update_or_insert( - &self.f64_histogram, - metric_name, - || meter.f64_histogram(metric_name).init(), - |rec| rec.record(cx, value, &[]), - ); - } - }; - } -} - -pub(crate) struct MetricVisitor<'a> { - pub(crate) instruments: &'a Instruments, - pub(crate) meter: &'a Meter, -} - -impl<'a> Visit for MetricVisitor<'a> { - fn record_debug(&mut self, _field: &Field, _value: &dyn fmt::Debug) { - // Do nothing - } - - fn record_u64(&mut self, field: &Field, value: u64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterU64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - if value <= I64_MAX { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterI64(value as i64), - metric_name, - ); - } else { - eprintln!( - "[tracing-opentelemetry]: Received Counter metric, but \ - provided u64: {} is greater than i64::MAX. Ignoring \ - this metric.", - value - ); - } - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramU64(value), - metric_name, - ); - } - } - - fn record_f64(&mut self, field: &Field, value: f64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterF64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterF64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramF64(value), - metric_name, - ); - } - } - - fn record_i64(&mut self, field: &Field, value: i64) { - let cx = OtelContext::current(); - if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_MONOTONIC_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::CounterU64(value as u64), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_COUNTER) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::UpDownCounterI64(value), - metric_name, - ); - } else if let Some(metric_name) = field.name().strip_prefix(METRIC_PREFIX_HISTOGRAM) { - self.instruments.update_metric( - &cx, - self.meter, - InstrumentType::HistogramI64(value), - metric_name, - ); - } - } -} - -/// A subscriber that publishes metrics via the OpenTelemetry SDK. -/// -/// # Usage -/// -/// No configuration is needed for this Subscriber, as it's only responsible for -/// pushing data out to the `opentelemetry` family of crates. For example, when -/// using `opentelemetry-otlp`, that crate will provide its own set of -/// configuration options for setting up the duration metrics will be collected -/// before exporting to the OpenTelemetry Collector, aggregation of data points, -/// etc. -/// -/// ```no_run -/// use tracing_opentelemetry::MetricsSubscriber; -/// use tracing_subscriber::subscribe::CollectExt; -/// use tracing_subscriber::Registry; -/// # use opentelemetry::sdk::metrics::controllers::BasicController; -/// -/// // Constructing a BasicController is out-of-scope for the docs here, but there -/// // are examples in the opentelemetry repository. See: -/// // https://github.com/open-telemetry/opentelemetry-rust/blob/d4b9befea04bcc7fc19319a6ebf5b5070131c486/examples/basic-otlp/src/main.rs#L35-L52 -/// # let controller: BasicController = unimplemented!(); -/// -/// let opentelemetry_metrics = MetricsSubscriber::new(controller); -/// let collector = Registry::default().with(opentelemetry_metrics); -/// tracing::collect::set_global_default(collector).unwrap(); -/// ``` -/// -/// To publish a new metric, add a key-value pair to your `tracing::Event` that -/// contains following prefixes: -/// - `monotonic_counter.` (non-negative numbers): Used when the counter should -/// only ever increase -/// - `counter.`: Used when the counter can go up or down -/// - `histogram.`: Used for discrete data points (i.e., summing them does not make -/// semantic sense) -/// -/// Examples: -/// ``` -/// # use tracing::info; -/// info!(monotonic_counter.foo = 1); -/// info!(monotonic_counter.bar = 1.1); -/// -/// info!(counter.baz = 1); -/// info!(counter.baz = -1); -/// info!(counter.xyz = 1.1); -/// -/// info!(histogram.qux = 1); -/// info!(histogram.abc = -1); -/// info!(histogram.def = 1.1); -/// ``` -/// -/// # Mixing data types -/// -/// ## Floating-point numbers -/// -/// Do not mix floating point and non-floating point numbers for the same -/// metric. If a floating point number will be used for a given metric, be sure -/// to cast any other usages of that metric to a floating point number. -/// -/// Do this: -/// ``` -/// # use tracing::info; -/// info!(monotonic_counter.foo = 1_f64); -/// info!(monotonic_counter.foo = 1.1); -/// ``` -/// -/// This is because all data published for a given metric name must be the same -/// numeric type. -/// -/// ## Integers -/// -/// Positive and negative integers can be mixed freely. The instrumentation -/// provided by `tracing` assumes that all integers are `i64` unless explicitly -/// cast to something else. In the case that an integer *is* cast to `u64`, this -/// subscriber will handle the conversion internally. -/// -/// For example: -/// ``` -/// # use tracing::info; -/// // The subscriber receives an i64 -/// info!(counter.baz = 1); -/// -/// // The subscriber receives an i64 -/// info!(counter.baz = -1); -/// -/// // The subscriber receives a u64, but casts it to i64 internally -/// info!(counter.baz = 1_u64); -/// -/// // The subscriber receives a u64, but cannot cast it to i64 because of -/// // overflow. An error is printed to stderr, and the metric is dropped. -/// info!(counter.baz = (i64::MAX as u64) + 1) -/// ``` -/// -/// # Implementation Details -/// -/// `MetricsSubscriber` holds a set of maps, with each map corresponding to a -/// type of metric supported by OpenTelemetry. These maps are populated lazily. -/// The first time that a metric is emitted by the instrumentation, a `Metric` -/// instance will be created and added to the corresponding map. This means that -/// any time a metric is emitted by the instrumentation, one map lookup has to -/// be performed. -/// -/// In the future, this can be improved by associating each `Metric` instance to -/// its callsite, eliminating the need for any maps. -#[cfg_attr(docsrs, doc(cfg(feature = "metrics")))] -pub struct MetricsSubscriber { - meter: Meter, - instruments: Instruments, -} - -impl MetricsSubscriber { - /// Create a new instance of MetricsSubscriber. - pub fn new(controller: BasicController) -> Self { - let meter = - controller.versioned_meter(INSTRUMENTATION_LIBRARY_NAME, Some(CARGO_PKG_VERSION), None); - MetricsSubscriber { - meter, - instruments: Default::default(), - } - } -} - -impl Subscribe for MetricsSubscriber -where - C: Collect + for<'span> LookupSpan<'span>, -{ - fn on_event(&self, event: &tracing::Event<'_>, _ctx: Context<'_, C>) { - let mut metric_visitor = MetricVisitor { - instruments: &self.instruments, - meter: &self.meter, - }; - event.record(&mut metric_visitor); - } -} diff --git a/tracing-opentelemetry/src/span_ext.rs b/tracing-opentelemetry/src/span_ext.rs deleted file mode 100644 index 78fbef6600..0000000000 --- a/tracing-opentelemetry/src/span_ext.rs +++ /dev/null @@ -1,170 +0,0 @@ -use crate::subscriber::WithContext; -use opentelemetry::{trace::SpanContext, Context, KeyValue}; - -/// Utility functions to allow tracing [`Span`]s to accept and return -/// [OpenTelemetry] [`Context`]s. -/// -/// [`Span`]: tracing::Span -/// [OpenTelemetry]: https://opentelemetry.io -/// [`Context`]: opentelemetry::Context -pub trait OpenTelemetrySpanExt { - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// parent [`Context`]. - /// - /// [`Context`]: opentelemetry::Context - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; - /// use opentelemetry::sdk::propagation::TraceContextPropagator; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use std::collections::HashMap; - /// use tracing::Span; - /// - /// // Example carrier, could be a framework header map that impls otel's `Extractor`. - /// let mut carrier = HashMap::new(); - /// - /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. - /// let propagator = TraceContextPropagator::new(); - /// - /// // Extract otel parent context via the chosen propagator - /// let parent_context = propagator.extract(&carrier); - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // Assign parent trace from external context - /// app_root.set_parent(parent_context.clone()); - /// - /// // Or if the current span has been created elsewhere: - /// Span::current().set_parent(parent_context); - /// ``` - fn set_parent(&self, cx: Context); - - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// followed span [`SpanContext`]. - /// - /// [`SpanContext`]: opentelemetry::trace::SpanContext - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::{propagation::TextMapPropagator, trace::TraceContextExt}; - /// use opentelemetry::sdk::propagation::TraceContextPropagator; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use std::collections::HashMap; - /// use tracing::Span; - /// - /// // Example carrier, could be a framework header map that impls otel's `Extractor`. - /// let mut carrier = HashMap::new(); - /// - /// // Propagator can be swapped with b3 propagator, jaeger propagator, etc. - /// let propagator = TraceContextPropagator::new(); - /// - /// // Extract otel context of linked span via the chosen propagator - /// let linked_span_otel_context = propagator.extract(&carrier); - /// - /// // Extract the linked span context from the otel context - /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // Assign linked trace from external context - /// app_root.add_link(linked_span_context); - /// - /// // Or if the current span has been created elsewhere: - /// let linked_span_context = linked_span_otel_context.span().span_context().clone(); - /// Span::current().add_link(linked_span_context); - /// ``` - fn add_link(&self, cx: SpanContext); - - /// Associates `self` with a given OpenTelemetry trace, using the provided - /// followed span [`SpanContext`] and attributes. - /// - /// [`SpanContext`]: opentelemetry::trace::SpanContext - fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec); - - /// Extracts an OpenTelemetry [`Context`] from `self`. - /// - /// [`Context`]: opentelemetry::Context - /// - /// # Examples - /// - /// ```rust - /// use opentelemetry::Context; - /// use tracing_opentelemetry::OpenTelemetrySpanExt; - /// use tracing::Span; - /// - /// fn make_request(cx: Context) { - /// // perform external request after injecting context - /// // e.g. if the request's headers impl `opentelemetry::propagation::Injector` - /// // then `propagator.inject_context(cx, request.headers_mut())` - /// } - /// - /// // Generate a tracing span as usual - /// let app_root = tracing::span!(tracing::Level::INFO, "app_start"); - /// - /// // To include tracing context in client requests from _this_ app, - /// // extract the current OpenTelemetry context. - /// make_request(app_root.context()); - /// - /// // Or if the current span has been created elsewhere: - /// make_request(Span::current().context()) - /// ``` - fn context(&self) -> Context; -} - -impl OpenTelemetrySpanExt for tracing::Span { - fn set_parent(&self, cx: Context) { - let mut cx = Some(cx); - self.with_collector(move |(id, collector)| { - if let Some(get_context) = collector.downcast_ref::() { - get_context.with_context(collector, id, move |data, _tracer| { - if let Some(cx) = cx.take() { - data.parent_cx = cx; - } - }); - } - }); - } - - fn add_link(&self, cx: SpanContext) { - self.add_link_with_attributes(cx, Vec::new()) - } - - fn add_link_with_attributes(&self, cx: SpanContext, attributes: Vec) { - if cx.is_valid() { - let mut cx = Some(cx); - let mut att = Some(attributes); - self.with_collector(move |(id, collector)| { - if let Some(get_context) = collector.downcast_ref::() { - get_context.with_context(collector, id, move |data, _tracer| { - if let Some(cx) = cx.take() { - let attr = att.take().unwrap_or_default(); - let follows_link = opentelemetry::trace::Link::new(cx, attr); - data.builder - .links - .get_or_insert_with(|| Vec::with_capacity(1)) - .push(follows_link); - } - }); - } - }); - } - } - - fn context(&self) -> Context { - let mut cx = None; - self.with_collector(|(id, collector)| { - if let Some(get_context) = collector.downcast_ref::() { - get_context.with_context(collector, id, |builder, tracer| { - cx = Some(tracer.sampled_context(builder)); - }) - } - }); - - cx.unwrap_or_default() - } -} diff --git a/tracing-opentelemetry/src/subscriber.rs b/tracing-opentelemetry/src/subscriber.rs deleted file mode 100644 index 9e121f25c1..0000000000 --- a/tracing-opentelemetry/src/subscriber.rs +++ /dev/null @@ -1,1413 +0,0 @@ -use crate::{OtelData, PreSampledTracer}; -use once_cell::unsync; -use opentelemetry::{ - trace::{self as otel, noop, OrderMap, TraceContextExt}, - Context as OtelContext, Key, KeyValue, StringValue, Value, -}; -use std::fmt; -use std::marker; -use std::thread; -use std::time::{Instant, SystemTime}; -use std::{any::TypeId, ptr::NonNull}; -use tracing_core::span::{self, Attributes, Id, Record}; -use tracing_core::{field, Collect, Event}; -#[cfg(feature = "tracing-log")] -use tracing_log::NormalizeEvent; -use tracing_subscriber::registry::LookupSpan; -use tracing_subscriber::subscribe::Context; -use tracing_subscriber::Subscribe; - -const SPAN_NAME_FIELD: &str = "otel.name"; -const SPAN_KIND_FIELD: &str = "otel.kind"; -const SPAN_STATUS_CODE_FIELD: &str = "otel.status_code"; -const SPAN_STATUS_MESSAGE_FIELD: &str = "otel.status_message"; - -const FIELD_EXCEPTION_MESSAGE: &str = "exception.message"; -const FIELD_EXCEPTION_STACKTRACE: &str = "exception.stacktrace"; - -/// An [OpenTelemetry] propagation subscriber for use in a project that uses -/// [tracing]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// [tracing]: https://github.com/tokio-rs/tracing -pub struct OpenTelemetrySubscriber { - tracer: T, - location: bool, - tracked_inactivity: bool, - with_threads: bool, - exception_config: ExceptionFieldConfig, - get_context: WithContext, - _registry: marker::PhantomData, -} - -impl Default for OpenTelemetrySubscriber -where - C: Collect + for<'span> LookupSpan<'span>, -{ - fn default() -> Self { - OpenTelemetrySubscriber::new(noop::NoopTracer::new()) - } -} - -/// Construct a subscriber to track spans via [OpenTelemetry]. -/// -/// [OpenTelemetry]: https://opentelemetry.io -/// -/// # Examples -/// -/// ```rust,no_run -/// use tracing_subscriber::subscribe::CollectExt; -/// use tracing_subscriber::Registry; -/// -/// // Use the tracing subscriber `Registry`, or any other subscriber -/// // that impls `LookupSpan` -/// let subscriber = Registry::default().with(tracing_opentelemetry::subscriber()); -/// # drop(subscriber); -/// ``` -pub fn subscriber() -> OpenTelemetrySubscriber -where - C: Collect + for<'span> LookupSpan<'span>, -{ - OpenTelemetrySubscriber::default() -} - -// this function "remembers" the types of the subscriber so that we -// can downcast to something aware of them without knowing those -// types at the callsite. -// -// See https://github.com/tokio-rs/tracing/blob/4dad420ee1d4607bad79270c1520673fa6266a3d/tracing-error/src/layer.rs -pub(crate) struct WithContext( - fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer)), -); - -impl WithContext { - // This function allows a function to be called in the context of the - // "remembered" subscriber. - pub(crate) fn with_context( - &self, - dispatch: &tracing::Dispatch, - id: &span::Id, - mut f: impl FnMut(&mut OtelData, &dyn PreSampledTracer), - ) { - (self.0)(dispatch, id, &mut f) - } -} - -fn str_to_span_kind(s: &str) -> Option { - match s { - s if s.eq_ignore_ascii_case("server") => Some(otel::SpanKind::Server), - s if s.eq_ignore_ascii_case("client") => Some(otel::SpanKind::Client), - s if s.eq_ignore_ascii_case("producer") => Some(otel::SpanKind::Producer), - s if s.eq_ignore_ascii_case("consumer") => Some(otel::SpanKind::Consumer), - s if s.eq_ignore_ascii_case("internal") => Some(otel::SpanKind::Internal), - _ => None, - } -} - -fn str_to_status(s: &str) -> otel::Status { - match s { - s if s.eq_ignore_ascii_case("ok") => otel::Status::Ok, - s if s.eq_ignore_ascii_case("error") => otel::Status::error(""), - _ => otel::Status::Unset, - } -} - -struct SpanEventVisitor<'a, 'b> { - event_builder: &'a mut otel::Event, - span_builder: Option<&'b mut otel::SpanBuilder>, - exception_config: ExceptionFieldConfig, -} - -impl<'a, 'b> field::Visit for SpanEventVisitor<'a, 'b> { - /// Record events on the underlying OpenTelemetry [`Span`] from `bool` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_bool(&mut self, field: &field::Field, value: bool) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `f64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_f64(&mut self, field: &field::Field, value: f64) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `i64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_i64(&mut self, field: &field::Field, value: i64) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value)); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from `&str` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_str(&mut self, field: &field::Field, value: &str) { - match field.name() { - "message" => self.event_builder.name = value.to_string().into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, value.to_string())); - } - } - } - - /// Record events on the underlying OpenTelemetry [`Span`] from values that - /// implement Debug. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { - match field.name() { - "message" => self.event_builder.name = format!("{:?}", value).into(), - // Skip fields that are actually log metadata that have already been handled - #[cfg(feature = "tracing-log")] - name if name.starts_with("log.") => (), - name => { - self.event_builder - .attributes - .push(KeyValue::new(name, format!("{:?}", value))); - } - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s - /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_error( - &mut self, - field: &tracing_core::Field, - value: &(dyn std::error::Error + 'static), - ) { - let mut chain = Vec::new(); - let mut next_err = value.source(); - - while let Some(err) = next_err { - chain.push(StringValue::from(err.to_string())); - next_err = err.source(); - } - - let error_msg = value.to_string(); - - if self.exception_config.record { - self.event_builder - .attributes - .push(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - self.event_builder - .attributes - .push(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); - } - - if self.exception_config.propagate { - if let Some(span) = &mut self.span_builder { - if let Some(attrs) = span.attributes.as_mut() { - attrs.insert(Key::new(FIELD_EXCEPTION_MESSAGE), error_msg.clone().into()); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - attrs.insert( - Key::new(FIELD_EXCEPTION_STACKTRACE), - Value::Array(chain.clone().into()), - ); - } - } - } - - self.event_builder - .attributes - .push(Key::new(field.name()).string(error_msg)); - self.event_builder - .attributes - .push(Key::new(format!("{}.chain", field.name())).array(chain)); - } -} - -/// Control over opentelemetry conventional exception fields -#[derive(Clone, Copy)] -struct ExceptionFieldConfig { - /// If an error value is recorded on an event/span, should the otel fields - /// be added - record: bool, - - /// If an error value is recorded on an event, should the otel fields be - /// added to the corresponding span - propagate: bool, -} - -struct SpanAttributeVisitor<'a> { - span_builder: &'a mut otel::SpanBuilder, - exception_config: ExceptionFieldConfig, -} - -impl<'a> SpanAttributeVisitor<'a> { - fn record(&mut self, attribute: KeyValue) { - debug_assert!(self.span_builder.attributes.is_some()); - if let Some(v) = self.span_builder.attributes.as_mut() { - v.insert(attribute.key, attribute.value); - } - } -} - -impl<'a> field::Visit for SpanAttributeVisitor<'a> { - /// Set attributes on the underlying OpenTelemetry [`Span`] from `bool` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_bool(&mut self, field: &field::Field, value: bool) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `f64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_f64(&mut self, field: &field::Field, value: f64) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `i64` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_i64(&mut self, field: &field::Field, value: i64) { - self.record(KeyValue::new(field.name(), value)); - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from `&str` values. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_str(&mut self, field: &field::Field, value: &str) { - match field.name() { - SPAN_NAME_FIELD => self.span_builder.name = value.to_string().into(), - SPAN_KIND_FIELD => self.span_builder.span_kind = str_to_span_kind(value), - SPAN_STATUS_CODE_FIELD => self.span_builder.status = str_to_status(value), - SPAN_STATUS_MESSAGE_FIELD => { - self.span_builder.status = otel::Status::error(value.to_string()) - } - _ => self.record(KeyValue::new(field.name(), value.to_string())), - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] from values that - /// implement Debug. - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_debug(&mut self, field: &field::Field, value: &dyn fmt::Debug) { - match field.name() { - SPAN_NAME_FIELD => self.span_builder.name = format!("{:?}", value).into(), - SPAN_KIND_FIELD => { - self.span_builder.span_kind = str_to_span_kind(&format!("{:?}", value)) - } - SPAN_STATUS_CODE_FIELD => { - self.span_builder.status = str_to_status(&format!("{:?}", value)) - } - SPAN_STATUS_MESSAGE_FIELD => { - self.span_builder.status = otel::Status::error(format!("{:?}", value)) - } - _ => self.record(Key::new(field.name()).string(format!("{:?}", value))), - } - } - - /// Set attributes on the underlying OpenTelemetry [`Span`] using a [`std::error::Error`]'s - /// [`std::fmt::Display`] implementation. Also adds the `source` chain as an extra field - /// - /// [`Span`]: opentelemetry::trace::Span - fn record_error( - &mut self, - field: &tracing_core::Field, - value: &(dyn std::error::Error + 'static), - ) { - let mut chain = Vec::new(); - let mut next_err = value.source(); - - while let Some(err) = next_err { - chain.push(StringValue::from(err.to_string())); - next_err = err.source(); - } - - let error_msg = value.to_string(); - - if self.exception_config.record { - self.record(Key::new(FIELD_EXCEPTION_MESSAGE).string(error_msg.clone())); - - // NOTE: This is actually not the stacktrace of the exception. This is - // the "source chain". It represents the heirarchy of errors from the - // app level to the lowest level such as IO. It does not represent all - // of the callsites in the code that led to the error happening. - // `std::error::Error::backtrace` is a nightly-only API and cannot be - // used here until the feature is stabilized. - self.record(Key::new(FIELD_EXCEPTION_STACKTRACE).array(chain.clone())); - } - - self.record(Key::new(field.name()).string(error_msg)); - self.record(Key::new(format!("{}.chain", field.name())).array(chain)); - } -} - -impl OpenTelemetrySubscriber -where - C: Collect + for<'span> LookupSpan<'span>, - T: otel::Tracer + PreSampledTracer + 'static, -{ - /// Set the [`Tracer`] that this subscriber will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_opentelemetry::OpenTelemetrySubscriber; - /// use tracing_subscriber::subscribe::CollectExt; - /// use tracing_subscriber::Registry; - /// - /// // Create a jaeger exporter pipeline for a `trace_demo` service. - /// let tracer = opentelemetry_jaeger::new_agent_pipeline() - /// .with_service_name("trace_demo") - /// .install_simple() - /// .expect("Error initializing Jaeger exporter"); - /// - /// // Create a subscriber with the configured tracer - /// let otel_subscriber = OpenTelemetrySubscriber::new(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_subscriber); - /// # drop(subscriber); - /// ``` - pub fn new(tracer: T) -> Self { - OpenTelemetrySubscriber { - tracer, - location: true, - tracked_inactivity: true, - with_threads: true, - exception_config: ExceptionFieldConfig { - record: false, - propagate: false, - }, - get_context: WithContext(Self::get_context), - _registry: marker::PhantomData, - } - } - - /// Set the [`Tracer`] that this subscriber will use to produce and track - /// OpenTelemetry [`Span`]s. - /// - /// [`Tracer`]: opentelemetry::trace::Tracer - /// [`Span`]: opentelemetry::trace::Span - /// - /// # Examples - /// - /// ```no_run - /// use tracing_subscriber::subscribe::CollectExt; - /// use tracing_subscriber::Registry; - /// - /// // Create a jaeger exporter pipeline for a `trace_demo` service. - /// let tracer = opentelemetry_jaeger::new_agent_pipeline() - /// .with_service_name("trace_demo") - /// .install_simple() - /// .expect("Error initializing Jaeger exporter"); - /// - /// // Create a subscriber with the configured tracer - /// let otel_subscriber = tracing_opentelemetry::subscriber().with_tracer(tracer); - /// - /// // Use the tracing subscriber `Registry`, or any other subscriber - /// // that impls `LookupSpan` - /// let subscriber = Registry::default().with(otel_subscriber); - /// # drop(subscriber); - /// ``` - pub fn with_tracer(self, tracer: Tracer) -> OpenTelemetrySubscriber - where - Tracer: otel::Tracer + PreSampledTracer + 'static, - { - OpenTelemetrySubscriber { - tracer, - location: self.location, - tracked_inactivity: self.tracked_inactivity, - with_threads: self.with_threads, - get_context: WithContext(OpenTelemetrySubscriber::::get_context), - exception_config: self.exception_config, - _registry: self._registry, - } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// exception fields such as `exception.message` and `exception.backtrace` - /// when an `Error` value is recorded. If multiple error values are recorded - /// on the same span/event, only the most recently recorded error value will - /// show up under these fields. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// exceptions][conv]. - /// - /// By default, these fields are disabled - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ - pub fn with_exception_fields(self, exception_fields: bool) -> Self { - Self { - exception_config: ExceptionFieldConfig { - record: exception_fields, - ..self.exception_config - }, - ..self - } - } - - /// Sets whether or not reporting an `Error` value on an event will - /// propagate the OpenTelemetry exception fields such as `exception.message` - /// and `exception.backtrace` to the corresponding span. You do not need to - /// enable `with_exception_fields` in order to enable this. If multiple - /// error values are recorded on the same span/event, only the most recently - /// recorded error value will show up under these fields. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// exceptions][conv]. - /// - /// By default, this is disabled - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/exceptions/ - pub fn with_exception_field_propagation(self, exception_field_propagation: bool) -> Self { - Self { - exception_config: ExceptionFieldConfig { - propagate: exception_field_propagation, - ..self.exception_config - }, - ..self - } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// attributes with location information, such as the file, module and line number. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// source locations][conv]. - /// - /// By default, locations are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes - pub fn with_location(self, location: bool) -> Self { - Self { location, ..self } - } - - /// Sets whether or not span and event metadata should include OpenTelemetry - /// attributes with location information, such as the file, module and line number. - /// - /// These attributes follow the [OpenTelemetry semantic conventions for - /// source locations][conv]. - /// - /// By default, locations are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#source-code-attributes - #[deprecated( - since = "0.17.3", - note = "renamed to `OpenTelemetrySubscriber::with_location`" - )] - pub fn with_event_location(self, event_location: bool) -> Self { - Self { - location: event_location, - ..self - } - } - - /// Sets whether or not spans metadata should include the _busy time_ - /// (total time for which it was entered), and _idle time_ (total time - /// the span existed but was not entered). - pub fn with_tracked_inactivity(self, tracked_inactivity: bool) -> Self { - Self { - tracked_inactivity, - ..self - } - } - - /// Sets whether or not spans record additional attributes for the thread - /// name and thread ID of the thread they were created on, following the - /// [OpenTelemetry semantic conventions for threads][conv]. - /// - /// By default, thread attributes are enabled. - /// - /// [conv]: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/span-general/#general-thread-attributes - pub fn with_threads(self, threads: bool) -> Self { - Self { - with_threads: threads, - ..self - } - } - - /// Retrieve the parent OpenTelemetry [`Context`] from the current tracing - /// [`span`] through the [`Registry`]. This [`Context`] links spans to their - /// parent for proper hierarchical visualization. - /// - /// [`Context`]: opentelemetry::Context - /// [`span`]: tracing::Span - /// [`Registry`]: tracing_subscriber::Registry - fn parent_context(&self, attrs: &Attributes<'_>, ctx: &Context<'_, C>) -> OtelContext { - // If a span is specified, it _should_ exist in the underlying `Registry`. - if let Some(parent) = attrs.parent() { - let span = ctx.span(parent).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - extensions - .get_mut::() - .map(|builder| self.tracer.sampled_context(builder)) - .unwrap_or_default() - // Else if the span is inferred from context, look up any available current span. - } else if attrs.is_contextual() { - ctx.lookup_current() - .and_then(|span| { - let mut extensions = span.extensions_mut(); - extensions - .get_mut::() - .map(|builder| self.tracer.sampled_context(builder)) - }) - .unwrap_or_else(OtelContext::current) - // Explicit root spans should have no parent context. - } else { - OtelContext::new() - } - } - - fn get_context( - dispatch: &tracing::Dispatch, - id: &span::Id, - f: &mut dyn FnMut(&mut OtelData, &dyn PreSampledTracer), - ) { - let subscriber = dispatch - .downcast_ref::() - .expect("subscriber should downcast to expected type; this is a bug!"); - let span = subscriber - .span(id) - .expect("registry should have a span for the current ID"); - let subscriber = dispatch - .downcast_ref::>() - .expect("subscriber should downcast to expected type; this is a bug!"); - - let mut extensions = span.extensions_mut(); - if let Some(builder) = extensions.get_mut::() { - f(builder, &subscriber.tracer); - } - } - - fn extra_span_attrs(&self) -> usize { - let mut extra_attrs = 0; - if self.location { - extra_attrs += 3; - } - if self.with_threads { - extra_attrs += 2; - } - extra_attrs - } -} - -thread_local! { - static THREAD_ID: unsync::Lazy = unsync::Lazy::new(|| { - // OpenTelemetry's semantic conventions require the thread ID to be - // recorded as an integer, but `std::thread::ThreadId` does not expose - // the integer value on stable, so we have to convert it to a `usize` by - // parsing it. Since this requires allocating a `String`, store it in a - // thread local so we only have to do this once. - // TODO(eliza): once `std::thread::ThreadId::as_u64` is stabilized - // (https://github.com/rust-lang/rust/issues/67939), just use that. - thread_id_integer(thread::current().id()) - }); -} - -impl Subscribe for OpenTelemetrySubscriber -where - C: Collect + for<'span> LookupSpan<'span>, - T: otel::Tracer + PreSampledTracer + 'static, -{ - /// Creates an [OpenTelemetry `Span`] for the corresponding [tracing `Span`]. - /// - /// [OpenTelemetry `Span`]: opentelemetry::trace::Span - /// [tracing `Span`]: tracing::Span - fn on_new_span(&self, attrs: &Attributes<'_>, id: &span::Id, ctx: Context<'_, C>) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if self.tracked_inactivity && extensions.get_mut::().is_none() { - extensions.insert(Timings::new()); - } - - let parent_cx = self.parent_context(attrs, &ctx); - let mut builder = self - .tracer - .span_builder(attrs.metadata().name()) - .with_start_time(SystemTime::now()) - // Eagerly assign span id so children have stable parent id - .with_span_id(self.tracer.new_span_id()); - - // Record new trace id if there is no active parent span - if !parent_cx.has_active_span() { - builder.trace_id = Some(self.tracer.new_trace_id()); - } - - let builder_attrs = builder.attributes.get_or_insert(OrderMap::with_capacity( - attrs.fields().len() + self.extra_span_attrs(), - )); - - if self.location { - let meta = attrs.metadata(); - - if let Some(filename) = meta.file() { - builder_attrs.insert("code.filepath".into(), filename.into()); - } - - if let Some(module) = meta.module_path() { - builder_attrs.insert("code.namespace".into(), module.into()); - } - - if let Some(line) = meta.line() { - builder_attrs.insert("code.lineno".into(), (line as i64).into()); - } - } - - if self.with_threads { - THREAD_ID.with(|id| builder_attrs.insert("thread.id".into(), (**id as i64).into())); - if let Some(name) = std::thread::current().name() { - // TODO(eliza): it's a bummer that we have to allocate here, but - // we can't easily get the string as a `static`. it would be - // nice if `opentelemetry` could also take `Arc`s as - // `String` values... - builder_attrs.insert("thread.name".into(), name.to_owned().into()); - } - } - - attrs.record(&mut SpanAttributeVisitor { - span_builder: &mut builder, - exception_config: self.exception_config, - }); - extensions.insert(OtelData { builder, parent_cx }); - } - - fn on_enter(&self, id: &span::Id, ctx: Context<'_, C>) { - if !self.tracked_inactivity { - return; - } - - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(timings) = extensions.get_mut::() { - let now = Instant::now(); - timings.idle += (now - timings.last).as_nanos() as i64; - timings.last = now; - } - } - - fn on_exit(&self, id: &span::Id, ctx: Context<'_, C>) { - if !self.tracked_inactivity { - return; - } - - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(timings) = extensions.get_mut::() { - let now = Instant::now(); - timings.busy += (now - timings.last).as_nanos() as i64; - timings.last = now; - } - } - - /// Record OpenTelemetry [`attributes`] for the given values. - /// - /// [`attributes`]: opentelemetry::trace::SpanBuilder::attributes - fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, C>) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - if let Some(data) = extensions.get_mut::() { - values.record(&mut SpanAttributeVisitor { - span_builder: &mut data.builder, - exception_config: self.exception_config, - }); - } - } - - fn on_follows_from(&self, id: &Id, follows: &Id, ctx: Context) { - let span = ctx.span(id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - let data = extensions - .get_mut::() - .expect("Missing otel data span extensions"); - - let follows_span = ctx - .span(follows) - .expect("Span to follow not found, this is a bug"); - let mut follows_extensions = follows_span.extensions_mut(); - let follows_data = follows_extensions - .get_mut::() - .expect("Missing otel data span extensions"); - - let follows_context = self - .tracer - .sampled_context(follows_data) - .span() - .span_context() - .clone(); - let follows_link = otel::Link::new(follows_context, Vec::new()); - if let Some(ref mut links) = data.builder.links { - links.push(follows_link); - } else { - data.builder.links = Some(vec![follows_link]); - } - } - - /// Records OpenTelemetry [`Event`] data on event. - /// - /// Note: an [`ERROR`]-level event will also set the OpenTelemetry span status code to - /// [`Error`], signaling that an error has occurred. - /// - /// [`Event`]: opentelemetry::trace::Event - /// [`ERROR`]: tracing::Level::ERROR - /// [`Error`]: opentelemetry::trace::StatusCode::Error - fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) { - // Ignore events that have no explicit parent set *and* are not in the context of a span - if let Some(span) = ctx.event_span(event) { - // Performing read operations before getting a write lock to avoid a deadlock - // See https://github.com/tokio-rs/tracing/issues/763 - #[cfg(feature = "tracing-log")] - let normalized_meta = event.normalized_metadata(); - #[cfg(feature = "tracing-log")] - let meta = normalized_meta.as_ref().unwrap_or_else(|| event.metadata()); - #[cfg(not(feature = "tracing-log"))] - let meta = event.metadata(); - - let target = Key::new("target"); - - #[cfg(feature = "tracing-log")] - let target = if normalized_meta.is_some() { - target.string(meta.target().to_owned()) - } else { - target.string(event.metadata().target()) - }; - - #[cfg(not(feature = "tracing-log"))] - let target = target.string(meta.target()); - - let mut extensions = span.extensions_mut(); - let span_builder = extensions - .get_mut::() - .map(|data| &mut data.builder); - - let mut otel_event = otel::Event::new( - String::new(), - SystemTime::now(), - vec![Key::new("level").string(meta.level().as_str()), target], - 0, - ); - event.record(&mut SpanEventVisitor { - event_builder: &mut otel_event, - span_builder, - exception_config: self.exception_config, - }); - - if let Some(OtelData { builder, .. }) = extensions.get_mut::() { - if builder.status == otel::Status::Unset - && *meta.level() == tracing_core::Level::ERROR - { - builder.status = otel::Status::error("") - } - - if self.location { - #[cfg(not(feature = "tracing-log"))] - let normalized_meta: Option> = None; - let (file, module) = match &normalized_meta { - Some(meta) => ( - meta.file().map(|s| Value::from(s.to_owned())), - meta.module_path().map(|s| Value::from(s.to_owned())), - ), - None => ( - event.metadata().file().map(Value::from), - event.metadata().module_path().map(Value::from), - ), - }; - - if let Some(file) = file { - otel_event - .attributes - .push(KeyValue::new("code.filepath", file)); - } - if let Some(module) = module { - otel_event - .attributes - .push(KeyValue::new("code.namespace", module)); - } - if let Some(line) = meta.line() { - otel_event - .attributes - .push(KeyValue::new("code.lineno", line as i64)); - } - } - - if let Some(ref mut events) = builder.events { - events.push(otel_event); - } else { - builder.events = Some(vec![otel_event]); - } - } - }; - } - - /// Exports an OpenTelemetry [`Span`] on close. - /// - /// [`Span`]: opentelemetry::trace::Span - fn on_close(&self, id: span::Id, ctx: Context<'_, C>) { - let span = ctx.span(&id).expect("Span not found, this is a bug"); - let mut extensions = span.extensions_mut(); - - if let Some(OtelData { - mut builder, - parent_cx, - }) = extensions.remove::() - { - if self.tracked_inactivity { - // Append busy/idle timings when enabled. - if let Some(timings) = extensions.get_mut::() { - let busy_ns = Key::new("busy_ns"); - let idle_ns = Key::new("idle_ns"); - - let attributes = builder - .attributes - .get_or_insert_with(|| OrderMap::with_capacity(2)); - attributes.insert(busy_ns, timings.busy.into()); - attributes.insert(idle_ns, timings.idle.into()); - } - } - - // Assign end time, build and start span, drop span to export - builder - .with_end_time(SystemTime::now()) - .start_with_context(&self.tracer, &parent_cx); - } - } - - // SAFETY: this is safe because the `WithContext` function pointer is valid - // for the lifetime of `&self`. - unsafe fn downcast_raw(&self, id: TypeId) -> Option> { - match id { - id if id == TypeId::of::() => Some(NonNull::from(self).cast()), - id if id == TypeId::of::() => { - Some(NonNull::from(&self.get_context).cast()) - } - _ => None, - } - } -} - -struct Timings { - idle: i64, - busy: i64, - last: Instant, -} - -impl Timings { - fn new() -> Self { - Self { - idle: 0, - busy: 0, - last: Instant::now(), - } - } -} - -fn thread_id_integer(id: thread::ThreadId) -> u64 { - let thread_id = format!("{:?}", id); - thread_id - .trim_start_matches("ThreadId(") - .trim_end_matches(')') - .parse::() - .expect("thread ID should parse as an integer") -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::OtelData; - use opentelemetry::{ - trace::{noop, TraceFlags}, - StringValue, - }; - use std::{ - borrow::Cow, - collections::HashMap, - error::Error, - fmt::Display, - sync::{Arc, Mutex}, - thread, - time::SystemTime, - }; - use tracing_subscriber::prelude::*; - - #[derive(Debug, Clone)] - struct TestTracer(Arc>>); - impl otel::Tracer for TestTracer { - type Span = noop::NoopSpan; - fn start_with_context(&self, _name: T, _context: &OtelContext) -> Self::Span - where - T: Into>, - { - noop::NoopSpan::new() - } - fn span_builder(&self, name: T) -> otel::SpanBuilder - where - T: Into>, - { - otel::SpanBuilder::from_name(name) - } - fn build_with_context( - &self, - builder: otel::SpanBuilder, - parent_cx: &OtelContext, - ) -> Self::Span { - *self.0.lock().unwrap() = Some(OtelData { - builder, - parent_cx: parent_cx.clone(), - }); - noop::NoopSpan::new() - } - } - - impl PreSampledTracer for TestTracer { - fn sampled_context(&self, _builder: &mut crate::OtelData) -> OtelContext { - OtelContext::new() - } - fn new_trace_id(&self) -> otel::TraceId { - otel::TraceId::INVALID - } - fn new_span_id(&self) -> otel::SpanId { - otel::SpanId::INVALID - } - } - - impl TestTracer { - fn with_data(&self, f: impl FnOnce(&OtelData) -> T) -> T { - let lock = self.0.lock().unwrap(); - let data = lock.as_ref().expect("no span data has been recorded yet"); - f(data) - } - } - - #[derive(Debug, Clone)] - struct TestSpan(otel::SpanContext); - impl otel::Span for TestSpan { - fn add_event_with_timestamp>>( - &mut self, - _: T, - _: SystemTime, - _: Vec, - ) { - } - fn span_context(&self) -> &otel::SpanContext { - &self.0 - } - fn is_recording(&self) -> bool { - false - } - fn set_attribute(&mut self, _attribute: KeyValue) {} - fn set_status(&mut self, _status: otel::Status) {} - fn update_name>>(&mut self, _new_name: T) {} - fn end_with_timestamp(&mut self, _timestamp: SystemTime) {} - } - - #[derive(Debug)] - struct TestDynError { - msg: &'static str, - source: Option>, - } - impl Display for TestDynError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.msg) - } - } - impl Error for TestDynError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - match &self.source { - Some(source) => Some(source), - None => None, - } - } - } - impl TestDynError { - fn new(msg: &'static str) -> Self { - Self { msg, source: None } - } - fn with_parent(self, parent_msg: &'static str) -> Self { - Self { - msg: parent_msg, - source: Some(Box::new(self)), - } - } - } - - #[test] - fn dynamic_span_names() { - let dynamic_name = "GET http://example.com".to_string(); - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = - tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("static_name", otel.name = dynamic_name.as_str()); - }); - - let recorded_name = tracer - .0 - .lock() - .unwrap() - .as_ref() - .map(|b| b.builder.name.clone()); - assert_eq!(recorded_name, Some(dynamic_name.into())) - } - - #[test] - fn span_kind() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = - tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request", otel.kind = "server"); - }); - - let recorded_kind = tracer.with_data(|data| data.builder.span_kind.clone()); - assert_eq!(recorded_kind, Some(otel::SpanKind::Server)) - } - - #[test] - fn span_status_code() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = - tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request", otel.status_code = ?otel::Status::Ok); - }); - let recorded_status = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status - .clone(); - - assert_eq!(recorded_status, otel::Status::Ok) - } - - #[test] - fn span_status_message() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = - tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - - let message = "message"; - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request", otel.status_message = message); - }); - - let recorded_status_message = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .status - .clone(); - - assert_eq!(recorded_status_message, otel::Status::error(message)) - } - - #[test] - fn trace_id_from_existing_context() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = - tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - let trace_id = otel::TraceId::from(42u128.to_be_bytes()); - let existing_cx = OtelContext::current_with_span(TestSpan(otel::SpanContext::new( - trace_id, - otel::SpanId::from(1u64.to_be_bytes()), - TraceFlags::default(), - false, - Default::default(), - ))); - let _g = existing_cx.attach(); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request", otel.kind = "server"); - }); - - let recorded_trace_id = - tracer.with_data(|data| data.parent_cx.span().span_context().trace_id()); - assert_eq!(recorded_trace_id, trace_id) - } - - #[test] - fn includes_timings() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - subscriber() - .with_tracer(tracer.clone()) - .with_tracked_inactivity(true), - ); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(keys.contains(&"idle_ns")); - assert!(keys.contains(&"busy_ns")); - } - - #[test] - fn records_error_fields() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - subscriber() - .with_tracer(tracer.clone()) - .with_exception_fields(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!( - "request", - error = &err as &(dyn std::error::Error + 'static) - ); - }); - - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); - - let key_values = attributes - .into_iter() - .map(|(key, value)| (key.as_str().to_owned(), value)) - .collect::>(); - - assert_eq!(key_values["error"].as_str(), "user error"); - assert_eq!( - key_values["error.chain"], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - - assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - key_values[FIELD_EXCEPTION_STACKTRACE], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - } - - #[test] - fn includes_span_location() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(subscriber().with_tracer(tracer.clone()).with_location(true)); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(keys.contains(&"code.filepath")); - assert!(keys.contains(&"code.namespace")); - assert!(keys.contains(&"code.lineno")); - } - - #[test] - fn excludes_span_location() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - subscriber() - .with_tracer(tracer.clone()) - .with_location(false), - ); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(!keys.contains(&"code.filepath")); - assert!(!keys.contains(&"code.namespace")); - assert!(!keys.contains(&"code.lineno")); - } - - #[test] - fn includes_thread() { - let thread = thread::current(); - let expected_name = thread - .name() - .map(|name| Value::String(name.to_owned().into())); - let expected_id = Value::I64(thread_id_integer(thread.id()) as i64); - - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(subscriber().with_tracer(tracer.clone()).with_threads(true)); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer - .with_data(|data| data.builder.attributes.as_ref().unwrap().clone()) - .drain(..) - .map(|(key, value)| (key.as_str().to_string(), value)) - .collect::>(); - assert_eq!(attributes.get("thread.name"), expected_name.as_ref()); - assert_eq!(attributes.get("thread.id"), Some(&expected_id)); - } - - #[test] - fn excludes_thread() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry() - .with(subscriber().with_tracer(tracer.clone()).with_threads(false)); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("request"); - }); - - let attributes = tracer.with_data(|data| data.builder.attributes.as_ref().unwrap().clone()); - let keys = attributes - .iter() - .map(|(key, _)| key.as_str()) - .collect::>(); - assert!(!keys.contains(&"thread.name")); - assert!(!keys.contains(&"thread.id")); - } - - #[test] - fn propagates_error_fields_from_event_to_span() { - let tracer = TestTracer(Arc::new(Mutex::new(None))); - let subscriber = tracing_subscriber::registry().with( - subscriber() - .with_tracer(tracer.clone()) - .with_exception_field_propagation(true), - ); - - let err = TestDynError::new("base error") - .with_parent("intermediate error") - .with_parent("user error"); - - tracing::collect::with_default(subscriber, || { - let _guard = tracing::debug_span!("request",).entered(); - - tracing::error!( - error = &err as &(dyn std::error::Error + 'static), - "request error!" - ) - }); - - let attributes = tracer - .0 - .lock() - .unwrap() - .as_ref() - .unwrap() - .builder - .attributes - .as_ref() - .unwrap() - .clone(); - - let key_values = attributes - .into_iter() - .map(|(key, value)| (key.as_str().to_owned(), value)) - .collect::>(); - - assert_eq!(key_values[FIELD_EXCEPTION_MESSAGE].as_str(), "user error"); - assert_eq!( - key_values[FIELD_EXCEPTION_STACKTRACE], - Value::Array( - vec![ - StringValue::from("intermediate error"), - StringValue::from("base error") - ] - .into() - ) - ); - } -} diff --git a/tracing-opentelemetry/src/tracer.rs b/tracing-opentelemetry/src/tracer.rs deleted file mode 100644 index 52513c36ad..0000000000 --- a/tracing-opentelemetry/src/tracer.rs +++ /dev/null @@ -1,233 +0,0 @@ -use opentelemetry::sdk::trace::{Tracer, TracerProvider}; -use opentelemetry::trace::OrderMap; -use opentelemetry::{ - trace as otel, - trace::{ - noop, SamplingDecision, SamplingResult, SpanBuilder, SpanContext, SpanId, SpanKind, - TraceContextExt, TraceFlags, TraceId, TraceState, - }, - Context as OtelContext, -}; - -/// An interface for authors of OpenTelemetry SDKs to build pre-sampled tracers. -/// -/// The OpenTelemetry spec does not allow trace ids to be updated after a span -/// has been created. In order to associate extracted parent trace ids with -/// existing `tracing` spans, `tracing-opentelemetry` builds up otel span data -/// using a [`SpanBuilder`] instead, and creates / exports full otel spans only -/// when the associated `tracing` span is closed. However, in order to properly -/// inject otel [`Context`] information to downstream requests, the sampling -/// state must now be known _before_ the otel span has been created. -/// -/// The logic for coming to a sampling decision and creating an injectable span -/// context from a [`SpanBuilder`] is encapsulated in the -/// [`PreSampledTracer::sampled_context`] method and has been implemented -/// for the standard OpenTelemetry SDK, but this trait may be implemented by -/// authors of alternate OpenTelemetry SDK implementations if they wish to have -/// `tracing` compatibility. -/// -/// See the [`OpenTelemetrySpanExt::set_parent`] and -/// [`OpenTelemetrySpanExt::context`] methods for example usage. -/// -/// [`OpenTelemetrySpanExt::set_parent`]: crate::OpenTelemetrySpanExt::set_parent -/// [`OpenTelemetrySpanExt::context`]: crate::OpenTelemetrySpanExt::context -/// [`Tracer`]: opentelemetry::trace::Tracer -/// [`SpanBuilder`]: opentelemetry::trace::SpanBuilder -/// [`Context`]: opentelemetry::Context -pub trait PreSampledTracer { - /// Produce an otel context containing an active and pre-sampled span for - /// the given span builder data. - /// - /// The sampling decision, span context information, and parent context - /// values must match the values recorded when the tracing span is closed. - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext; - - /// Generate a new trace id. - fn new_trace_id(&self) -> otel::TraceId; - - /// Generate a new span id. - fn new_span_id(&self) -> otel::SpanId; -} - -impl PreSampledTracer for noop::NoopTracer { - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext { - data.parent_cx.clone() - } - - fn new_trace_id(&self) -> otel::TraceId { - otel::TraceId::INVALID - } - - fn new_span_id(&self) -> otel::SpanId { - otel::SpanId::INVALID - } -} - -impl PreSampledTracer for Tracer { - fn sampled_context(&self, data: &mut crate::OtelData) -> OtelContext { - // Ensure tracing pipeline is still installed. - if self.provider().is_none() { - return OtelContext::new(); - } - let provider = self.provider().unwrap(); - let parent_cx = &data.parent_cx; - let builder = &mut data.builder; - - // Gather trace state - let (trace_id, parent_trace_flags) = current_trace_state(builder, parent_cx, &provider); - - // Sample or defer to existing sampling decisions - let (flags, trace_state) = if let Some(result) = &builder.sampling_result { - process_sampling_result(result, parent_trace_flags) - } else { - builder.sampling_result = Some(provider.config().sampler.should_sample( - Some(parent_cx), - trace_id, - &builder.name, - builder.span_kind.as_ref().unwrap_or(&SpanKind::Internal), - builder.attributes.as_ref().unwrap_or(&OrderMap::default()), - builder.links.as_deref().unwrap_or(&[]), - self.instrumentation_library(), - )); - - process_sampling_result( - builder.sampling_result.as_ref().unwrap(), - parent_trace_flags, - ) - } - .unwrap_or_default(); - - let span_id = builder.span_id.unwrap_or(SpanId::INVALID); - let span_context = SpanContext::new(trace_id, span_id, flags, false, trace_state); - parent_cx.with_remote_span_context(span_context) - } - - fn new_trace_id(&self) -> otel::TraceId { - self.provider() - .map(|provider| provider.config().id_generator.new_trace_id()) - .unwrap_or(otel::TraceId::INVALID) - } - - fn new_span_id(&self) -> otel::SpanId { - self.provider() - .map(|provider| provider.config().id_generator.new_span_id()) - .unwrap_or(otel::SpanId::INVALID) - } -} - -fn current_trace_state( - builder: &SpanBuilder, - parent_cx: &OtelContext, - provider: &TracerProvider, -) -> (TraceId, TraceFlags) { - if parent_cx.has_active_span() { - let span = parent_cx.span(); - let sc = span.span_context(); - (sc.trace_id(), sc.trace_flags()) - } else { - ( - builder - .trace_id - .unwrap_or_else(|| provider.config().id_generator.new_trace_id()), - Default::default(), - ) - } -} - -fn process_sampling_result( - sampling_result: &SamplingResult, - trace_flags: TraceFlags, -) -> Option<(TraceFlags, TraceState)> { - match sampling_result { - SamplingResult { - decision: SamplingDecision::Drop, - .. - } => None, - SamplingResult { - decision: SamplingDecision::RecordOnly, - trace_state, - .. - } => Some((trace_flags & !TraceFlags::SAMPLED, trace_state.clone())), - SamplingResult { - decision: SamplingDecision::RecordAndSample, - trace_state, - .. - } => Some((trace_flags | TraceFlags::SAMPLED, trace_state.clone())), - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::OtelData; - use opentelemetry::sdk::trace::{config, Sampler, TracerProvider}; - use opentelemetry::trace::{SpanBuilder, SpanId, TracerProvider as _}; - - #[test] - fn assigns_default_trace_id_if_missing() { - let provider = TracerProvider::default(); - let tracer = provider.tracer("test"); - let mut builder = SpanBuilder::from_name("empty".to_string()); - builder.span_id = Some(SpanId::from(1u64.to_be_bytes())); - builder.trace_id = None; - let parent_cx = OtelContext::new(); - let cx = tracer.sampled_context(&mut OtelData { builder, parent_cx }); - let span = cx.span(); - let span_context = span.span_context(); - - assert!(span_context.is_valid()); - } - - #[rustfmt::skip] - fn sampler_data() -> Vec<(&'static str, Sampler, OtelContext, Option, bool)> { - vec![ - // No parent samples - ("empty_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new(), None, true), - ("empty_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new(), None, false), - - // Remote parent samples - ("remote_parent_cx_always_on", Sampler::AlwaysOn, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), - ("remote_parent_cx_always_off", Sampler::AlwaysOff, OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, false), - ("sampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOff)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::SAMPLED, true)), None, true), - ("unsampled_remote_parent_cx_parent_based", Sampler::ParentBased(Box::new(Sampler::AlwaysOn)), OtelContext::new().with_remote_span_context(span_context(TraceFlags::default(), true)), None, false), - - // Existing sampling result defers - ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), - ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), - - // Existing local parent, defers - ("previous_drop_result_always_on", Sampler::AlwaysOn, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::Drop, attributes: vec![], trace_state: Default::default() }), false), - ("previous_record_and_sample_result_always_off", Sampler::AlwaysOff, OtelContext::new(), Some(SamplingResult { decision: SamplingDecision::RecordAndSample, attributes: vec![], trace_state: Default::default() }), true), - ] - } - - #[test] - fn sampled_context() { - for (name, sampler, parent_cx, previous_sampling_result, is_sampled) in sampler_data() { - let provider = TracerProvider::builder() - .with_config(config().with_sampler(sampler)) - .build(); - let tracer = provider.tracer("test"); - let mut builder = SpanBuilder::from_name("parent".to_string()); - builder.sampling_result = previous_sampling_result; - let sampled = tracer.sampled_context(&mut OtelData { builder, parent_cx }); - - assert_eq!( - sampled.span().span_context().is_sampled(), - is_sampled, - "{}", - name - ) - } - } - - fn span_context(trace_flags: TraceFlags, is_remote: bool) -> SpanContext { - SpanContext::new( - TraceId::from(1u128.to_be_bytes()), - SpanId::from(1u64.to_be_bytes()), - trace_flags, - is_remote, - Default::default(), - ) - } -} diff --git a/tracing-opentelemetry/tests/metrics_publishing.rs b/tracing-opentelemetry/tests/metrics_publishing.rs deleted file mode 100644 index 7443cb140b..0000000000 --- a/tracing-opentelemetry/tests/metrics_publishing.rs +++ /dev/null @@ -1,282 +0,0 @@ -use opentelemetry::{ - metrics::MetricsError, - sdk::{ - export::metrics::{ - aggregation::{self, Histogram, Sum, TemporalitySelector}, - InstrumentationLibraryReader, - }, - metrics::{ - aggregators::{HistogramAggregator, SumAggregator}, - controllers::BasicController, - processors, - sdk_api::{Descriptor, InstrumentKind, Number, NumberKind}, - selectors, - }, - }, - Context, -}; -use std::cmp::Ordering; -use tracing::Collect; -use tracing_opentelemetry::MetricsSubscriber; -use tracing_subscriber::prelude::*; - -const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION"); -const INSTRUMENTATION_LIBRARY_NAME: &str = "tracing/tracing-opentelemetry"; - -#[tokio::test] -async fn u64_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "hello_world".to_string(), - InstrumentKind::Counter, - NumberKind::U64, - Number::from(1_u64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(monotonic_counter.hello_world = 1_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn u64_counter_is_exported_i64_at_instrumentation_point() { - let (subscriber, exporter) = init_subscriber( - "hello_world2".to_string(), - InstrumentKind::Counter, - NumberKind::U64, - Number::from(1_u64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(monotonic_counter.hello_world2 = 1_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "float_hello_world".to_string(), - InstrumentKind::Counter, - NumberKind::F64, - Number::from(1.000000123_f64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(monotonic_counter.float_hello_world = 1.000000123_f64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_up_down_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "pebcak".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::I64, - Number::from(-5_i64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(counter.pebcak = -5_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_up_down_counter_is_exported_u64_at_instrumentation_point() { - let (subscriber, exporter) = init_subscriber( - "pebcak2".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::I64, - Number::from(5_i64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(counter.pebcak2 = 5_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_up_down_counter_is_exported() { - let (subscriber, exporter) = init_subscriber( - "pebcak_blah".to_string(), - InstrumentKind::UpDownCounter, - NumberKind::F64, - Number::from(99.123_f64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(counter.pebcak_blah = 99.123_f64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn u64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg".to_string(), - InstrumentKind::Histogram, - NumberKind::U64, - Number::from(9_u64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(histogram.abcdefg = 9_u64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn i64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg_auenatsou".to_string(), - InstrumentKind::Histogram, - NumberKind::I64, - Number::from(-19_i64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(histogram.abcdefg_auenatsou = -19_i64); - }); - - exporter.export().unwrap(); -} - -#[tokio::test] -async fn f64_histogram_is_exported() { - let (subscriber, exporter) = init_subscriber( - "abcdefg_racecar".to_string(), - InstrumentKind::Histogram, - NumberKind::F64, - Number::from(777.0012_f64), - ); - - tracing::collect::with_default(subscriber, || { - tracing::info!(histogram.abcdefg_racecar = 777.0012_f64); - }); - - exporter.export().unwrap(); -} - -fn init_subscriber( - expected_metric_name: String, - expected_instrument_kind: InstrumentKind, - expected_number_kind: NumberKind, - expected_value: Number, -) -> (impl Collect + 'static, TestExporter) { - let controller = opentelemetry::sdk::metrics::controllers::basic(processors::factory( - selectors::simple::histogram(vec![-10.0, 100.0]), - aggregation::cumulative_temporality_selector(), - )) - .build(); - - let exporter = TestExporter { - expected_metric_name, - expected_instrument_kind, - expected_number_kind, - expected_value, - controller: controller.clone(), - }; - - ( - tracing_subscriber::registry().with(MetricsSubscriber::new(controller)), - exporter, - ) -} - -#[derive(Clone, Debug)] -struct TestExporter { - expected_metric_name: String, - expected_instrument_kind: InstrumentKind, - expected_number_kind: NumberKind, - expected_value: Number, - controller: BasicController, -} - -impl TestExporter { - fn export(&self) -> Result<(), MetricsError> { - self.controller.collect(&Context::current())?; - self.controller.try_for_each(&mut |library, reader| { - reader.try_for_each(self, &mut |record| { - assert_eq!(self.expected_metric_name, record.descriptor().name()); - assert_eq!( - self.expected_instrument_kind, - *record.descriptor().instrument_kind() - ); - assert_eq!( - self.expected_number_kind, - *record.descriptor().number_kind() - ); - match self.expected_instrument_kind { - InstrumentKind::Counter | InstrumentKind::UpDownCounter => { - let number = record - .aggregator() - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .sum() - .unwrap(); - - assert_eq!( - Ordering::Equal, - number - .partial_cmp(&NumberKind::U64, &self.expected_value) - .unwrap() - ); - } - InstrumentKind::Histogram => { - let histogram = record - .aggregator() - .unwrap() - .as_any() - .downcast_ref::() - .unwrap() - .histogram() - .unwrap(); - - let counts = histogram.counts(); - if dbg!(self.expected_value.to_i64(&self.expected_number_kind)) > 100 { - assert_eq!(counts, &[0.0, 0.0, 1.0]); - } else if self.expected_value.to_i64(&self.expected_number_kind) > 0 { - assert_eq!(counts, &[0.0, 1.0, 0.0]); - } else { - assert_eq!(counts, &[1.0, 0.0, 0.0]); - } - } - _ => panic!( - "InstrumentKind {:?} not currently supported!", - self.expected_instrument_kind - ), - }; - - // The following are the same regardless of the individual metric. - assert_eq!(INSTRUMENTATION_LIBRARY_NAME, library.name); - assert_eq!(CARGO_PKG_VERSION, library.version.as_ref().unwrap()); - - Ok(()) - }) - }) - } -} - -impl TemporalitySelector for TestExporter { - fn temporality_for( - &self, - _descriptor: &Descriptor, - _kind: &aggregation::AggregationKind, - ) -> aggregation::Temporality { - // I don't think the value here makes a difference since - // we are just testing a single metric. - aggregation::Temporality::Cumulative - } -} diff --git a/tracing-opentelemetry/tests/trace_state_propagation.rs b/tracing-opentelemetry/tests/trace_state_propagation.rs deleted file mode 100644 index bc80e1364b..0000000000 --- a/tracing-opentelemetry/tests/trace_state_propagation.rs +++ /dev/null @@ -1,171 +0,0 @@ -use futures_util::future::BoxFuture; -use opentelemetry::{ - propagation::TextMapPropagator, - sdk::{ - export::trace::{ExportResult, SpanData, SpanExporter}, - propagation::{BaggagePropagator, TextMapCompositePropagator, TraceContextPropagator}, - trace::{Tracer, TracerProvider}, - }, - trace::{SpanContext, TraceContextExt, Tracer as _, TracerProvider as _}, - Context, -}; -use std::collections::{HashMap, HashSet}; -use std::sync::{Arc, Mutex}; -use tracing::Collect; -use tracing_opentelemetry::{subscriber, OpenTelemetrySpanExt}; -use tracing_subscriber::prelude::*; - -#[test] -fn trace_with_active_otel_context() { - let (cx, subscriber, exporter, provider) = build_sampled_context(); - let attached = cx.attach(); - - tracing::collect::with_default(subscriber, || { - tracing::debug_span!("child"); - }); - - drop(attached); // end implicit parent - drop(provider); // flush all spans - - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn trace_with_assigned_otel_context() { - let (cx, subscriber, exporter, provider) = build_sampled_context(); - - tracing::collect::with_default(subscriber, || { - let child = tracing::debug_span!("child"); - child.set_parent(cx); - }); - - drop(provider); // flush all spans - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn trace_root_with_children() { - let (_tracer, provider, exporter, subscriber) = test_tracer(); - - tracing::collect::with_default(subscriber, || { - // Propagate trace information through tracing parent -> child - let root = tracing::debug_span!("root"); - root.in_scope(|| tracing::debug_span!("child")); - }); - - drop(provider); // flush all spans - let spans = exporter.0.lock().unwrap(); - assert_eq!(spans.len(), 2); - assert_shared_attrs_eq(&spans[0].span_context, &spans[1].span_context); -} - -#[test] -fn inject_context_into_outgoing_requests() { - let (_tracer, _provider, _exporter, subscriber) = test_tracer(); - let propagator = test_propagator(); - let carrier = test_carrier(); - let cx = propagator.extract(&carrier); - let mut outgoing_req_carrier = HashMap::new(); - - tracing::collect::with_default(subscriber, || { - let root = tracing::debug_span!("root"); - root.set_parent(cx); - let _g = root.enter(); - let child = tracing::debug_span!("child"); - propagator.inject_context(&child.context(), &mut outgoing_req_carrier); - }); - - // Ensure all values that should be passed between services are preserved - assert_carrier_attrs_eq(&carrier, &outgoing_req_carrier); -} - -fn assert_shared_attrs_eq(sc_a: &SpanContext, sc_b: &SpanContext) { - assert_eq!(sc_a.trace_id(), sc_b.trace_id()); - assert_eq!(sc_a.trace_state(), sc_b.trace_state()); -} - -fn assert_carrier_attrs_eq( - carrier_a: &HashMap, - carrier_b: &HashMap, -) { - // Match baggage unordered - assert_eq!( - carrier_a - .get("baggage") - .map(|b| b.split_terminator(',').collect::>()), - carrier_b - .get("baggage") - .map(|b| b.split_terminator(',').collect()) - ); - // match trace parent values, except span id - assert_eq!( - carrier_a.get("traceparent").unwrap()[0..36], - carrier_b.get("traceparent").unwrap()[0..36], - ); - // match tracestate values - assert_eq!(carrier_a.get("tracestate"), carrier_b.get("tracestate")); -} - -fn test_tracer() -> (Tracer, TracerProvider, TestExporter, impl Collect) { - let exporter = TestExporter::default(); - let provider = TracerProvider::builder() - .with_simple_exporter(exporter.clone()) - .build(); - let tracer = provider.tracer("test"); - let subscriber = tracing_subscriber::registry().with(subscriber().with_tracer(tracer.clone())); - - (tracer, provider, exporter, subscriber) -} - -fn test_propagator() -> TextMapCompositePropagator { - let baggage_propagator = BaggagePropagator::new(); - let trace_context_propagator = TraceContextPropagator::new(); - - TextMapCompositePropagator::new(vec![ - Box::new(baggage_propagator), - Box::new(trace_context_propagator), - ]) -} - -fn test_carrier() -> HashMap { - let mut carrier = HashMap::new(); - carrier.insert( - "baggage".to_string(), - "key2=value2,key1=value1;property1;property2,key3=value3;propertyKey=propertyValue" - .to_string(), - ); - carrier.insert("tracestate".to_string(), "test1=test2".to_string()); - carrier.insert( - "traceparent".to_string(), - "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01".to_string(), - ); - - carrier -} - -fn build_sampled_context() -> (Context, impl Collect, TestExporter, TracerProvider) { - let (tracer, provider, exporter, subscriber) = test_tracer(); - let span = tracer.start("sampled"); - let cx = Context::current_with_span(span); - - (cx, subscriber, exporter, provider) -} - -#[derive(Clone, Default, Debug)] -struct TestExporter(Arc>>); - -impl SpanExporter for TestExporter { - fn export(&mut self, mut batch: Vec) -> BoxFuture<'static, ExportResult> { - let spans = self.0.clone(); - Box::pin(async move { - if let Ok(mut inner) = spans.lock() { - inner.append(&mut batch); - } - Ok(()) - }) - } -} diff --git a/tracing-opentelemetry/trace.png b/tracing-opentelemetry/trace.png deleted file mode 100644 index 4cb98d135830218144b8e6575a41233d344a00e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 87211 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

`s)6FQc|!d{!W7d0?mAWX#Gzq^{{ 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 diff --git a/tracing-subscriber/src/filter/targets.rs b/tracing-subscriber/src/filter/targets.rs index 9710096c9f..d26c428a73 100644 --- a/tracing-subscriber/src/filter/targets.rs +++ b/tracing-subscriber/src/filter/targets.rs @@ -325,7 +325,7 @@ impl Targets { /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); /// ``` pub fn default_level(&self) -> Option { - self.0.directives().into_iter().find_map(|d| { + self.0.directives().find_map(|d| { if d.target.is_none() { Some(d.level) } else { From 1406b4cbe36b750a67284775fcd54d548a271c21 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Wed, 5 Apr 2023 14:19:55 -0400 Subject: [PATCH 005/101] chore: bump MSRV to 1.56 (#2546) As part of upgrading syn to 2.0 (e.g., https://github.com/tokio-rs/tracing/pull/2516), we need to bump the MSRV to 1.56. As part of this PR, I've: - Updated the text descriptions of what would be an in-policy MSRV bump to use more recent versions of rustc. The _niceness_ of said version numbers are purely coincidental. - I've removed some of the exceptions made in CI.yml in order to support some crates with a higher MSRV. --- .github/workflows/CI.yml | 16 ++-------------- README.md | 6 +++--- examples/Cargo.toml | 2 +- tracing-appender/README.md | 4 ++-- tracing-appender/src/lib.rs | 4 ++-- tracing-attributes/Cargo.toml | 2 +- tracing-attributes/README.md | 8 ++++---- tracing-attributes/src/lib.rs | 8 ++++---- tracing-core/Cargo.toml | 2 +- tracing-core/README.md | 8 ++++---- tracing-core/src/lib.rs | 8 ++++---- tracing-error/Cargo.toml | 2 +- tracing-error/README.md | 8 ++++---- tracing-error/src/lib.rs | 8 ++++---- tracing-flame/Cargo.toml | 2 +- tracing-flame/README.md | 8 ++++---- tracing-flame/src/lib.rs | 8 ++++---- tracing-futures/Cargo.toml | 2 +- tracing-futures/README.md | 8 ++++---- tracing-futures/src/lib.rs | 8 ++++---- tracing-journald/Cargo.toml | 2 +- tracing-journald/README.md | 8 ++++---- tracing-journald/src/lib.rs | 8 ++++---- tracing-log/Cargo.toml | 2 +- tracing-log/README.md | 8 ++++---- tracing-log/src/lib.rs | 8 ++++---- tracing-macros/Cargo.toml | 2 +- tracing-mock/Cargo.toml | 2 +- tracing-mock/README.md | 8 ++++---- tracing-serde/Cargo.toml | 2 +- tracing-serde/README.md | 8 ++++---- tracing-serde/src/lib.rs | 8 ++++---- tracing-subscriber/Cargo.toml | 2 +- tracing-subscriber/README.md | 8 ++++---- tracing-subscriber/src/fmt/mod.rs | 2 +- tracing-subscriber/src/lib.rs | 8 ++++---- tracing-tower/Cargo.toml | 2 +- tracing/Cargo.toml | 2 +- tracing/README.md | 8 ++++---- tracing/src/lib.rs | 8 ++++---- 40 files changed, 108 insertions(+), 120 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 98dbf24b17..a672bb28dd 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.49.0). This + # Run `cargo check` on our minimum supported Rust version (1.56.0). This # checks with minimal versions; maximal versions are checked above. name: "cargo check (+MSRV -Zminimal-versions)" needs: check @@ -143,20 +143,8 @@ jobs: - tracing-tower - tracing toolchain: - - 1.49.0 + - 1.56.0 - stable - # TODO(eliza): remove this when appender is on the same MSRV. - # same for tracing subscriber - exclude: - - subcrate: tracing-appender - toolchain: 1.49.0 - - subcrate: tracing-subscriber - toolchain: 1.49.0 - include: - - subcrate: tracing-appender - toolchain: 1.53.0 - - subcrate: tracing-subscriber - toolchain: 1.50.0 steps: - uses: actions/checkout@v3 - name: install Rust nightly diff --git a/README.md b/README.md index e366458c46..7fb11378ff 100644 --- a/README.md +++ b/README.md @@ -254,14 +254,14 @@ attachment that `Future::instrument` does. ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 42e366d6c5..b33c85f1c4 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -3,7 +3,7 @@ name = "tracing-examples" version = "0.0.0" publish = false edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = [] diff --git a/tracing-appender/README.md b/tracing-appender/README.md index 121b92d701..54161684cb 100644 --- a/tracing-appender/README.md +++ b/tracing-appender/README.md @@ -152,8 +152,8 @@ Rust versions earlier than the minimum supported version. Tracing follows the same compiler support policies as the rest of the Tokio project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current -stable compiler version is 1.45, the minimum supported version will not be -increased past 1.42, three minor versions prior. Increasing the minimum +stable compiler version is 1.69, the minimum supported version will not be +increased past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs index ff336194ef..97ad8d4d58 100644 --- a/tracing-appender/src/lib.rs +++ b/tracing-appender/src/lib.rs @@ -116,8 +116,8 @@ //! Tracing follows the same compiler support policies as the rest of the Tokio //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 4ef87a7c5f..12000c378e 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -28,7 +28,7 @@ keywords = ["logging", "tracing", "macro", "instrument", "log"] license = "MIT" readme = "README.md" edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [lib] proc-macro = true diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md index efb8b50ebd..976aafe305 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -69,14 +69,14 @@ pub fn my_function(my_arg: usize) { ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs index 4b92520deb..fcde9ae8c2 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -41,14 +41,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index ecf5114d2e..363d8fad53 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -24,7 +24,7 @@ categories = [ ] keywords = ["logging", "tracing", "profiling"] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["std"] diff --git a/tracing-core/README.md b/tracing-core/README.md index 189d4f66f6..24f0fb1783 100644 --- a/tracing-core/README.md +++ b/tracing-core/README.md @@ -53,7 +53,7 @@ The crate provides: In addition, it defines the global callsite registry and per-thread current dispatcher which other components of the tracing system rely on. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -99,14 +99,14 @@ The following crate feature flags are available: ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.69, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index 34e290eecd..e31978368d 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -109,14 +109,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-error/Cargo.toml b/tracing-error/Cargo.toml index 190f791bb6..7db6fb8837 100644 --- a/tracing-error/Cargo.toml +++ b/tracing-error/Cargo.toml @@ -32,7 +32,7 @@ keywords = [ "backtrace" ] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["traced-error"] diff --git a/tracing-error/README.md b/tracing-error/README.md index 7a625ca296..ae2721233f 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -186,14 +186,14 @@ fn main() { ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs index 386b6a0e36..c6dba25fe3 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -166,14 +166,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-flame/Cargo.toml b/tracing-flame/Cargo.toml index 351aed7d61..2ea6d0b807 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.49.0" +rust-version = "1.56.0" [features] default = ["smallvec"] diff --git a/tracing-flame/README.md b/tracing-flame/README.md index 46340ed541..5a6b9017af 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions [post]: http://www.brendangregg.com/flamegraphs.html @@ -106,14 +106,14 @@ _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.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs index dea0278b11..b123d80b8a 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! [post]: http://www.brendangregg.com/flamegraphs.html @@ -95,14 +95,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index 3bda4cb601..d4ef1f71b6 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.49.0" +rust-version = "1.56.0" [features] default = ["std-future", "std"] diff --git a/tracing-futures/README.md b/tracing-futures/README.md index c27248cfa3..a7d3331a76 100644 --- a/tracing-futures/README.md +++ b/tracing-futures/README.md @@ -51,21 +51,21 @@ 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index 520f92d437..5af13a0a5b 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -15,7 +15,7 @@ //! * [`WithCollector`] allows a `tracing` [collector] to be attached to a //! future, sink, stream, or executor. //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -60,14 +60,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml index 29390804d8..a2c0755283 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.49.0" +rust-version = "1.56.0" [dependencies] libc = "0.2.126" diff --git a/tracing-journald/README.md b/tracing-journald/README.md index 76d3d4c0d7..6e6ec5942a 100644 --- a/tracing-journald/README.md +++ b/tracing-journald/README.md @@ -28,7 +28,7 @@ scoped, structured, and async-aware diagnostics. `tracing-journald` provides a and events to [`systemd-journald`][journald], on Linux distributions that use `systemd`. -*Compiler support: [requires `rustc` 1.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions [`tracing`]: https://crates.io/crates/tracing @@ -38,14 +38,14 @@ 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.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs index 2433418ce9..237a0de647 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! [`tracing`]: https://crates.io/crates/tracing @@ -21,14 +21,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index 4976491527..61e68b376c 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.49.0" +rust-version = "1.56.0" [features] default = ["log-tracer", "std"] diff --git a/tracing-log/README.md b/tracing-log/README.md index 1cfee3681d..3da5f6b7e1 100644 --- a/tracing-log/README.md +++ b/tracing-log/README.md @@ -56,21 +56,21 @@ 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index 36f0b6fc05..916d0b6b7f 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -16,7 +16,7 @@ //! - An [`env_logger`] module, with helpers for using the [`env_logger` crate] //! with `tracing` (optional, enabled by the `env-logger` feature). //! -//! *Compiler support: [requires `rustc` 1.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -76,14 +76,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-macros/Cargo.toml b/tracing-macros/Cargo.toml index 3d15aee3b6..b457067e26 100644 --- a/tracing-macros/Cargo.toml +++ b/tracing-macros/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.49.0" +rust-version = "1.56.0" [dependencies] tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] } diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index efd35b05d2..e041338b0a 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" repository = "https://github.com/tokio-rs/tracing" homepage = "https://tokio.rs" edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" publish = false [dependencies] diff --git a/tracing-mock/README.md b/tracing-mock/README.md index b508db00cb..e561606119 100644 --- a/tracing-mock/README.md +++ b/tracing-mock/README.md @@ -29,7 +29,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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -154,14 +154,14 @@ handle.assert_finished(); ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-serde/Cargo.toml b/tracing-serde/Cargo.toml index 41154f7c09..7b027fc5ac 100644 --- a/tracing-serde/Cargo.toml +++ b/tracing-serde/Cargo.toml @@ -16,7 +16,7 @@ categories = [ "encoding", ] keywords = ["logging", "tracing", "serialization"] -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["std"] diff --git a/tracing-serde/README.md b/tracing-serde/README.md index f02a880428..b52b9ae88a 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -113,14 +113,14 @@ The following crate feature flags are available: ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index c01490d5c0..640c8d7886 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -128,14 +128,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index a93d231dd1..d39da0cdc8 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -20,7 +20,7 @@ categories = [ "asynchronous", ] keywords = ["logging", "tracing", "metrics", "subscriber"] -rust-version = "1.50.0" +rust-version = "1.56.0" [features] diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md index 5f4d2e91b7..9e5c0b645e 100644 --- a/tracing-subscriber/README.md +++ b/tracing-subscriber/README.md @@ -32,21 +32,21 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers. [discord-url]: https://discord.gg/EeF3cQw [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg -*Compiler support: [requires `rustc` 1.50+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions ## Supported Rust Versions Tracing is built against the latest stable release. The minimum supported -version is 1.50. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index 936ecd6400..ba5066d2ec 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: ../index.html#supported-rust-versions //! diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 6f10eac97f..5e7787028c 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 `Collector` authors and //! application authors using `tracing` to instrument their applications. //! -//! *Compiler support: [requires `rustc` 1.50+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! @@ -106,14 +106,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.50. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml index 7520c5d7b8..970886ae84 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -15,7 +15,7 @@ categories = [ ] keywords = ["logging", "tracing"] license = "MIT" -rust-version = "1.49.0" +rust-version = "1.56.0" [features] default = ["tower-layer", "tower-make"] diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 6c43ff6c70..55221efeb5 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -28,7 +28,7 @@ categories = [ ] keywords = ["logging", "tracing", "metrics", "async"] edition = "2018" -rust-version = "1.49.0" +rust-version = "1.56.0" [dependencies] tracing-core = { path = "../tracing-core", version = "0.2", default-features = false } diff --git a/tracing/README.md b/tracing/README.md index a4dea4f724..8f7f0a6241 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.49+][msrv]* +*Compiler support: [requires `rustc` 1.56+][msrv]* [msrv]: #supported-rust-versions @@ -425,14 +425,14 @@ 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.49. The current Tracing version is not guaranteed to build on Rust +version is 1.56. 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 project. The current stable Rust compiler and the three most recent minor versions before it will always be supported. For example, if the current stable -compiler version is 1.45, the minimum supported version will not be increased -past 1.42, three minor versions prior. Increasing the minimum supported compiler +compiler version is 1.69, the minimum supported version will not be increased +past 1.66, three minor versions prior. Increasing the minimum supported compiler version is not considered a semver breaking change as long as doing so complies with this policy. diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 21411c7df4..49b3ffc5a5 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.49+][msrv]* +//! *Compiler support: [requires `rustc` 1.56+][msrv]* //! //! [msrv]: #supported-rust-versions //! # Core Concepts @@ -880,14 +880,14 @@ //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported -//! version is 1.49. The current Tracing version is not guaranteed to build on +//! version is 1.56. 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 //! project. The current stable Rust compiler and the three most recent minor //! versions before it will always be supported. For example, if the current -//! stable compiler version is 1.45, the minimum supported version will not be -//! increased past 1.42, three minor versions prior. Increasing the minimum +//! stable compiler version is 1.69, the minimum supported version will not be +//! increased past 1.66, three minor versions prior. Increasing the minimum //! supported compiler version is not considered a semver breaking change as //! long as doing so complies with this policy. //! From 49cbc5a68bdd0fdaa88c31502c4c9cab9dd7ac63 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Wed, 5 Apr 2023 12:19:20 -0700 Subject: [PATCH 006/101] chore: fix `clippy::needless_borrow` lint in journald tests (#2547) --- tracing-journald/tests/journal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs index dadb37aac3..5eab5f79cd 100644 --- a/tracing-journald/tests/journal.rs +++ b/tracing-journald/tests/journal.rs @@ -121,7 +121,7 @@ fn read_from_journal(test_name: &str) -> Vec> { let stdout = String::from_utf8( Command::new("journalctl") // We pass --all to circumvent journalctl's default limit of 4096 bytes for field values - .args(&["--user", "--output=json", "--all"]) + .args(["--user", "--output=json", "--all"]) // Filter by the PID of the current test process .arg(format!("_PID={}", std::process::id())) .arg(format!("TEST_NAME={}", test_name)) From db64fc2d18ca0adc3ffb54df779129734fa72f4c Mon Sep 17 00:00:00 2001 From: Donough Liu Date: Wed, 12 Apr 2023 06:43:23 +0800 Subject: [PATCH 007/101] tracing: `inline` methods to make macro-generated code smaller (#2555) ## Motivation Make `tracing::event!` codegen smaller ## Solution Add `inline` to several functions called by `tracing::event!`. Simple example: https://github.com/ldm0/tracing_test After inlining, executable size drops from 746kb to 697kb (`cargo build --release + strip`), saves 50 bytes per `event!`. Test environment: ``` toolchain: nightly-aarch64-apple-darwin rustc-version: rustc 1.70.0-nightly (88fb1b922 2023-04-10) ``` There are also performance improvements in the benchmarks: ``` event/scoped [-40.689% -40.475% -40.228%] event/scoped_recording [-14.972% -14.685% -14.410%] event/global [-48.412% -48.217% -48.010%] span_fields/scoped [-25.317% -24.876% -24.494%] span_fields/global [-39.695% -39.488% -39.242%] span_repeated/global [-27.514% -26.633% -25.298%] static/baseline_single_threaded [-32.275% -32.032% -31.808%] static/single_threaded [-29.628% -29.376% -29.156%] static/enabled_one [-29.777% -29.544% -29.305%] static/enabled_many [-30.901% -30.504% -30.140%] dynamic/baseline_single_threaded [-20.157% -19.880% -19.603%] ``` I retried benchmark several times and the improvements seem to be fairly stable. raw log: https://gist.github.com/ldm0/6573935f4979d2645fbcf5bde7361386 --- tracing-core/src/field.rs | 3 +++ tracing-core/src/metadata.rs | 1 + tracing/src/macros.rs | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 12697fd402..768107b042 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -697,6 +697,7 @@ impl FieldSet { /// /// [`Identifier`]: super::callsite::Identifier /// [`Callsite`]: super::callsite::Callsite + #[inline] pub(crate) fn callsite(&self) -> callsite::Identifier { callsite::Identifier(self.callsite.0) } @@ -734,6 +735,7 @@ impl FieldSet { } /// Returns an iterator over the `Field`s in this `FieldSet`. + #[inline] pub fn iter(&self) -> Iter { let idxs = 0..self.len(); Iter { @@ -837,6 +839,7 @@ impl PartialEq for FieldSet { impl Iterator for Iter { type Item = Field; + #[inline] fn next(&mut self) -> Option { let i = self.idxs.next()?; Some(Field { diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs index 73fdc03a22..7487f9d403 100644 --- a/tracing-core/src/metadata.rs +++ b/tracing-core/src/metadata.rs @@ -274,6 +274,7 @@ impl<'a> Metadata<'a> { } /// Returns the names of the fields on the described span or event. + #[inline] pub fn fields(&self) -> &field::FieldSet { &self.fields } diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 8b93c14546..138a80a40d 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -2314,7 +2314,7 @@ macro_rules! valueset { ) }; - // Remainder is unparseable, but exists --- must be format args! + // Remainder is unparsable, but exists --- must be format args! (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) }; From 5722b4503e01686ef9ca00882003ee8579e3e18e Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Thu, 13 Apr 2023 07:06:59 +1000 Subject: [PATCH 008/101] Remove dep `cfg-if` from tracing (#2553) ## Motivation Same reason as https://github.com/rust-lang/log/pull/536 : `cfg_if` is only used in a single place and `tracing` is used by many other crates, so even removing one dependency will be beneficial. ## Solution Remove dependency `cfg-if` and replace `cfg_if::cfg_if!` with a `const fn get_max_level_inner() -> LevelFilter` and uses `if cfg!(...)` inside. Using if in const function is stablised in [1.46](https://github.com/rust-lang/rust/blob/master/RELEASES.md#version-1460-2020-08-27) so this should work fine in msrv 1.56 Signed-off-by: Jiahao XU --- tracing/Cargo.toml | 1 - tracing/src/level_filters.rs | 54 +++++++++++++++++++----------------- 2 files changed, 29 insertions(+), 26 deletions(-) diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 55221efeb5..5afe42fc09 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -34,7 +34,6 @@ rust-version = "1.56.0" tracing-core = { path = "../tracing-core", version = "0.2", default-features = false } log = { version = "0.4.17", optional = true } tracing-attributes = { path = "../tracing-attributes", version = "0.2", optional = true } -cfg-if = "1.0.0" pin-project-lite = "0.2.9" [dev-dependencies] diff --git a/tracing/src/level_filters.rs b/tracing/src/level_filters.rs index 4181dc3bf7..4e56ada2c5 100644 --- a/tracing/src/level_filters.rs +++ b/tracing/src/level_filters.rs @@ -63,32 +63,36 @@ pub use tracing_core::{metadata::ParseLevelFilterError, LevelFilter}; /// determine if those spans or events are enabled. /// /// [module-level documentation]: self#compile-time-filters -pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL; +pub const STATIC_MAX_LEVEL: LevelFilter = get_max_level_inner(); -cfg_if::cfg_if! { - if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::OFF; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::WARN; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::INFO; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; - } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] { - const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; - } else if #[cfg(feature = "max_level_off")] { - const MAX_LEVEL: LevelFilter = LevelFilter::OFF; - } else if #[cfg(feature = "max_level_error")] { - const MAX_LEVEL: LevelFilter = LevelFilter::ERROR; - } else if #[cfg(feature = "max_level_warn")] { - const MAX_LEVEL: LevelFilter = LevelFilter::WARN; - } else if #[cfg(feature = "max_level_info")] { - const MAX_LEVEL: LevelFilter = LevelFilter::INFO; - } else if #[cfg(feature = "max_level_debug")] { - const MAX_LEVEL: LevelFilter = LevelFilter::DEBUG; +const fn get_max_level_inner() -> LevelFilter { + if cfg!(not(debug_assertions)) { + if cfg!(feature = "release_max_level_off") { + LevelFilter::OFF + } else if cfg!(feature = "release_max_level_error") { + LevelFilter::ERROR + } else if cfg!(feature = "release_max_level_warn") { + LevelFilter::WARN + } else if cfg!(feature = "release_max_level_info") { + LevelFilter::INFO + } else if cfg!(feature = "release_max_level_debug") { + LevelFilter::DEBUG + } else { + // Same as branch cfg!(feature = "release_max_level_trace") + LevelFilter::TRACE + } + } else if cfg!(feature = "max_level_off") { + LevelFilter::OFF + } else if cfg!(feature = "max_level_error") { + LevelFilter::ERROR + } else if cfg!(feature = "max_level_warn") { + LevelFilter::WARN + } else if cfg!(feature = "max_level_info") { + LevelFilter::INFO + } else if cfg!(feature = "max_level_debug") { + LevelFilter::DEBUG } else { - const MAX_LEVEL: LevelFilter = LevelFilter::TRACE; + // Same as branch cfg!(feature = "max_level_trace") + LevelFilter::TRACE } } From 9744ec03f925a4ea3de8578e7a27f2fe2a5f1655 Mon Sep 17 00:00:00 2001 From: keepsimple1 Date: Thu, 13 Apr 2023 13:24:51 -0700 Subject: [PATCH 009/101] subscriber: update time crate to 0.3.18 (#2550) Bump up the version of the `time` crate so that we don't need to build with `--cfg unsound_local_offset` for using `fmt::time::LocalTime`. --- tracing-subscriber/Cargo.toml | 4 ++-- tracing-subscriber/src/fmt/time/time_crate.rs | 24 ------------------- 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index d39da0cdc8..8bad1b7729 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -49,7 +49,7 @@ once_cell = { optional = true, version = "1.13.0" } # fmt tracing-log = { path = "../tracing-log", version = "0.2", optional = true, default-features = false, features = ["log-tracer", "std"] } nu-ansi-term = { version = "0.46.0", optional = true } -time = { version = "0.3.2", features = ["formatting"], optional = true } +time = { version = "0.3.18", features = ["formatting"], optional = true } # only required by the json feature serde_json = { version = "1.0.82", optional = true } @@ -73,7 +73,7 @@ regex = { version = "1.6.0", default-features = false, features = ["std"] } tracing-futures = { path = "../tracing-futures", version = "0.3", default-features = false, features = ["std-future", "std"] } tokio = { version = "1.20.0", features = ["rt", "macros"] } # Enable the `time` crate's `macros` feature, for examples. -time = { version = "0.3.2", features = ["formatting", "macros"] } +time = { version = "0.3.18", features = ["formatting", "macros"] } [badges] maintenance = { status = "experimental" } diff --git a/tracing-subscriber/src/fmt/time/time_crate.rs b/tracing-subscriber/src/fmt/time/time_crate.rs index 656677e8d4..5092588b56 100644 --- a/tracing-subscriber/src/fmt/time/time_crate.rs +++ b/tracing-subscriber/src/fmt/time/time_crate.rs @@ -6,17 +6,6 @@ use time::{format_description::well_known, formatting::Formattable, OffsetDateTi /// /// To format the current [UTC time] instead, use the [`UtcTime`] type. /// -///

-///
-///     Warning: The time
-///     crate must be compiled with --cfg unsound_local_offset in order to use
-///     local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
-///     events will be logged without timestamps.
-///
-///    See the time
-///    documentation for more details.
-/// 
-/// /// [local time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_local /// [UTC time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_utc /// [formatter]: https://docs.rs/time/0.3/time/formatting/trait.Formattable.html @@ -76,19 +65,6 @@ impl LocalTime { /// [`time` crate] with the provided provided format. The format may be any /// type that implements the [`Formattable`] trait. /// - /// - ///
- ///
-    ///     Warning: The 
-    ///     time crate must be compiled with --cfg
-    ///     unsound_local_offset in order to use local timestamps. When this
-    ///     cfg is not enabled, local timestamps cannot be recorded, and
-    ///     events will be logged without timestamps.
-    ///
-    ///    See the 
-    ///    time documentation for more details.
-    /// 
- /// /// Typically, the format will be a format description string, or one of the /// `time` crate's [well-known formats]. /// From ca26746391b1ce55bbe49883d021dcef91c49c63 Mon Sep 17 00:00:00 2001 From: klensy Date: Fri, 14 Apr 2023 00:12:53 +0300 Subject: [PATCH 010/101] attributes: remove unused syn's feature visit (#2530) Remove unused `syn`s feature `visit` --- tracing-attributes/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 12000c378e..53f67613cb 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -35,7 +35,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.40" -syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } +syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } quote = "1.0.20" [dev-dependencies] From 539ffbe189dfd7bb89055097043282d268aaaa94 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Thu, 13 Apr 2023 23:29:33 +0200 Subject: [PATCH 011/101] attributes: update to syn 2.0 (#2516) ## Motivation syn 2.0 is out! ## Solution Update to syn 2.0 :rocket: Co-authored-by: David Barsky Co-authored-by: Eliza Weisman --- tracing-attributes/Cargo.toml | 4 ++-- tracing-attributes/src/attr.rs | 6 +++--- tracing-attributes/src/expand.rs | 7 ++----- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 53f67613cb..ae20aa23da 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -35,7 +35,7 @@ proc-macro = true [dependencies] proc-macro2 = "1.0.40" -syn = { version = "1.0.98", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } +syn = { version = "2.0", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] } quote = "1.0.20" [dev-dependencies] @@ -43,7 +43,7 @@ tracing = { path = "../tracing", version = "0.2" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } tokio-test = "0.4.2" tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["env-filter"] } -async-trait = "0.1.56" +async-trait = "0.1.67" trybuild = "1.0.64" rustversion = "1.0.9" diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 6bb586ef40..a1854c7e80 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -239,7 +239,7 @@ impl Parse for Skips { let _ = input.parse::(); let content; let _ = syn::parenthesized!(content in input); - let names: Punctuated = content.parse_terminated(Ident::parse_any)?; + let names = content.parse_terminated(Ident::parse_any, Token![,])?; let mut skips = HashSet::new(); for name in names { if skips.contains(&name) { @@ -290,7 +290,7 @@ impl Parse for Fields { let _ = input.parse::(); let content; let _ = syn::parenthesized!(content in input); - let fields: Punctuated<_, Token![,]> = content.parse_terminated(Field::parse)?; + let fields = content.parse_terminated(Field::parse, Token![,])?; Ok(Self(fields)) } } @@ -335,7 +335,7 @@ impl ToTokens for Field { let name = &self.name; let kind = &self.kind; tokens.extend(quote! { - #name = #kind#value + #name = #kind #value }) } else if self.kind == FieldKind::Value { // XXX(eliza): I don't like that fields without values produce diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 2e1cda8e3d..d71366c663 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -477,10 +477,7 @@ fn param_names(pat: Pat, record_type: RecordType) -> Box Box::new( + Pat::TupleStruct(PatTupleStruct { elems, .. }) => Box::new( elems .into_iter() .flat_map(|p| param_names(p, RecordType::Debug)), @@ -564,7 +561,7 @@ impl<'block> AsyncInfo<'block> { // last expression of the block: it determines the return value of the // block, this is quite likely a `Box::pin` statement or an async block let (last_expr_stmt, last_expr) = block.stmts.iter().rev().find_map(|stmt| { - if let Stmt::Expr(expr) = stmt { + if let Stmt::Expr(expr, _semi) = stmt { Some((stmt, expr)) } else { None From a351b978d59b79560c49842010537f827cebfb38 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Thu, 13 Apr 2023 14:58:19 -0700 Subject: [PATCH 012/101] subscriber: revert "subscriber: update time crate to 0.3.18 (#2550)" This reverts commit 9744ec03f925a4ea3de8578e7a27f2fe2a5f1655. This change breaks MSRV compatibility, and was accidentally auto-merged due to what appears to be an issue with the CI configuration, which (apparently) doesn't require the MSRV minimal-versions check runs to complete before allowing a branch to merge. We'll have to solve the original issue here through documentation for now. See [this comment] for details. [this comment]: https://github.com/tokio-rs/tracing/pull/2550#issuecomment-1507656030 --- tracing-subscriber/Cargo.toml | 4 ++-- tracing-subscriber/src/fmt/time/time_crate.rs | 24 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 8bad1b7729..d39da0cdc8 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -49,7 +49,7 @@ once_cell = { optional = true, version = "1.13.0" } # fmt tracing-log = { path = "../tracing-log", version = "0.2", optional = true, default-features = false, features = ["log-tracer", "std"] } nu-ansi-term = { version = "0.46.0", optional = true } -time = { version = "0.3.18", features = ["formatting"], optional = true } +time = { version = "0.3.2", features = ["formatting"], optional = true } # only required by the json feature serde_json = { version = "1.0.82", optional = true } @@ -73,7 +73,7 @@ regex = { version = "1.6.0", default-features = false, features = ["std"] } tracing-futures = { path = "../tracing-futures", version = "0.3", default-features = false, features = ["std-future", "std"] } tokio = { version = "1.20.0", features = ["rt", "macros"] } # Enable the `time` crate's `macros` feature, for examples. -time = { version = "0.3.18", features = ["formatting", "macros"] } +time = { version = "0.3.2", features = ["formatting", "macros"] } [badges] maintenance = { status = "experimental" } diff --git a/tracing-subscriber/src/fmt/time/time_crate.rs b/tracing-subscriber/src/fmt/time/time_crate.rs index 5092588b56..656677e8d4 100644 --- a/tracing-subscriber/src/fmt/time/time_crate.rs +++ b/tracing-subscriber/src/fmt/time/time_crate.rs @@ -6,6 +6,17 @@ use time::{format_description::well_known, formatting::Formattable, OffsetDateTi /// /// To format the current [UTC time] instead, use the [`UtcTime`] type. /// +///
+///
+///     Warning: The time
+///     crate must be compiled with --cfg unsound_local_offset in order to use
+///     local timestamps. When this cfg is not enabled, local timestamps cannot be recorded, and
+///     events will be logged without timestamps.
+///
+///    See the time
+///    documentation for more details.
+/// 
+/// /// [local time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_local /// [UTC time]: https://docs.rs/time/0.3/time/struct.OffsetDateTime.html#method.now_utc /// [formatter]: https://docs.rs/time/0.3/time/formatting/trait.Formattable.html @@ -65,6 +76,19 @@ impl LocalTime { /// [`time` crate] with the provided provided format. The format may be any /// type that implements the [`Formattable`] trait. /// + /// + ///
+ ///
+    ///     Warning: The 
+    ///     time crate must be compiled with --cfg
+    ///     unsound_local_offset in order to use local timestamps. When this
+    ///     cfg is not enabled, local timestamps cannot be recorded, and
+    ///     events will be logged without timestamps.
+    ///
+    ///    See the 
+    ///    time documentation for more details.
+    /// 
+ /// /// Typically, the format will be a format description string, or one of the /// `time` crate's [well-known formats]. /// From 1cb523b87d3dc113ecc33fdf6cbee8ae63fbfec2 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Fri, 14 Apr 2023 21:38:48 +0200 Subject: [PATCH 013/101] subscriber: add ability to disable ANSI without crate feature (#2532) ## Motivation Currently it is not possible to disable ANSI in `fmt::Subscriber` without enabling the "ansi" crate feature. This makes it difficult for users to implement interoperable settings that are controllable with crate features without having to pull in the dependencies "ansi" does. I hit this while writing an application with multiple logging options set during compile-time and I wanted to cut down on dependencies if possible. ## Solution This changes `fmt::Subscriber::with_ansi()` to not require the "ansi" feature flag. This way, `with_ansi(false)` can be called even when the "ansi" feature is disabled. Calling `with_ansi(true)` when the "ansi" feature is not enabled will panic in debug mode, or print a warning if debug assertions are disabled. Co-authored-by: Eliza Weisman --- tracing-subscriber/src/fmt/fmt_subscriber.rs | 28 +++++++++++++++++--- tracing-subscriber/src/fmt/mod.rs | 18 ++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index 9887b711ff..1b89039261 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -274,10 +274,32 @@ impl Subscriber { } } - /// Enable ANSI terminal colors for formatted output. - #[cfg(feature = "ansi")] - #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] + /// Sets whether or not the formatter emits ANSI terminal escape codes + /// for colors and other text formatting. + /// + /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi" + /// crate feature flag. Calling `with_ansi(true)` without the "ansi" + /// feature flag enabled will panic if debug assertions are enabled, or + /// print a warning otherwise. + /// + /// This method itself is still available without the feature flag. This + /// is to allow ANSI escape codes to be explicitly *disabled* without + /// having to opt-in to the dependencies required to emit ANSI formatting. + /// This way, code which constructs a formatter that should never emit + /// ANSI escape codes can ensure that they are not used, regardless of + /// whether or not other crates in the dependency graph enable the "ansi" + /// feature flag. pub fn with_ansi(self, ansi: bool) -> Self { + #[cfg(not(feature = "ansi"))] + if ansi { + const ERROR: &str = + "tracing-subscriber: the `ansi` crate feature is required to enable ANSI terminal colors"; + #[cfg(debug_assertions)] + panic!("{}", ERROR); + #[cfg(not(debug_assertions))] + eprintln!("{}", ERROR); + } + Subscriber { is_ansi: ansi, ..self diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs index ba5066d2ec..38acf4d092 100644 --- a/tracing-subscriber/src/fmt/mod.rs +++ b/tracing-subscriber/src/fmt/mod.rs @@ -610,9 +610,21 @@ where } } - /// Enable ANSI terminal colors for formatted output. - #[cfg(feature = "ansi")] - #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] + /// Sets whether or not the formatter emits ANSI terminal escape codes + /// for colors and other text formatting. + /// + /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi" + /// crate feature flag. Calling `with_ansi(true)` without the "ansi" + /// feature flag enabled will panic if debug assertions are enabled, or + /// print a warning otherwise. + /// + /// This method itself is still available without the feature flag. This + /// is to allow ANSI escape codes to be explicitly *disabled* without + /// having to opt-in to the dependencies required to emit ANSI formatting. + /// This way, code which constructs a formatter that should never emit + /// ANSI escape codes can ensure that they are not used, regardless of + /// whether or not other crates in the dependency graph enable the "ansi" + /// feature flag. pub fn with_ansi(self, ansi: bool) -> CollectorBuilder, F, W> { CollectorBuilder { inner: self.inner.with_ansi(ansi), From 8aae1c37b091963aafdd336b1168fe5a24c0b4f0 Mon Sep 17 00:00:00 2001 From: Ilya Salauyeu <47687266+ilslv@users.noreply.github.com> Date: Wed, 19 Apr 2023 00:55:46 +0200 Subject: [PATCH 014/101] tracing, tracing-futures: instrument `Future` inside `Drop` (#2562) ## Motivation Currently it is not possible to disable ANSI in `fmt::Subscriber` without enabling the "ansi" crate feature. This makes it difficult for users to implement interoperable settings that are controllable with crate features without having to pull in the dependencies "ansi" does. I hit this while writing an application with multiple logging options set during compile-time and I wanted to cut down on dependencies if possible. ## Solution This changes `fmt::Subscriber::with_ansi()` to not require the "ansi" feature flag. This way, `with_ansi(false)` can be called even when the "ansi" feature is disabled. Calling `with_ansi(true)` when the "ansi" feature is not enabled will panic in debug mode, or print a warning if debug assertions are disabled. Co-authored-by: daxpedda Co-authored-by: Eliza Weisman --- tracing-attributes/tests/async_fn.rs | 26 ++++ tracing-attributes/tests/err.rs | 4 + tracing-attributes/tests/follows_from.rs | 2 + tracing-attributes/tests/ret.rs | 2 + tracing-futures/Cargo.toml | 1 + tracing-futures/src/executor/futures_01.rs | 22 ++- tracing-futures/src/lib.rs | 150 +++++++++++++++------ tracing-futures/tests/std_future.rs | 63 +++++++++ tracing/Cargo.toml | 1 + tracing/src/instrument.rs | 86 ++++++++++-- tracing/tests/instrument.rs | 67 +++++++++ 11 files changed, 361 insertions(+), 63 deletions(-) create mode 100644 tracing/tests/instrument.rs diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs index 38675a451a..29481c4e7c 100644 --- a/tracing-attributes/tests/async_fn.rs +++ b/tracing-attributes/tests/async_fn.rs @@ -90,6 +90,8 @@ fn async_fn_only_enters_for_polls() { .exit(expect::span().named("test_async_fn")) .enter(expect::span().named("test_async_fn")) .exit(expect::span().named("test_async_fn")) + .enter(expect::span().named("test_async_fn")) + .exit(expect::span().named("test_async_fn")) .drop_span(expect::span().named("test_async_fn")) .only() .run_with_handle(); @@ -120,8 +122,12 @@ fn async_fn_nested() { .enter(span2.clone()) .event(expect::event().with_fields(expect::field("nested").with_value(&true))) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -199,13 +205,19 @@ fn async_fn_with_async_trait() { .enter(span3.clone()) .event(expect::event().with_fields(expect::field("val").with_value(&2u64))) .exit(span3.clone()) + .enter(span3.clone()) + .exit(span3.clone()) .drop_span(span3) .new_span(span2.clone().with_field(expect::field("self"))) .enter(span2.clone()) .event(expect::event().with_fields(expect::field("val").with_value(&5u64))) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -256,6 +268,8 @@ fn async_fn_with_async_trait_and_fields_expressions() { ) .enter(span.clone()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -331,8 +345,12 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() { .with_field(expect::field("Self").with_value(&std::any::type_name::())), ) .enter(span4.clone()) + .exit(span4.clone()) + .enter(span4.clone()) .exit(span4) .exit(span2.clone()) + .enter(span2.clone()) + .exit(span2.clone()) .drop_span(span2) .new_span( span3 @@ -341,6 +359,8 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() { ) .enter(span3.clone()) .exit(span3.clone()) + .enter(span3.clone()) + .exit(span3.clone()) .drop_span(span3) .only() .run_with_handle(); @@ -382,6 +402,8 @@ fn out_of_scope_fields() { .new_span(span.clone()) .enter(span.clone()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -417,6 +439,8 @@ fn manual_impl_future() { .enter(span.clone()) .event(poll_event()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -448,6 +472,8 @@ fn manual_box_pin() { .enter(span.clone()) .event(poll_event()) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs index ffd30b3742..8777502b26 100644 --- a/tracing-attributes/tests/err.rs +++ b/tracing-attributes/tests/err.rs @@ -78,6 +78,8 @@ fn test_async() { .enter(span.clone()) .event(expect::event().at_level(Level::ERROR)) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); @@ -132,6 +134,8 @@ fn test_mut_async() { .enter(span.clone()) .event(expect::event().at_level(Level::ERROR)) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); diff --git a/tracing-attributes/tests/follows_from.rs b/tracing-attributes/tests/follows_from.rs index 266f7b59a3..acfec81f4b 100644 --- a/tracing-attributes/tests/follows_from.rs +++ b/tracing-attributes/tests/follows_from.rs @@ -58,6 +58,8 @@ fn follows_from_async_test() { .follows_from(consequence.clone(), cause_b) .follows_from(consequence.clone(), cause_c) .enter(consequence.clone()) + .exit(consequence.clone()) + .enter(consequence.clone()) .exit(consequence) .only() .run_with_handle(); diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs index 0a2d2ed191..b5c4b4e07b 100644 --- a/tracing-attributes/tests/ret.rs +++ b/tracing-attributes/tests/ret.rs @@ -138,6 +138,8 @@ fn test_async() { .at_level(Level::INFO), ) .exit(span.clone()) + .enter(span.clone()) + .exit(span.clone()) .drop_span(span) .only() .run_with_handle(); diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index d4ef1f71b6..f3b4dc1d61 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -40,6 +40,7 @@ tokio-threadpool = "0.1.18" mio = "0.6.23" [dev-dependencies] +futures = "0.3.21" tokio-test = "0.4.2" tracing-core = { path = "../tracing-core", version = "0.2" } tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] } diff --git a/tracing-futures/src/executor/futures_01.rs b/tracing-futures/src/executor/futures_01.rs index 56ba6e3c42..7d4b674af8 100644 --- a/tracing-futures/src/executor/futures_01.rs +++ b/tracing-futures/src/executor/futures_01.rs @@ -4,16 +4,6 @@ use futures_01::{ Future, }; -macro_rules! deinstrument_err { - ($e:expr) => { - $e.map_err(|e| { - let kind = e.kind(); - let future = e.into_future().inner; - ExecuteError::new(kind, future) - }) - }; -} - impl Executor for Instrumented where T: Executor>, @@ -21,7 +11,11 @@ where { fn execute(&self, future: F) -> Result<(), ExecuteError> { let future = future.instrument(self.span.clone()); - deinstrument_err!(self.inner.execute(future)) + self.inner.execute(future).map_err(|e| { + let kind = e.kind(); + let future = e.into_future().into_inner(); + ExecuteError::new(kind, future) + }) } } @@ -32,7 +26,11 @@ where { fn execute(&self, future: F) -> Result<(), ExecuteError> { let future = self.with_dispatch(future); - deinstrument_err!(self.inner.execute(future)) + self.inner.execute(future).map_err(|e| { + let kind = e.kind(); + let future = e.into_future().inner; + ExecuteError::new(kind, future) + }) } } diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs index 5af13a0a5b..262d15fc87 100644 --- a/tracing-futures/src/lib.rs +++ b/tracing-futures/src/lib.rs @@ -103,7 +103,11 @@ use pin_project_lite::pin_project; #[cfg(feature = "std-future")] -use core::{pin::Pin, task::Context}; +use core::{ + mem::{self, ManuallyDrop}, + pin::Pin, + task::Context, +}; #[cfg(feature = "std")] use tracing::{dispatch, Dispatch}; @@ -118,13 +122,13 @@ pub mod executor; /// /// [span]: mod@tracing::span pub trait Instrument: Sized { - /// Instruments this type with the provided `Span`, returning an - /// `Instrumented` wrapper. + /// Instruments this type with the provided [`Span`], returning an + /// [`Instrumented`] wrapper. /// - /// If the instrumented type is a future, stream, or sink, the attached `Span` - /// will be [entered] every time it is polled. If the instrumented type - /// is a future executor, every future spawned on that executor will be - /// instrumented by the attached `Span`. + /// If the instrumented type is a future, stream, or sink, the attached + /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If + /// the instrumented type is a future executor, every future spawned on that + /// executor will be instrumented by the attached [`Span`]. /// /// # Examples /// @@ -145,18 +149,22 @@ pub trait Instrument: Sized { /// # } /// ``` /// - /// [entered]: tracing::span::Span::enter() + /// [entered]: Span::enter() fn instrument(self, span: Span) -> Instrumented { - Instrumented { inner: self, span } + #[cfg(feature = "std-future")] + let inner = ManuallyDrop::new(self); + #[cfg(not(feature = "std-future"))] + let inner = self; + Instrumented { inner, span } } - /// Instruments this type with the [current] `Span`, returning an - /// `Instrumented` wrapper. + /// Instruments this type with the [current] [`Span`], returning an + /// [`Instrumented`] wrapper. /// - /// If the instrumented type is a future, stream, or sink, the attached `Span` - /// will be [entered] every time it is polled. If the instrumented type - /// is a future executor, every future spawned on that executor will be - /// instrumented by the attached `Span`. + /// If the instrumented type is a future, stream, or sink, the attached + /// [`Span`] will be [entered] every time it is polled or [`Drop`]ped. If + /// the instrumented type is a future executor, every future spawned on that + /// executor will be instrumented by the attached [`Span`]. /// /// This can be used to propagate the current span when spawning a new future. /// @@ -180,8 +188,8 @@ pub trait Instrument: Sized { /// # } /// ``` /// - /// [current]: tracing::span::Span::current() - /// [entered]: tracing::span::Span::enter() + /// [current]: Span::current() + /// [entered]: Span::enter() #[inline] fn in_current_span(self) -> Instrumented { self.instrument(Span::current()) @@ -240,12 +248,56 @@ pub trait WithCollector: Sized { #[cfg(feature = "std-future")] pin_project! { /// A future, stream, sink, or executor that has been instrumented with a `tracing` span. + #[project = InstrumentedProj] + #[project_ref = InstrumentedProjRef] #[derive(Debug, Clone)] pub struct Instrumented { + // `ManuallyDrop` is used here to to enter instrument `Drop` by entering + // `Span` and executing `ManuallyDrop::drop`. #[pin] - inner: T, + inner: ManuallyDrop, span: Span, } + + impl PinnedDrop for Instrumented { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + let _enter = this.span.enter(); + // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't + // different from wrapping `T` in `Option` and calling + // `Pin::set(&mut this.inner, None)`, except avoiding + // additional memory overhead. + // 2. `ManuallyDrop::drop()` is safe, because + // `PinnedDrop::drop()` is guaranteed to be called only + // once. + unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) } + } + } +} + +#[cfg(feature = "std-future")] +impl<'a, T> InstrumentedProj<'a, T> { + /// Get a mutable reference to the [`Span`] a pinned mutable reference to + /// the wrapped type. + fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) }; + (self.span, inner) + } +} + +#[cfg(feature = "std-future")] +impl<'a, T> InstrumentedProjRef<'a, T> { + /// Get a reference to the [`Span`] a pinned reference to the wrapped type. + fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked(|v| &**v) }; + (self.span, inner) + } } /// A future, stream, sink, or executor that has been instrumented with a `tracing` span. @@ -287,9 +339,9 @@ impl core::future::Future for Instrumented { type Output = T::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> core::task::Poll { - let this = self.project(); - let _enter = this.span.enter(); - this.inner.poll(cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + inner.poll(cx) } } @@ -346,9 +398,9 @@ impl futures::Stream for Instrumented { self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_next(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_next(inner, cx) } } @@ -364,33 +416,33 @@ where self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_ready(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_ready(inner, cx) } fn start_send(self: Pin<&mut Self>, item: I) -> Result<(), Self::Error> { - let this = self.project(); - let _enter = this.span.enter(); - T::start_send(this.inner, item) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::start_send(inner, item) } fn poll_flush( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_flush(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_flush(inner, cx) } fn poll_close( self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> futures::task::Poll> { - let this = self.project(); - let _enter = this.span.enter(); - T::poll_close(this.inner, cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + T::poll_close(inner, cx) } } @@ -419,20 +471,36 @@ impl Instrumented { #[cfg(feature = "std-future")] #[cfg_attr(docsrs, doc(cfg(feature = "std-future")))] pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> { - self.project_ref().inner + self.project_ref().span_and_inner_pin_ref().1 } /// Get a pinned mutable reference to the wrapped type. #[cfg(feature = "std-future")] #[cfg_attr(docsrs, doc(cfg(feature = "std-future")))] pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().inner + self.project().span_and_inner_pin_mut().1 } /// Consumes the `Instrumented`, returning the wrapped type. /// /// Note that this drops the span. pub fn into_inner(self) -> T { + #[cfg(feature = "std-future")] + { + // To manually destructure `Instrumented` without `Drop`, we save + // pointers to the fields and use `mem::forget` to leave those pointers + // valid. + let span: *const Span = &self.span; + let inner: *const ManuallyDrop = &self.inner; + mem::forget(self); + // SAFETY: Those pointers are valid for reads, because `Drop` didn't + // run, and properly aligned, because `Instrumented` isn't + // `#[repr(packed)]`. + let _span = unsafe { span.read() }; + let inner = unsafe { inner.read() }; + ManuallyDrop::into_inner(inner) + } + #[cfg(not(feature = "std-future"))] self.inner } } @@ -570,6 +638,8 @@ mod tests { .exit(expect::span().named("foo")) .enter(expect::span().named("foo")) .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) .drop_span(expect::span().named("foo")) .only() .run_with_handle(); @@ -589,6 +659,8 @@ mod tests { .exit(expect::span().named("foo")) .enter(expect::span().named("foo")) .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) .drop_span(expect::span().named("foo")) .only() .run_with_handle(); @@ -613,6 +685,8 @@ mod tests { .exit(expect::span().named("foo")) .enter(expect::span().named("foo")) .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) .drop_span(expect::span().named("foo")) .run_with_handle(); with_default(collector, || { diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs index cd9656f153..ebba8cf084 100644 --- a/tracing-futures/tests/std_future.rs +++ b/tracing-futures/tests/std_future.rs @@ -1,3 +1,6 @@ +use std::{future::Future, pin::Pin, task}; + +use futures::FutureExt as _; use tracing::Instrument; use tracing::{collect::with_default, Level}; use tracing_mock::*; @@ -9,6 +12,8 @@ fn enter_exit_is_reasonable() { .exit(expect::span().named("foo")) .enter(expect::span().named("foo")) .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) .drop_span(expect::span().named("foo")) .only() .run_with_handle(); @@ -26,6 +31,8 @@ fn error_ends_span() { .exit(expect::span().named("foo")) .enter(expect::span().named("foo")) .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) .drop_span(expect::span().named("foo")) .only() .run_with_handle(); @@ -35,3 +42,59 @@ fn error_ends_span() { }); handle.assert_finished(); } + +#[test] +fn span_on_drop() { + #[derive(Clone, Debug)] + struct AssertSpanOnDrop; + + impl Drop for AssertSpanOnDrop { + fn drop(&mut self) { + tracing::info!("Drop"); + } + } + + struct Fut(Option); + + impl Future for Fut { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll { + self.set(Fut(None)); + task::Poll::Ready(()) + } + } + + let collector = collector::mock() + .enter(expect::span().named("foo")) + .event( + expect::event() + .with_contextual_parent(Some("foo")) + .at_level(Level::INFO), + ) + .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) + .drop_span(expect::span().named("foo")) + .enter(expect::span().named("bar")) + .event( + expect::event() + .with_contextual_parent(Some("bar")) + .at_level(Level::INFO), + ) + .exit(expect::span().named("bar")) + .drop_span(expect::span().named("bar")) + .only() + .run(); + + with_default(collector, || { + // polled once + Fut(Some(AssertSpanOnDrop)) + .instrument(tracing::span!(Level::TRACE, "foo")) + .now_or_never() + .unwrap(); + + // never polled + drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar"))); + }); +} diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index 5afe42fc09..2d7bf1120e 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -38,6 +38,7 @@ pin-project-lite = "0.2.9" [dev-dependencies] criterion = { version = "0.3.6", default_features = false } +futures = { version = "0.3.21", default_features = false } log = "0.4.17" tracing-mock = { path = "../tracing-mock" } diff --git a/tracing/src/instrument.rs b/tracing/src/instrument.rs index 98f0da9bd1..208c2bd0cf 100644 --- a/tracing/src/instrument.rs +++ b/tracing/src/instrument.rs @@ -1,7 +1,11 @@ use crate::span::Span; -use core::pin::Pin; -use core::task::{Context, Poll}; -use core::{future::Future, marker::Sized}; +use core::{ + future::Future, + marker::Sized, + mem::{self, ManuallyDrop}, + pin::Pin, + task::{Context, Poll}, +}; use pin_project_lite::pin_project; #[cfg(feature = "std")] @@ -18,7 +22,7 @@ pub trait Instrument: Sized { /// `Instrumented` wrapper. /// /// The attached [`Span`] will be [entered] every time the instrumented - /// [`Future`] is polled. + /// [`Future`] is polled or [`Drop`]ped. /// /// # Examples /// @@ -80,14 +84,17 @@ pub trait Instrument: Sized { /// [disabled]: super::Span::is_disabled() /// [`Future`]: std::future::Future fn instrument(self, span: Span) -> Instrumented { - Instrumented { inner: self, span } + Instrumented { + inner: ManuallyDrop::new(self), + span, + } } /// Instruments this type with the [current] [`Span`], returning an /// `Instrumented` wrapper. /// /// The attached [`Span`] will be [entered] every time the instrumented - /// [`Future`] is polled. + /// [`Future`] is polled or [`Drop`]ped. /// /// This can be used to propagate the current span when spawning a new future. /// @@ -285,13 +292,55 @@ pin_project! { /// /// [`Future`]: std::future::Future /// [`Span`]: crate::Span + #[project = InstrumentedProj] + #[project_ref = InstrumentedProjRef] #[derive(Debug, Clone)] #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Instrumented { + // `ManuallyDrop` is used here to to enter instrument `Drop` by entering + // `Span` and executing `ManuallyDrop::drop`. #[pin] - inner: T, + inner: ManuallyDrop, span: Span, } + + impl PinnedDrop for Instrumented { + fn drop(this: Pin<&mut Self>) { + let this = this.project(); + let _enter = this.span.enter(); + // SAFETY: 1. `Pin::get_unchecked_mut()` is safe, because this isn't + // different from wrapping `T` in `Option` and calling + // `Pin::set(&mut this.inner, None)`, except avoiding + // additional memory overhead. + // 2. `ManuallyDrop::drop()` is safe, because + // `PinnedDrop::drop()` is guaranteed to be called only + // once. + unsafe { ManuallyDrop::drop(this.inner.get_unchecked_mut()) } + } + } +} + +impl<'a, T> InstrumentedProj<'a, T> { + /// Get a mutable reference to the [`Span`] a pinned mutable reference to + /// the wrapped type. + fn span_and_inner_pin_mut(self) -> (&'a mut Span, Pin<&'a mut T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked_mut(|v| &mut **v) }; + (self.span, inner) + } +} + +impl<'a, T> InstrumentedProjRef<'a, T> { + /// Get a reference to the [`Span`] a pinned reference to the wrapped type. + fn span_and_inner_pin_ref(self) -> (&'a Span, Pin<&'a T>) { + // SAFETY: As long as `ManuallyDrop` does not move, `T` won't move + // and `inner` is valid, because `ManuallyDrop::drop` is called + // only inside `Drop` of the `Instrumented`. + let inner = unsafe { self.inner.map_unchecked(|v| &**v) }; + (self.span, inner) + } } // === impl Instrumented === @@ -300,9 +349,9 @@ impl Future for Instrumented { type Output = T::Output; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let this = self.project(); - let _enter = this.span.enter(); - this.inner.poll(cx) + let (span, inner) = self.project().span_and_inner_pin_mut(); + let _enter = span.enter(); + inner.poll(cx) } } @@ -331,19 +380,30 @@ impl Instrumented { /// Get a pinned reference to the wrapped type. pub fn inner_pin_ref(self: Pin<&Self>) -> Pin<&T> { - self.project_ref().inner + self.project_ref().span_and_inner_pin_ref().1 } /// Get a pinned mutable reference to the wrapped type. pub fn inner_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> { - self.project().inner + self.project().span_and_inner_pin_mut().1 } /// Consumes the `Instrumented`, returning the wrapped type. /// /// Note that this drops the span. pub fn into_inner(self) -> T { - self.inner + // To manually destructure `Instrumented` without `Drop`, we save + // pointers to the fields and use `mem::forget` to leave those pointers + // valid. + let span: *const Span = &self.span; + let inner: *const ManuallyDrop = &self.inner; + mem::forget(self); + // SAFETY: Those pointers are valid for reads, because `Drop` didn't + // run, and properly aligned, because `Instrumented` isn't + // `#[repr(packed)]`. + let _span = unsafe { span.read() }; + let inner = unsafe { inner.read() }; + ManuallyDrop::into_inner(inner) } } diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs new file mode 100644 index 0000000000..9cb4dfbb3f --- /dev/null +++ b/tracing/tests/instrument.rs @@ -0,0 +1,67 @@ +// These tests require the thread-local scoped dispatcher, which only works when +// we have a standard library. The behaviour being tested should be the same +// with the standard lib disabled. +#![cfg(feature = "std")] + +use std::{future::Future, pin::Pin, task}; + +use futures::FutureExt as _; +use tracing::{collect::with_default, Instrument as _, Level}; +use tracing_mock::*; + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn span_on_drop() { + #[derive(Clone, Debug)] + struct AssertSpanOnDrop; + + impl Drop for AssertSpanOnDrop { + fn drop(&mut self) { + tracing::info!("Drop"); + } + } + + struct Fut(Option); + + impl Future for Fut { + type Output = (); + + fn poll(mut self: Pin<&mut Self>, _: &mut task::Context<'_>) -> task::Poll { + self.set(Fut(None)); + task::Poll::Ready(()) + } + } + + let collector = collector::mock() + .enter(expect::span().named("foo")) + .event( + expect::event() + .with_contextual_parent(Some("foo")) + .at_level(Level::INFO), + ) + .exit(expect::span().named("foo")) + .enter(expect::span().named("foo")) + .exit(expect::span().named("foo")) + .drop_span(expect::span().named("foo")) + .enter(expect::span().named("bar")) + .event( + expect::event() + .with_contextual_parent(Some("bar")) + .at_level(Level::INFO), + ) + .exit(expect::span().named("bar")) + .drop_span(expect::span().named("bar")) + .only() + .run(); + + with_default(collector, || { + // polled once + Fut(Some(AssertSpanOnDrop)) + .instrument(tracing::span!(Level::TRACE, "foo")) + .now_or_never() + .unwrap(); + + // never polled + drop(Fut(Some(AssertSpanOnDrop)).instrument(tracing::span!(Level::TRACE, "bar"))); + }); +} From c59a7fb14d4642cb1c230ce68ffaa28642c00e5c Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 21 Apr 2023 13:47:44 -0700 Subject: [PATCH 015/101] attributes: update UI tests with the latest stable version of Rust (#2568) updated UI tests using TRYBUILD=overwrite with the latest stable version of Rust ## Motivation UI tests are failing on the latest stable version of Rust ## Solution Run `TRYBUILD=overwrite cargo test` to update the effected files. --- tracing-attributes/tests/ui/async_instrument.stderr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tracing-attributes/tests/ui/async_instrument.stderr b/tracing-attributes/tests/ui/async_instrument.stderr index 519b71e045..2c64b0c15e 100644 --- a/tracing-attributes/tests/ui/async_instrument.stderr +++ b/tracing-attributes/tests/ui/async_instrument.stderr @@ -16,7 +16,7 @@ error[E0308]: mismatched types 10 | "" | ^^- help: try using a conversion method: `.to_string()` | | - | expected struct `String`, found `&str` + | expected `String`, found `&str` | note: return type inferred to be `String` here --> tests/ui/async_instrument.rs:9:31 @@ -47,7 +47,7 @@ error[E0308]: mismatched types --> tests/ui/async_instrument.rs:23:5 | 23 | "" - | ^^ expected struct `Wrapper`, found `&str` + | ^^ expected `Wrapper<_>`, found `&str` | = note: expected struct `Wrapper<_>` found reference `&'static str` @@ -79,7 +79,7 @@ error[E0308]: mismatched types 36 | return ""; | ^^- help: try using a conversion method: `.to_string()` | | - | expected struct `String`, found `&str` + | expected `String`, found `&str` | note: return type inferred to be `String` here --> tests/ui/async_instrument.rs:34:28 From 998774eb7a9e8f5fe7020fa660fbcca9aaec2169 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 21 Apr 2023 13:59:25 -0700 Subject: [PATCH 016/101] subscriber: add "unicode-case" and "unicode-perl" features to `regex` dependency (#2566) ## Motivation Missing features for the `regex` crate were causing build time errors due to the the use of unicode characters in the regex without using the proper features within the regex crate ## Solution Add the missing feature flags. Fixes #2565 Authored-by: Devin Bidwell --- tracing-subscriber/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index d39da0cdc8..100da643ca 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -42,7 +42,7 @@ tracing-core = { path = "../tracing-core", version = "0.2", default-features = f # only required by the `env-filter` feature tracing = { optional = true, path = "../tracing", version = "0.2", default-features = false } matchers = { optional = true, version = "0.1.0" } -regex = { optional = true, version = "1.6.0", default-features = false, features = ["std"] } +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" } From bfc44ba2fd170f10e17f6f242b979517edb89b5f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 11 May 2023 18:09:52 +0200 Subject: [PATCH 017/101] docs: add SECURITY.md (#2591) We should make it explicit that tracing uses the security policy of Tokio. --- SECURITY.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..ff00c19168 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,9 @@ +# Security Policy + +Tracing is part of the Tokio project and uses the same security policy as [Tokio][tokio-security]. + +## Report a security issue + +The process for reporting an issue is the same as for [Tokio][tokio-security]. This includes private reporting via security@tokio.rs. + +[tokio-security]: https://github.com/tokio-rs/tokio/security/policy From 29146260fb4615d271d2e899ad95a753bb42915e Mon Sep 17 00:00:00 2001 From: Jason Heeris Date: Fri, 12 May 2023 01:59:37 +0800 Subject: [PATCH 018/101] subscriber: document `registry` feature requirement for `fmt` feature (#2590) ## Motivation This was inconsistent with other features, which mention their requirements, and a reader might assume they don't need to inspect the feature flags page or manifest. --- tracing-subscriber/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 5e7787028c..90757df699 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -54,7 +54,7 @@ //! similar to the [`env_logger` crate]. **Requires "std"**. //! - `fmt`: Enables the [`fmt`] module, which provides a subscriber //! implementation for printing formatted representations of trace events. -//! Enabled by default. **Requires "std"**. +//! Enabled by default. **Requires "registry" and "std"**. //! - `ansi`: Enables `fmt` support for ANSI terminal colors. Enabled by //! default. //! - `registry`: enables the [`registry`] module. Enabled by default. From e603c2a254d157a25a7a1fbfd4da46ad7e05f555 Mon Sep 17 00:00:00 2001 From: John Gillott <8356592+johngillott@users.noreply.github.com> Date: Fri, 9 Jun 2023 15:17:08 +0100 Subject: [PATCH 019/101] Fix doc link to RAI pattern crate documentation (#2612) --- tracing/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 49b3ffc5a5..3fe1c2b50e 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -192,7 +192,7 @@ //! //! You can find more examples showing how to use this crate [here][examples]. //! -//! [RAII]: https://github.com/rust-unofficial/patterns/blob/master/patterns/behavioural/RAII.md +//! [RAII]: https://github.com/rust-unofficial/patterns/blob/main/src/patterns/behavioural/RAII.md //! [examples]: https://github.com/tokio-rs/tracing/tree/master/examples //! //! ### Events From e6f4ff15b812e8c276ee5b3332703e4037fd0a8a Mon Sep 17 00:00:00 2001 From: Cijo Thomas Date: Thu, 15 Jun 2023 14:19:58 -0700 Subject: [PATCH 020/101] docs: fix deadlink to tracing-etw repo (#2602) --- tracing/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 3fe1c2b50e..916cf03cdf 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -837,7 +837,7 @@ //! [Tracy]: https://github.com/wolfpld/tracy //! [`tracing-elastic-apm`]: https://crates.io/crates/tracing-elastic-apm //! [Elastic APM]: https://www.elastic.co/apm -//! [`tracing-etw`]: https://github.com/microsoft/tracing-etw +//! [`tracing-etw`]: https://github.com/microsoft/rust_win_etw/tree/main/win_etw_tracing //! [ETW]: https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing //! [`tracing-fluent-assertions`]: https://crates.io/crates/tracing-fluent-assertions //! [`sentry-tracing`]: https://crates.io/crates/sentry-tracing From 3bde225fda31dcba2c3b52a539a432cb0932b710 Mon Sep 17 00:00:00 2001 From: Noa Date: Thu, 15 Jun 2023 17:34:23 -0500 Subject: [PATCH 021/101] attributes: fix `clippy::let_with_type_underscore` in generated code (#2609) Co-authored-by: Hayden Stainsby Co-authored-by: Eliza Weisman --- tracing-attributes/src/expand.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index d71366c663..50d6648d6b 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -64,7 +64,10 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( // unreachable, but does affect inference, so it needs to be written // exactly that way for it to do its magic. let fake_return_edge = quote_spanned! {return_span=> - #[allow(unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value, clippy::unreachable)] + #[allow( + unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value, + clippy::unreachable, clippy::let_with_type_underscore + )] if false { let __tracing_attr_fake_return: #return_type = unreachable!("this is just for type inference, and is unreachable code"); From 2fc09f47caddd880b5935a68ce3d40cdb545d1c5 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Fri, 16 Jun 2023 08:24:06 -0700 Subject: [PATCH 022/101] chore: silence `clippy::redundant_clone` warning (#2619) The purpose of this test is to assert two clones of the same span are equal to each other, so the clone is kind of the whole point of the test. This commit adds an allow attribute to make clippy shut up about it. --- tracing/tests/span.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index ad71b17a8c..14c4417cb6 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -21,7 +21,12 @@ fn handles_to_the_same_span_are_equal() { // expect to see any spans. with_default(collector::mock().run(), || { let foo1 = tracing::span!(Level::TRACE, "foo"); + + // The purpose of this test is to assert that two clones of the same + // span are equal, so the clone here is kind of the whole point :) + #[allow(clippy::redundant_clone)] let foo2 = foo1.clone(); + // Two handles that point to the same span are equal. assert_eq!(foo1, foo2); }); From c14525e1610db88986f849d46bd3e9795878b012 Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Fri, 16 Jun 2023 17:43:59 +0200 Subject: [PATCH 023/101] core: fix typo in `field` docs (#2611) ## Motivation Module `tracing::field` documentation is missing a word. ## Solution Fixing the typo by adding `event`. --- tracing-core/src/field.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 768107b042..21ac163eee 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -7,7 +7,7 @@ //! # `Value`s and `Collect`s //! //! Collectors consume `Value`s as fields attached to [span]s or [`Event`]s. -//! The set of field keys on a given span or is defined on its [`Metadata`]. +//! The set of field keys on a given span or event is defined on its [`Metadata`]. //! When a span is created, it provides [`Attributes`] to the collector's //! [`new_span`] method, containing any fields whose values were provided when //! the span was created; and may call the collector's [`record`] method From ebd437cf1a1f4bb3f916cde7d798776b89e319cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Tue, 20 Jun 2023 16:36:12 +0200 Subject: [PATCH 024/101] attributes: allow `unknown_lints` in generated code (#2626) ## Motivation #2609 added an allow to generated code to allow a lint that was added in Clippy 1.70.0. This was released with a patch bump so anyone who uses an older version and latest tracing gets a compilation warning about an unkonwn lint. ## Solution Allowing unkonwn lints should fix this now and prevent similar issues in the future. If the lints are unknown it will most likely be because the lints are introduced only in newer compiler. There is just a higher risk that a future contributor tries to add another allow and if they make a typo, the issue will not be caught. --- tracing-attributes/src/expand.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index 50d6648d6b..bedff8e833 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -65,8 +65,8 @@ 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( - unreachable_code, clippy::diverging_sub_expression, clippy::let_unit_value, - clippy::unreachable, clippy::let_with_type_underscore + unknown_lints, unreachable_code, clippy::diverging_sub_expression, + clippy::let_unit_value, clippy::unreachable, clippy::let_with_type_underscore )] if false { let __tracing_attr_fake_return: #return_type = From 2906278ba7afde87067a5af7c4eab51ebc410619 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 7 Jun 2022 22:12:06 +0200 Subject: [PATCH 025/101] core: add `ValueSet::len` and `Record::len` (#2152) ## Motivation This PR adds two new accessor functions that are useful for creating a structured serde implementation for tracing. This is a sort of "distilled" version of https://github.com/tokio-rs/tracing/pull/2113, based on the `v0.1.x` branch. As it is unlikely that "structured serde" will be 1:1 compatible with the existing JSON-based `tracing-serde` (or at least - I'm not sure how to do it in a reasonable amount of effort), these functions will allow me to make a separate crate to hold me over until breaking formatting changes are possible in `tracing-serde`. CC @hawkw, as we've discussed this pretty extensively --- tracing-core/src/field.rs | 15 ++++++++++++++- tracing-core/src/span.rs | 8 ++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 21ac163eee..8bfcab6f20 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -880,6 +880,19 @@ impl<'a> ValueSet<'a> { } } + /// Returns the number of fields in this `ValueSet` that would be visited + /// by a given [visitor] to the [`ValueSet::record()`] method. + /// + /// [visitor]: Visit + /// [`ValueSet::record()`]: ValueSet::record() + pub fn len(&self) -> usize { + let my_callsite = self.callsite(); + self.values + .iter() + .filter(|(field, _)| field.callsite() == my_callsite) + .count() + } + /// Returns `true` if this `ValueSet` contains a value for the given `Field`. pub(crate) fn contains(&self, field: &Field) -> bool { field.callsite() == self.callsite() @@ -890,7 +903,7 @@ impl<'a> ValueSet<'a> { } /// Returns true if this `ValueSet` contains _no_ values. - pub(crate) fn is_empty(&self) -> bool { + pub fn is_empty(&self) -> bool { let my_callsite = self.callsite(); self.values .iter() diff --git a/tracing-core/src/span.rs b/tracing-core/src/span.rs index 1113eabe39..49c1cb95d5 100644 --- a/tracing-core/src/span.rs +++ b/tracing-core/src/span.rs @@ -226,6 +226,14 @@ impl<'a> Record<'a> { self.values.record(visitor) } + /// Returns the number of fields that would be visited from this `Record` + /// when [`Record::record()`] is called + /// + /// [`Record::record()`]: Record::record() + pub fn len(&self) -> usize { + self.values.len() + } + /// Returns `true` if this `Record` contains a value for the given `Field`. pub fn contains(&self, field: &field::Field) -> bool { self.values.contains(field) From 9ad27405f82ca7414a9b742493c00e6782e548df Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Sat, 24 Jun 2023 04:09:29 +0200 Subject: [PATCH 026/101] mock: document public API in collector module (#2389) There has been interest around publishing `tracing-mock` to crates.io for some time. In order to make this possible, documentation and some code clean up is needed. This change adds documentation to the collector module itself and to all the public APIs in the module. This includes doctests on all the methods that serve as examples. Additionally the implementation for the `Expect` struct has been moved into the module with the definition, this was missed in #2369. Refs: #539 --- tracing-mock/src/collector.rs | 915 +++++++++++++++++++++++++++++++--- tracing-mock/src/expect.rs | 50 ++ 2 files changed, 893 insertions(+), 72 deletions(-) diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs index 530766fd6c..3298bfb22f 100644 --- a/tracing-mock/src/collector.rs +++ b/tracing-mock/src/collector.rs @@ -1,4 +1,142 @@ -#![allow(missing_docs)] +//! An implementation of the [`Collect`] trait to receive and validate +//! `tracing` data. +//! +//! The [`MockCollector`] is the central component of this crate. The +//! `MockCollector` has expectations set on it which are later +//! validated as the code under test is run. +//! +//! # Examples +//! +//! ``` +//! use tracing_mock::{collector, expect, field}; +//! +//! let (collector, handle) = collector::mock() +//! // Expect a single event with a specified message +//! .event(expect::event().with_fields(field::msg("droids"))) +//! .only() +//! .run_with_handle(); +//! +//! // Use `with_default` to apply the `MockCollector` for the duration +//! // of the closure - this is what we are testing. +//! tracing::collect::with_default(collector, || { +//! // These *are* the droids we are looking for +//! tracing::info!("droids"); +//! }); +//! +//! // Use the handle to check the assertions. This line will panic if an +//! // assertion is not met. +//! handle.assert_finished(); +//! ``` +//! +//! A more complex example may consider multiple spans and events with +//! their respective fields: +//! +//! ``` +//! use tracing_mock::{collector, expect, field}; +//! +//! let span = expect::span() +//! .named("my_span"); +//! let (collector, handle) = collector::mock() +//! // Enter a matching span +//! .enter(span.clone()) +//! // Record an event with message "collect parting message" +//! .event(expect::event().with_fields(field::msg("collect parting message"))) +//! // Record a value for the field `parting` on a matching span +//! .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +//! // Exit a matching span +//! .exit(span) +//! // Expect no further messages to be recorded +//! .only() +//! // Return the collector and handle +//! .run_with_handle(); +//! +//! // Use `with_default` to apply the `MockCollector` for the duration +//! // of the closure - this is what we are testing. +//! tracing::collect::with_default(collector, || { +//! let span = tracing::trace_span!( +//! "my_span", +//! greeting = "hello world", +//! parting = tracing::field::Empty +//! ); +//! +//! let _guard = span.enter(); +//! tracing::info!("collect parting message"); +//! let parting = "goodbye world!"; +//! +//! span.record("parting", &parting); +//! }); +//! +//! // Use the handle to check the assertions. This line will panic if an +//! // assertion is not met. +//! handle.assert_finished(); +//! ``` +//! +//! If we modify the previous example so that we **don't** enter the +//! span before recording an event, the test will fail: +//! +//! ```should_panic +//! use tracing_mock::{collector, expect, field}; +//! +//! let span = expect::span() +//! .named("my_span"); +//! let (collector, handle) = collector::mock() +//! .enter(span.clone()) +//! .event(expect::event().with_fields(field::msg("collect parting message"))) +//! .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +//! .exit(span) +//! .only() +//! .run_with_handle(); +//! +//! // Use `with_default` to apply the `MockCollector` for the duration +//! // of the closure - this is what we are testing. +//! tracing::collect::with_default(collector, || { +//! let span = tracing::trace_span!( +//! "my_span", +//! greeting = "hello world", +//! parting = tracing::field::Empty +//! ); +//! +//! // Don't enter the span. +//! // let _guard = span.enter(); +//! tracing::info!("collect parting message"); +//! let parting = "goodbye world!"; +//! +//! span.record("parting", &parting); +//! }); +//! +//! // Use the handle to check the assertions. This line will panic if an +//! // assertion is not met. +//! handle.assert_finished(); +//! ``` +//! +//! This will result in an error message such as the following: +//! +//! ```text +//! thread 'main' panicked at ' +//! [main] expected to enter a span named `my_span` +//! [main] but instead observed event Event { +//! fields: ValueSet { +//! message: collect parting message, +//! callsite: Identifier(0x10eda3278), +//! }, +//! metadata: Metadata { +//! name: "event src/collector.rs:27", +//! target: "rust_out", +//! level: Level( +//! Info, +//! ), +//! module_path: "rust_out", +//! location: src/collector.rs:27, +//! fields: {message}, +//! callsite: Identifier(0x10eda3278), +//! kind: Kind(EVENT), +//! }, +//! parent: Current, +//! }', tracing/tracing-mock/src/expect.rs:59:33 +//! ``` +//! +//! [`Collect`]: trait@tracing::Collect +//! [`MockCollector`]: struct@crate::collector::MockCollector use crate::{ event::ExpectedEvent, expect::Expect, @@ -7,7 +145,6 @@ use crate::{ }; use std::{ collections::{HashMap, VecDeque}, - fmt, sync::{ atomic::{AtomicUsize, Ordering}, Arc, Mutex, @@ -37,6 +174,12 @@ struct Running) -> bool> { name: String, } +/// A collector which can validate received traces. +/// +/// For a detailed description and examples see the documentation +/// for the methods and the [`collector`] module. +/// +/// [`collector`]: mod@crate::collector pub struct MockCollector) -> bool> { expected: VecDeque, max_level: Option, @@ -44,8 +187,67 @@ pub struct MockCollector) -> bool> { name: String, } +/// A handle which is used to invoke validation of expectations. +/// +/// The handle is currently only used to assert that all the expected +/// events and spans were seen. +/// +/// For additional information and examples, see the [`collector`] +/// module documentation. +/// +/// [`collector`]: mod@crate::collector pub struct MockHandle(Arc>>, String); +/// Create a new [`MockCollector`]. +/// +/// For additional information and examples, see the [`collector`] +/// module and [`MockCollector`] documentation. +/// +/// # Examples +/// +/// +/// ``` +/// use tracing_mock::{collector, expect, field}; +/// +/// let span = expect::span() +/// .named("my_span"); +/// let (collector, handle) = collector::mock() +/// // Enter a matching span +/// .enter(span.clone()) +/// // Record an event with message "collect parting message" +/// .event(expect::event().with_fields(field::msg("collect parting message"))) +/// // Record a value for the field `parting` on a matching span +/// .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +/// // Exit a matching span +/// .exit(span) +/// // Expect no further messages to be recorded +/// .only() +/// // Return the collector and handle +/// .run_with_handle(); +/// +/// // Use `with_default` to apply the `MockCollector` for the duration +/// // of the closure - this is what we are testing. +/// tracing::collect::with_default(collector, || { +/// let span = tracing::trace_span!( +/// "my_span", +/// greeting = "hello world", +/// parting = tracing::field::Empty +/// ); +/// +/// let _guard = span.enter(); +/// tracing::info!("collect parting message"); +/// let parting = "goodbye world!"; +/// +/// span.record("parting", &parting); +/// }); +/// +/// // Use the handle to check the assertions. This line will panic if an +/// // assertion is not met. +/// handle.assert_finished(); +/// ``` +/// +/// [`collector`]: mod@crate::collector +#[must_use] pub fn mock() -> MockCollector) -> bool> { MockCollector { expected: VecDeque::new(), @@ -67,14 +269,56 @@ where /// The debugging output is displayed if the test panics, or if the test is /// run with `--nocapture`. /// - /// By default, the mock subscriber's name is the name of the test + /// By default, the mock collector's name is the name of the test /// (*technically*, the name of the thread where it was created, which is /// the name of the test unless tests are run with `--test-threads=1`). - /// When a test has only one mock subscriber, this is sufficient. However, - /// some tests may include multiple subscribers, in order to test - /// interactions between multiple subscribers. In that case, it can be - /// helpful to give each subscriber a separate name to distinguish where the + /// When a test has only one mock collector, this is sufficient. However, + /// some tests may include multiple collectors, in order to test + /// interactions between multiple collectors. In that case, it can be + /// helpful to give each collector a separate name to distinguish where the /// debugging output comes from. + /// + /// # Examples + /// + /// In the following example, we create 2 collectors, both + /// expecting to receive an event. As we only record a single + /// event, the test will fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let (collector_1, handle_1) = collector::mock() + /// .named("collector-1") + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// let (collector_2, handle_2) = collector::mock() + /// .named("collector-2") + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// let _guard = tracing::collect::set_default(collector_2); + /// + /// tracing::collect::with_default(collector_1, || { + /// tracing::info!("a"); + /// }); + /// + /// handle_1.assert_finished(); + /// handle_2.assert_finished(); + /// ``` + /// + /// In the test output, we see that the collector which didn't + /// received the event was the one named `collector-2`, which is + /// correct as the collector named `collector-1` was the default + /// when the event was recorded: + /// + /// ```text + /// [collector-2] more notifications expected: [ + /// Event( + /// MockEvent, + /// ), + /// ]', tracing-mock/src/collector.rs:1276:13 + /// ``` pub fn named(self, name: impl ToString) -> Self { Self { name: name.to_string(), @@ -82,43 +326,443 @@ where } } - pub fn enter(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::Enter(span)); + /// Adds an expectation that an event matching the [`ExpectedEvent`] + /// will be recorded next. + /// + /// The `event` can be a default mock which will match any event + /// (`expect::event()`) or can include additional expectations. + /// See the [`ExpectedEvent`] documentation for more details. + /// + /// If an event is recorded that doesn't match the `ExpectedEvent`, + /// or if something else (such as entering a span) is recorded + /// first, then the expectation will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// A span is entered before the event, causing the test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("span"); + /// let _guard = span.enter(); + /// tracing::info!("a"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + pub fn event(mut self, event: ExpectedEvent) -> Self { + self.expected.push_back(Expect::Event(event)); self } - pub fn follows_from(mut self, consequence: ExpectedSpan, cause: ExpectedSpan) -> Self { - self.expected - .push_back(Expect::FollowsFrom { consequence, cause }); + /// Adds an expectation that the creation of a span will be + /// recorded next. + /// + /// This function accepts `Into` instead of + /// [`ExpectedSpan`] directly, so it can be used to test + /// span fields and the span parent. This is because a + /// collector only receives the span fields and parent when + /// a span is created, not when it is entered. + /// + /// The new span doesn't need to be entered for this expectation + /// to succeed. + /// + /// If a span is recorded that doesn't match the `ExpectedSpan`, + /// or if something else (such as an event) is recorded first, + /// then the expectation will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing") + /// .with_field(expect::field("testing").with_value(&"yes")); + /// let (collector, handle) = collector::mock() + /// .new_span(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// _ = tracing::info_span!("the span we're testing", testing = "yes"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// An event is recorded before the span is created, causing the + /// test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing") + /// .with_field(expect::field("testing").with_value(&"yes")); + /// let (collector, handle) = collector::mock() + /// .new_span(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("an event"); + /// _ = tracing::info_span!("the span we're testing", testing = "yes"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + pub fn new_span(mut self, new_span: I) -> Self + where + I: Into, + { + self.expected.push_back(Expect::NewSpan(new_span.into())); self } - pub fn event(mut self, event: ExpectedEvent) -> Self { - self.expected.push_back(Expect::Event(event)); + /// Adds an expectation that entering a span matching the + /// [`ExpectedSpan`] will be recorded next. + /// + /// This expectation is generally accompanied by a call to + /// [`exit`] as well. If used together with [`only`], this + /// is necessary. + /// + /// If the span that is entered doesn't match the [`ExpectedSpan`], + /// or if something else (such as an event) is recorded first, + /// then the expectation will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .enter(span.clone()) + /// .exit(span) + /// .only() + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("the span we're testing"); + /// let _entered = span.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// An event is recorded before the span is entered, causing the + /// test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .enter(span.clone()) + /// .exit(span) + /// .only() + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("an event"); + /// let span = tracing::info_span!("the span we're testing"); + /// let _entered = span.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`exit`]: fn@Self::exit + /// [`only`]: fn@Self::only + pub fn enter(mut self, span: ExpectedSpan) -> Self { + self.expected.push_back(Expect::Enter(span)); self } + /// Adds ab expectation that exiting a span matching the + /// [`ExpectedSpan`] will be recorded next. + /// + /// As a span may be entered and exited multiple times, + /// this is different from the span being closed. In + /// general [`enter`] and `exit` should be paired. + /// + /// If the span that is exited doesn't match the [`ExpectedSpan`], + /// or if something else (such as an event) is recorded first, + /// then the expectation will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .enter(span.clone()) + /// .exit(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("the span we're testing"); + /// let _entered = span.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// An event is recorded before the span is exited, causing the + /// test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .enter(span.clone()) + /// .exit(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("the span we're testing"); + /// let _entered = span.enter(); + /// tracing::info!("an event"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`enter`]: fn@Self::enter pub fn exit(mut self, span: ExpectedSpan) -> Self { self.expected.push_back(Expect::Exit(span)); self } + /// Adds an expectation that cloning a span matching the + /// [`ExpectedSpan`] will be recorded next. + /// + /// The cloned span does need to be entered. + /// + /// If the span that is cloned doesn't match the [`ExpectedSpan`], + /// or if something else (such as an event) is recorded first, + /// then the expectation will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .clone_span(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("the span we're testing"); + /// _ = span.clone(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// An event is recorded before the span is cloned, causing the + /// test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .at_level(tracing::Level::INFO) + /// .named("the span we're testing"); + /// let (collector, handle) = collector::mock() + /// .clone_span(span) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("the span we're testing"); + /// tracing::info!("an event"); + /// _ = span.clone(); + /// }); + /// + /// handle.assert_finished(); + /// ``` pub fn clone_span(mut self, span: ExpectedSpan) -> Self { self.expected.push_back(Expect::CloneSpan(span)); self } + /// **This method is deprecated.** + /// + /// Adds an expectation that a span matching the [`ExpectedSpan`] + /// getting dropped via the deprecated function + /// [`Collect::drop_span`] will be recorded next. + /// + /// Instead [`Collect::try_close`] should be used on the collector + /// and should be asserted with `close_span` (which hasn't been + /// implemented yet, but will be done as part of #539). + /// + /// [`Collect::drop_span`]: fn@tracing::Collect::drop_span #[allow(deprecated)] pub fn drop_span(mut self, span: ExpectedSpan) -> Self { self.expected.push_back(Expect::DropSpan(span)); self } - pub fn only(mut self) -> Self { - self.expected.push_back(Expect::Nothing); + /// Adds an expectation that a `follows_from` relationship will be + /// recorded next. Specifically that a span matching `consequence` + /// follows from a span matching `cause`. + /// + /// For further details on what this causal relationship means, see + /// [`Span::follows_from`]. + /// + /// If either of the 2 spans don't match their respective + /// [`ExpectedSpan`] or if something else (such as an event) is + /// recorded first, then the expectation will fail. + /// + /// **Note**: The 2 spans, `consequence` and `cause` are matched + /// by `name` only. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let cause = expect::span().named("cause"); + /// let consequence = expect::span().named("consequence"); + /// + /// let (collector, handle) = collector::mock() + /// .follows_from(consequence, cause) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let cause = tracing::info_span!("cause"); + /// let consequence = tracing::info_span!("consequence"); + /// + /// consequence.follows_from(&cause); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// The `cause` span doesn't match, it is actually recorded at + /// `Level::WARN` instead of the expected `Level::INFO`, causing + /// this test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let cause = expect::span().named("cause"); + /// let consequence = expect::span().named("consequence"); + /// + /// let (collector, handle) = collector::mock() + /// .follows_from(consequence, cause) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let cause = tracing::info_span!("another cause"); + /// let consequence = tracing::info_span!("consequence"); + /// + /// consequence.follows_from(&cause); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`Span::follows_from`]: fn@tracing::Span::follows_from + pub fn follows_from(mut self, consequence: ExpectedSpan, cause: ExpectedSpan) -> Self { + self.expected + .push_back(Expect::FollowsFrom { consequence, cause }); self } + /// Adds an expectation that `fields` are recorded on a span + /// matching the [`ExpectedSpan`] will be recorded next. + /// + /// For further information on how to specify the expected + /// fields, see the documentation on the [`field`] module. + /// + /// If either the span doesn't match the [`ExpectedSpan`], the + /// fields don't match the expected fields, or if something else + /// (such as an event) is recorded first, then the expectation + /// will fail. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .named("my_span"); + /// let (collector, handle) = collector::mock() + /// .record(span, expect::field("parting").with_value(&"goodbye world!")) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::trace_span!( + /// "my_span", + /// greeting = "hello world", + /// parting = tracing::field::Empty + /// ); + /// span.record("parting", "goodbye world!"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// The value of the recorded field doesn't match the expectation, + /// causing the test to fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let span = expect::span() + /// .named("my_span"); + /// let (collector, handle) = collector::mock() + /// .record(span, expect::field("parting").with_value(&"goodbye world!")) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::trace_span!( + /// "my_span", + /// greeting = "hello world", + /// parting = tracing::field::Empty + /// ); + /// span.record("parting", "goodbye universe!"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`field`]: mod@crate::field pub fn record(mut self, span: ExpectedSpan, fields: I) -> Self where I: Into, @@ -127,14 +771,31 @@ where self } - pub fn new_span(mut self, new_span: I) -> Self - where - I: Into, - { - self.expected.push_back(Expect::NewSpan(new_span.into())); - self - } - + /// Filter the traces evaluated by the `MockCollector`. + /// + /// The filter will be applied to all traces received before + /// any validation occurs - so its position in the call chain + /// is not important. The filter does not perform any validation + /// itself. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .with_filter(|meta| meta.level() <= &tracing::Level::WARN) + /// .event(expect::event()) + /// .only() + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// tracing::warn!("b"); + /// }); + /// + /// handle.assert_finished(); + /// ``` pub fn with_filter(self, filter: G) -> MockCollector where G: Fn(&Metadata<'_>) -> bool + 'static, @@ -147,6 +808,39 @@ where } } + /// Sets the max level that will be provided to the `tracing` + /// system. + /// + /// This method can be used to test the internals of `tracing`, + /// but it is also useful to filter out traces on more verbose + /// levels if you only want to verify above a certain level. + /// + /// **Note**: this value determines a global filter, if + /// `with_max_level_hint` is called on multiple collectors, the + /// global filter will be the least restrictive of all collectors. + /// To filter the events evaluated by a specific `MockCollector`, + /// use [`with_filter`] instead. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .with_max_level_hint(tracing::Level::INFO) + /// .event(expect::event().at_level(tracing::Level::INFO)) + /// .only() + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::debug!("a message we don't care about"); + /// tracing::info!("a message we want to validate"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`with_filter`]: fn@Self::with_filter pub fn with_max_level_hint(self, hint: impl Into) -> Self { Self { max_level: Some(hint.into()), @@ -154,11 +848,108 @@ where } } + /// Expects that no further traces are received. + /// + /// The call to `only` should appear immediately before the final + /// call to `run` or `run_with_handle`, as any expectations which + /// are added after `only` will not be considered. + /// + /// # Examples + /// + /// Consider this simple test. It passes even though we only + /// expect a single event, but receive three: + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// tracing::info!("b"); + /// tracing::info!("c"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// After including `only`, the test will fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .only() + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// tracing::info!("b"); + /// tracing::info!("c"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + pub fn only(mut self) -> Self { + self.expected.push_back(Expect::Nothing); + self + } + + /// Consume the receiver and return an `impl` [`Collect`] which can + /// be set as the default collector. + /// + /// This function is similar to [`run_with_handle`], but it doesn't + /// return a [`MockHandle`]. This is useful if the desired + /// assertions can be checked externally to the collector. + /// + /// # Examples + /// + /// The following test is used within the `tracing` + /// codebase: + /// + /// ``` + /// use tracing_mock::collector; + /// + /// tracing::collect::with_default(collector::mock().run(), || { + /// let foo1 = tracing::span!(tracing::Level::TRACE, "foo"); + /// let foo2 = foo1.clone(); + /// // Two handles that point to the same span are equal. + /// assert_eq!(foo1, foo2); + /// }); + /// ``` + /// + /// [`Collect`]: tracing::Collect + /// [`run_with_handle`]: fn@Self::run_with_handle pub fn run(self) -> impl Collect { let (collector, _) = self.run_with_handle(); collector } + /// Consume the receiver and return an `impl` [`Collect`] which can + /// be set as the default collector and a [`MockHandle`] which can + /// be used to validate the provided expectations. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// // collector and handle are returned from `run_with_handle()` + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`Collect`]: tracing::Collect pub fn run_with_handle(self) -> (impl Collect, MockHandle) { let expected = Arc::new(Mutex::new(self.expected)); let handle = MockHandle(expected.clone(), self.name.clone()); @@ -267,9 +1058,11 @@ where cause: ref expected_cause, }) => { if let Some(name) = expected_consequence.name() { + // TODO(hds): Write proper assertion text. assert_eq!(name, consequence_span.name); } if let Some(name) = expected_cause.name() { + // TODO(hds): Write proper assertion text. assert_eq!(name, cause_span.name); } } @@ -467,6 +1260,32 @@ impl MockHandle { Self(expected, name) } + /// Checks the expectations which were set on the + /// [`MockCollector`]. + /// + /// Calling `assert_finished` is usually the final part of a test. + /// + /// # Panics + /// + /// This method will panic if any of the provided expectations are + /// not met. + /// + /// # Examples + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// + /// let (collector, handle) = collector::mock() + /// .event(expect::event()) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// tracing::info!("a"); + /// }); + /// + /// // Check assertions set on the mock collector + /// handle.assert_finished(); + /// ``` pub fn assert_finished(&self) { if let Ok(ref expected) = self.0.lock() { assert!( @@ -478,51 +1297,3 @@ impl MockHandle { } } } - -impl Expect { - pub fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { - let name = name.as_ref(); - match self { - Expect::Event(e) => panic!( - "\n[{}] expected event {}\n[{}] but instead {}", - name, e, name, what, - ), - Expect::FollowsFrom { consequence, cause } => panic!( - "\n[{}] expected consequence {} to follow cause {} but instead {}", - name, consequence, cause, what, - ), - Expect::Enter(e) => panic!( - "\n[{}] expected to enter {}\n[{}] but instead {}", - name, e, name, what, - ), - Expect::Exit(e) => panic!( - "\n[{}] expected to exit {}\n[{}] but instead {}", - name, e, name, what, - ), - Expect::CloneSpan(e) => { - panic!( - "\n[{}] expected to clone {}\n[{}] but instead {}", - name, e, name, what, - ) - } - Expect::DropSpan(e) => { - panic!( - "\n[{}] expected to drop {}\n[{}] but instead {}", - name, e, name, what, - ) - } - Expect::Visit(e, fields) => panic!( - "\n[{}] expected {} to record {}\n[{}] but instead {}", - name, e, fields, name, what, - ), - Expect::NewSpan(e) => panic!( - "\n[{}] expected {}\n[{}] but instead {}", - name, e, name, what - ), - Expect::Nothing => panic!( - "\n[{}] expected nothing else to happen\n[{}] but {} instead", - name, name, what, - ), - } - } -} diff --git a/tracing-mock/src/expect.rs b/tracing-mock/src/expect.rs index a21f260e8d..044f134580 100644 --- a/tracing-mock/src/expect.rs +++ b/tracing-mock/src/expect.rs @@ -1,3 +1,5 @@ +use std::fmt; + use crate::{ event::ExpectedEvent, field::{ExpectedField, ExpectedFields, ExpectedValue}, @@ -41,3 +43,51 @@ pub fn span() -> ExpectedSpan { ..Default::default() } } + +impl Expect { + pub(crate) fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { + let name = name.as_ref(); + match self { + Expect::Event(e) => panic!( + "\n[{}] expected event {}\n[{}] but instead {}", + name, e, name, what, + ), + Expect::FollowsFrom { consequence, cause } => panic!( + "\n[{}] expected consequence {} to follow cause {} but instead {}", + name, consequence, cause, what, + ), + Expect::Enter(e) => panic!( + "\n[{}] expected to enter {}\n[{}] but instead {}", + name, e, name, what, + ), + Expect::Exit(e) => panic!( + "\n[{}] expected to exit {}\n[{}] but instead {}", + name, e, name, what, + ), + Expect::CloneSpan(e) => { + panic!( + "\n[{}] expected to clone {}\n[{}] but instead {}", + name, e, name, what, + ) + } + Expect::DropSpan(e) => { + panic!( + "\n[{}] expected to drop {}\n[{}] but instead {}", + name, e, name, what, + ) + } + Expect::Visit(e, fields) => panic!( + "\n[{}] expected {} to record {}\n[{}] but instead {}", + name, e, fields, name, what, + ), + Expect::NewSpan(e) => panic!( + "\n[{}] expected {}\n[{}] but instead {}", + name, e, name, what + ), + Expect::Nothing => panic!( + "\n[{}] expected nothing else to happen\n[{}] but {} instead", + name, name, what, + ), + } + } +} From c6e8a8f679f40d2528008918e2d636e0b48b8107 Mon Sep 17 00:00:00 2001 From: Eliza Weisman Date: Sat, 24 Jun 2023 10:13:17 -0700 Subject: [PATCH 027/101] tracing: fix recursive `register_callsite` deadlock (#2634) ## Motivation A deadlock exists when a collector's `register_callsite` method calls into code that also contains tracing instrumentation and triggers a second `register_callsite` call for the same callsite. This is because the current implementation of the `MacroCallsite` type holds a `core::sync::Once` which it uses to ensure that it is only added to the callsite registry a single time. This deadlock was fixed in v0.1.x in PR #2083, but the issue still exists on v0.2.x. ## Solution This branch forward-ports the solution from #2083. Rather than using a `core::sync::Once`, we now track the callsite's registration state directly in `MacroCallsite`. If a callsite has started registering, but has not yet completed, subsequent `register` calls will just immediately receive an `Interest::sometimes` until the registration has completed, rather than waiting to attempt their own registration. I've also forward-ported the tests for this that were added in #2083. --- tracing/src/lib.rs | 59 +++++++++++++++---- .../tests/rebuild_interest_doesnt_deadlock.rs | 51 ++++++++++++++++ .../register_callsite_doesnt_deadlock.rs | 49 +++++++++++++++ 3 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 tracing/tests/rebuild_interest_doesnt_deadlock.rs create mode 100644 tracing/tests/register_callsite_doesnt_deadlock.rs diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 916cf03cdf..1494048523 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -988,8 +988,7 @@ pub mod __macro_support { pub use crate::callsite::{Callsite, Registration}; use crate::{collect::Interest, Metadata}; use core::fmt; - use core::sync::atomic::{AtomicUsize, Ordering}; - use tracing_core::Once; + use core::sync::atomic::{AtomicU8, Ordering}; /// Callsite implementation used by macro-generated code. /// @@ -1003,9 +1002,9 @@ pub mod __macro_support { where T: 'static, { - interest: AtomicUsize, + interest: AtomicU8, + register: AtomicU8, meta: &'static Metadata<'static>, - register: Once, registration: &'static Registration, } @@ -1023,12 +1022,21 @@ pub mod __macro_support { registration: &'static Registration, ) -> Self { Self { - interest: AtomicUsize::new(0xDEADFACED), + interest: AtomicU8::new(Self::INTEREST_EMPTY), + register: AtomicU8::new(Self::UNREGISTERED), meta, - register: Once::new(), registration, } } + + const UNREGISTERED: u8 = 0; + const REGISTERING: u8 = 1; + const REGISTERED: u8 = 2; + + const INTEREST_NEVER: u8 = 0; + const INTEREST_SOMETIMES: u8 = 1; + const INTEREST_ALWAYS: u8 = 2; + const INTEREST_EMPTY: u8 = 0xFF; } impl MacroCallsite<&'static dyn Callsite> { @@ -1046,11 +1054,36 @@ pub mod __macro_support { // This only happens once (or if the cached interest value was corrupted). #[cold] pub fn register(&'static self) -> Interest { - self.register - .call_once(|| crate::callsite::register(self.registration)); + // Attempt to advance the registration state to `REGISTERING`... + match self.register.compare_exchange( + Self::UNREGISTERED, + Self::REGISTERING, + Ordering::AcqRel, + Ordering::Acquire, + ) { + Ok(_) => { + // Okay, we advanced the state, try to register the callsite. + crate::callsite::register(self.registration); + self.register.store(Self::REGISTERED, Ordering::Release); + } + // Great, the callsite is already registered! Just load its + // previous cached interest. + Err(Self::REGISTERED) => {} + // Someone else is registering... + Err(_state) => { + debug_assert_eq!( + _state, + Self::REGISTERING, + "weird callsite registration state" + ); + // Just hit `enabled` this time. + return Interest::sometimes(); + } + } + match self.interest.load(Ordering::Relaxed) { - 0 => Interest::never(), - 2 => Interest::always(), + Self::INTEREST_NEVER => Interest::never(), + Self::INTEREST_ALWAYS => Interest::always(), _ => Interest::sometimes(), } } @@ -1067,9 +1100,9 @@ pub mod __macro_support { #[inline] pub fn interest(&'static self) -> Interest { match self.interest.load(Ordering::Relaxed) { - 0 => Interest::never(), - 1 => Interest::sometimes(), - 2 => Interest::always(), + Self::INTEREST_NEVER => Interest::never(), + Self::INTEREST_SOMETIMES => Interest::sometimes(), + Self::INTEREST_ALWAYS => Interest::always(), _ => self.register(), } } diff --git a/tracing/tests/rebuild_interest_doesnt_deadlock.rs b/tracing/tests/rebuild_interest_doesnt_deadlock.rs new file mode 100644 index 0000000000..cd877cbafd --- /dev/null +++ b/tracing/tests/rebuild_interest_doesnt_deadlock.rs @@ -0,0 +1,51 @@ +use std::{sync::mpsc, thread, time::Duration}; +use tracing::{ + callsite, + collect::{self, Collect, Interest}, + metadata::Metadata, + span, Event, +}; + +#[test] +fn rebuild_callsites_doesnt_deadlock() { + pub struct EvilCollector; + + impl Collect for EvilCollector { + fn register_callsite(&self, meta: &'static Metadata<'static>) -> Interest { + tracing::info!(?meta, "registered a callsite"); + Interest::always() + } + + fn enabled(&self, _: &Metadata<'_>) -> bool { + true + } + fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { + span::Id::from_u64(1) + } + fn record(&self, _: &span::Id, _: &span::Record<'_>) {} + fn record_follows_from(&self, _: &span::Id, _: &span::Id) {} + fn event(&self, _: &Event<'_>) {} + fn enter(&self, _: &span::Id) {} + fn exit(&self, _: &span::Id) {} + fn current_span(&self) -> tracing_core::span::Current { + unimplemented!() + } + } + + collect::set_global_default(EvilCollector).unwrap(); + + // spawn a thread, and assert it doesn't hang... + let (tx, didnt_hang) = mpsc::channel(); + let th = thread::spawn(move || { + tracing::info!("hello world!"); + callsite::rebuild_interest_cache(); + tx.send(()).unwrap(); + }); + + didnt_hang + // Note: 60 seconds is *way* more than enough, but let's be generous in + // case of e.g. slow CI machines. + .recv_timeout(Duration::from_secs(60)) + .expect("the thread must not have hung!"); + th.join().expect("thread should join successfully"); +} diff --git a/tracing/tests/register_callsite_doesnt_deadlock.rs b/tracing/tests/register_callsite_doesnt_deadlock.rs new file mode 100644 index 0000000000..32f3e686bc --- /dev/null +++ b/tracing/tests/register_callsite_doesnt_deadlock.rs @@ -0,0 +1,49 @@ +use std::{sync::mpsc, thread, time::Duration}; +use tracing::{ + collect::{self, Collect, Interest}, + metadata::Metadata, + span, Event, +}; + +#[test] +fn register_callsite_doesnt_deadlock() { + pub struct EvilCollector; + + impl Collect for EvilCollector { + fn register_callsite(&self, meta: &'static Metadata<'static>) -> Interest { + tracing::info!(?meta, "registered a callsite"); + Interest::always() + } + + fn enabled(&self, _: &Metadata<'_>) -> bool { + true + } + fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { + span::Id::from_u64(1) + } + fn record(&self, _: &span::Id, _: &span::Record<'_>) {} + fn record_follows_from(&self, _: &span::Id, _: &span::Id) {} + fn event(&self, _: &Event<'_>) {} + fn enter(&self, _: &span::Id) {} + fn exit(&self, _: &span::Id) {} + fn current_span(&self) -> tracing_core::span::Current { + unimplemented!() + } + } + + collect::set_global_default(EvilCollector).unwrap(); + + // spawn a thread, and assert it doesn't hang... + let (tx, didnt_hang) = mpsc::channel(); + let th = thread::spawn(move || { + tracing::info!("hello world!"); + tx.send(()).unwrap(); + }); + + didnt_hang + // Note: 60 seconds is *way* more than enough, but let's be generous in + // case of e.g. slow CI machines. + .recv_timeout(Duration::from_secs(60)) + .expect("the thread must not have hung!"); + th.join().expect("thread should join successfully"); +} From 27f688efb72316a26f3ec1f952c82626692c08ff Mon Sep 17 00:00:00 2001 From: Harry Barber <106155934+hlbarber@users.noreply.github.com> Date: Tue, 11 Jul 2023 20:39:10 +0100 Subject: [PATCH 028/101] tracing: use fully qualified names in macros for items exported from std prelude (#2621) ## Motivation Currently, in many places, macros do not use fully qualified names for items exported from the prelude. This means that naming collisions (`struct Some`) or the removal of the std library prelude will cause compilation errors. ## Solution - Identify and use fully qualified names in macros were we previously assumed the Rust std prelude. We use `::core` rather than `::std`. - Add [`no_implicit_prelude`](https://doc.rust-lang.org/reference/names/preludes.html#the-no_implicit_prelude-attribute) to `tracing/tests/macros.rs`. I'm unsure if this is giving us good coverage - can we improve on this approach? I'm not confident I've caught everything. --- tracing-core/src/field.rs | 2 +- tracing-core/src/lib.rs | 6 +- tracing-log/src/lib.rs | 6 +- tracing-subscriber/src/fmt/fmt_subscriber.rs | 2 +- tracing-subscriber/src/macros.rs | 2 +- tracing-subscriber/src/subscribe/mod.rs | 2 +- tracing/src/macros.rs | 40 +-- tracing/tests/macros.rs | 320 ++++++++++--------- 8 files changed, 192 insertions(+), 188 deletions(-) diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index 8bfcab6f20..80ca065ba3 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -963,7 +963,7 @@ macro_rules! impl_valid_len { ( $( $len:tt ),+ ) => { $( impl<'a> private::ValidLen<'a> for - [(&'a Field, Option<&'a (dyn Value + 'a)>); $len] {} + [(&'a Field, ::core::option::Option<&'a (dyn Value + 'a)>); $len] {} )+ } } diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index e31978368d..d01b4a0c7a 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -263,9 +263,9 @@ macro_rules! metadata { $name, $target, $level, - Some(file!()), - Some(line!()), - Some(module_path!()), + ::core::option::Option::Some(file!()), + ::core::option::Option::Some(line!()), + ::core::option::Option::Some(module_path!()), $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)), $kind, ) diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs index 916d0b6b7f..a822051baf 100644 --- a/tracing-log/src/lib.rs +++ b/tracing-log/src/lib.rs @@ -280,9 +280,9 @@ macro_rules! log_cs { "log event", "log", $level, - None, - None, - None, + ::core::option::Option::None, + ::core::option::Option::None, + ::core::option::Option::None, field::FieldSet::new(FIELD_NAMES, identify_callsite!(&$cs)), Kind::EVENT, ); diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index 1b89039261..366660fb82 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -778,7 +778,7 @@ macro_rules! with_event_from_span { #[allow(unused)] let mut iter = fs.iter(); let v = [$( - (&iter.next().unwrap(), Some(&$value as &dyn field::Value)), + (&iter.next().unwrap(), ::core::option::Option::Some(&$value as &dyn field::Value)), )*]; let vs = fs.value_set(&v); let $event = Event::new_child_of($id, meta, &vs); diff --git a/tracing-subscriber/src/macros.rs b/tracing-subscriber/src/macros.rs index 81351132f5..5b461d0a0a 100644 --- a/tracing-subscriber/src/macros.rs +++ b/tracing-subscriber/src/macros.rs @@ -4,7 +4,7 @@ macro_rules! try_lock { try_lock!($lock, else return) }; ($lock:expr, else $els:expr) => { - if let Ok(l) = $lock { + if let ::core::result::Result::Ok(l) = $lock { l } else if std::thread::panicking() { $els diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs index a18d95fc5a..8601ca3287 100644 --- a/tracing-subscriber/src/subscribe/mod.rs +++ b/tracing-subscriber/src/subscribe/mod.rs @@ -1729,7 +1729,7 @@ macro_rules! subscriber_impl_body { #[doc(hidden)] #[inline] - unsafe fn downcast_raw(&self, id: TypeId) -> Option> { + unsafe fn downcast_raw(&self, id: TypeId) -> ::core::option::Option> { self.deref().downcast_raw(id) } }; diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 138a80a40d..f47f9bbb2b 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -2196,79 +2196,79 @@ macro_rules! valueset { // }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$($k).+ as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$($k).+) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$($k).+) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) }, $next, ) }; @@ -2276,47 +2276,47 @@ macro_rules! valueset { // Handle literal names (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) }, $next, $($rest)* ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) }, $next, ) }; (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => { $crate::valueset!( - @ { $($out),*, (&$next, Some(&$val as &dyn Value)) }, + @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) }, $next, ) }; // Remainder is unparsable, but exists --- must be format args! (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => { - $crate::valueset!(@ { (&$next, Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) + $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, ) }; // === entry === @@ -2327,7 +2327,7 @@ macro_rules! valueset { let mut iter = $fields.iter(); $fields.value_set($crate::valueset!( @ { }, - iter.next().expect("FieldSet corrupted (this is a bug)"), + ::core::iter::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"), $($kvs)+ )) } diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs index a9679a3e94..423984da27 100644 --- a/tracing/tests/macros.rs +++ b/tracing/tests/macros.rs @@ -1,4 +1,8 @@ #![deny(warnings)] +// We call all macros in this module with `no_implicit_prelude` to ensure they do not depend on the standard prelude. +#![no_implicit_prelude] +extern crate tracing; + use tracing::{ callsite, debug, debug_span, enabled, error, error_span, event, event_enabled, info, info_span, span, span_enabled, trace, trace_span, warn, warn_span, Level, @@ -111,80 +115,80 @@ fn error_span() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn span_root() { - span!(target: "foo_events", parent: None, Level::TRACE, "foo", bar.baz = 2, quux = 3); - span!(target: "foo_events", parent: None, Level::TRACE, "foo", bar.baz = 2, quux = 3); - span!(target: "foo_events", parent: None, Level::TRACE, "foo", bar.baz = 2, quux = 4,); - span!(target: "foo_events", parent: None, Level::TRACE, "foo"); - span!(target: "foo_events", parent: None, Level::TRACE, "bar",); - span!(parent: None, Level::DEBUG, "foo", bar.baz = 2, quux = 3); - span!(parent: None, Level::DEBUG, "foo", bar.baz = 2, quux = 4,); - span!(parent: None, Level::DEBUG, "foo"); - span!(parent: None, Level::DEBUG, "bar",); + span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo", bar.baz = 2, quux = 3); + span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo", bar.baz = 2, quux = 3); + span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo", bar.baz = 2, quux = 4,); + span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo"); + span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "bar",); + span!(parent: ::core::option::Option::None, Level::DEBUG, "foo", bar.baz = 2, quux = 3); + span!(parent: ::core::option::Option::None, Level::DEBUG, "foo", bar.baz = 2, quux = 4,); + span!(parent: ::core::option::Option::None, Level::DEBUG, "foo"); + span!(parent: ::core::option::Option::None, Level::DEBUG, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn trace_span_root() { - trace_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3); - trace_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,); - trace_span!(target: "foo_events", parent: None, "foo"); - trace_span!(target: "foo_events", parent: None, "bar",); - trace_span!(parent: None, "foo", bar.baz = 2, quux = 3); - trace_span!(parent: None, "foo", bar.baz = 2, quux = 4,); - trace_span!(parent: None, "foo"); - trace_span!(parent: None, "bar",); + trace_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + trace_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + trace_span!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + trace_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",); + trace_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + trace_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + trace_span!(parent: ::core::option::Option::None, "foo"); + trace_span!(parent: ::core::option::Option::None, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn debug_span_root() { - debug_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3); - debug_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,); - debug_span!(target: "foo_events", parent: None, "foo"); - debug_span!(target: "foo_events", parent: None, "bar",); - debug_span!(parent: None, "foo", bar.baz = 2, quux = 3); - debug_span!(parent: None, "foo", bar.baz = 2, quux = 4,); - debug_span!(parent: None, "foo"); - debug_span!(parent: None, "bar",); + debug_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + debug_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + debug_span!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + debug_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",); + debug_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + debug_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + debug_span!(parent: ::core::option::Option::None, "foo"); + debug_span!(parent: ::core::option::Option::None, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn info_span_root() { - info_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3); - info_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,); - info_span!(target: "foo_events", parent: None, "foo"); - info_span!(target: "foo_events", parent: None, "bar",); - info_span!(parent: None, "foo", bar.baz = 2, quux = 3); - info_span!(parent: None, "foo", bar.baz = 2, quux = 4,); - info_span!(parent: None, "foo"); - info_span!(parent: None, "bar",); + info_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + info_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + info_span!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + info_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",); + info_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + info_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + info_span!(parent: ::core::option::Option::None, "foo"); + info_span!(parent: ::core::option::Option::None, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn warn_span_root() { - warn_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3); - warn_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,); - warn_span!(target: "foo_events", parent: None, "foo"); - warn_span!(target: "foo_events", parent: None, "bar",); - warn_span!(parent: None, "foo", bar.baz = 2, quux = 3); - warn_span!(parent: None, "foo", bar.baz = 2, quux = 4,); - warn_span!(parent: None, "foo"); - warn_span!(parent: None, "bar",); + warn_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + warn_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + warn_span!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + warn_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",); + warn_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + warn_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + warn_span!(parent: ::core::option::Option::None, "foo"); + warn_span!(parent: ::core::option::Option::None, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn error_span_root() { - error_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 3); - error_span!(target: "foo_events", parent: None, "foo", bar.baz = 2, quux = 4,); - error_span!(target: "foo_events", parent: None, "foo"); - error_span!(target: "foo_events", parent: None, "bar",); - error_span!(parent: None, "foo", bar.baz = 2, quux = 3); - error_span!(parent: None, "foo", bar.baz = 2, quux = 4,); - error_span!(parent: None, "foo"); - error_span!(parent: None, "bar",); + error_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + error_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + error_span!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + error_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",); + error_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3); + error_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,); + error_span!(parent: ::core::option::Option::None, "foo"); + error_span!(parent: ::core::option::Option::None, "bar",); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] @@ -605,144 +609,144 @@ fn error() { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn event_root() { - event!(parent: None, Level::DEBUG, foo = ?3, bar.baz = %2, quux = false); + event!(parent: ::core::option::Option::None, Level::DEBUG, foo = ?3, bar.baz = %2, quux = false); event!( - parent: None, + parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 2, quux = false ); - event!(parent: None, Level::DEBUG, foo = 3, bar.baz = 3,); - event!(parent: None, Level::DEBUG, "foo"); - event!(parent: None, Level::DEBUG, "foo: {}", 3); - event!(parent: None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux"); - event!(parent: None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - event!(parent: None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - event!(parent: None, Level::DEBUG, { foo = ?2, bar.baz = %78 }, "quux"); - event!(target: "foo_events", parent: None, Level::DEBUG, foo = 3, bar.baz = 2, quux = false); - event!(target: "foo_events", parent: None, Level::DEBUG, foo = 3, bar.baz = 3,); - event!(target: "foo_events", parent: None, Level::DEBUG, "foo"); - event!(target: "foo_events", parent: None, Level::DEBUG, "foo: {}", 3); - event!(target: "foo_events", parent: None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux"); - event!(target: "foo_events", parent: None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - event!(target: "foo_events", parent: None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - event!(target: "foo_events", parent: None, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux"); + event!(parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 3,); + event!(parent: ::core::option::Option::None, Level::DEBUG, "foo"); + event!(parent: ::core::option::Option::None, Level::DEBUG, "foo: {}", 3); + event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux"); + event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = ?2, bar.baz = %78 }, "quux"); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 2, quux = false); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 3,); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, "foo"); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, "foo: {}", 3); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux"); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn trace_root() { - trace!(parent: None, foo = ?3, bar.baz = %2, quux = false); - trace!(parent: None, foo = 3, bar.baz = 2, quux = false); - trace!(parent: None, foo = 3, bar.baz = 3,); - trace!(parent: None, "foo"); - trace!(parent: None, "foo: {}", 3); - trace!(parent: None, { foo = 3, bar.baz = 80 }, "quux"); - trace!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - trace!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - trace!(parent: None, { foo = 2, bar.baz = 78 }, "quux"); - trace!(parent: None, { foo = ?2, bar.baz = %78 }, "quux"); - trace!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false); - trace!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,); - trace!(target: "foo_events", parent: None, "foo"); - trace!(target: "foo_events", parent: None, "foo: {}", 3); - trace!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux"); - trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - trace!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux"); + trace!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false); + trace!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + trace!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + trace!(parent: ::core::option::Option::None, "foo"); + trace!(parent: ::core::option::Option::None, "foo: {}", 3); + trace!(parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + trace!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + trace!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + trace!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 78 }, "quux"); + trace!(parent: ::core::option::Option::None, { foo = ?2, bar.baz = %78 }, "quux"); + trace!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + trace!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + trace!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + trace!(target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3); + trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn debug_root() { - debug!(parent: None, foo = ?3, bar.baz = %2, quux = false); - debug!(parent: None, foo = 3, bar.baz = 2, quux = false); - debug!(parent: None, foo = 3, bar.baz = 3,); - debug!(parent: None, "foo"); - debug!(parent: None, "foo: {}", 3); - debug!(parent: None, { foo = 3, bar.baz = 80 }, "quux"); - debug!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - debug!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - debug!(parent: None, { foo = 2, bar.baz = 78 }, "quux"); - debug!(parent: None, { foo = ?2, bar.baz = %78 }, "quux"); - debug!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false); - debug!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,); - debug!(target: "foo_events", parent: None, "foo"); - debug!(target: "foo_events", parent: None, "foo: {}", 3); - debug!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux"); - debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - debug!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux"); + debug!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false); + debug!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + debug!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + debug!(parent: ::core::option::Option::None, "foo"); + debug!(parent: ::core::option::Option::None, "foo: {}", 3); + debug!(parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + debug!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + debug!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + debug!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 78 }, "quux"); + debug!(parent: ::core::option::Option::None, { foo = ?2, bar.baz = %78 }, "quux"); + debug!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + debug!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + debug!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + debug!(target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3); + debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn info_root() { - info!(parent: None, foo = ?3, bar.baz = %2, quux = false); - info!(parent: None, foo = 3, bar.baz = 2, quux = false); - info!(parent: None, foo = 3, bar.baz = 3,); - info!(parent: None, "foo"); - info!(parent: None, "foo: {}", 3); - info!(parent: None, { foo = 3, bar.baz = 80 }, "quux"); - info!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - info!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - info!(parent: None, { foo = 2, bar.baz = 78 }, "quux"); - info!(parent: None, { foo = ?2, bar.baz = %78 }, "quux"); - info!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false); - info!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,); - info!(target: "foo_events", parent: None, "foo"); - info!(target: "foo_events", parent: None, "foo: {}", 3); - info!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux"); - info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - info!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux"); + info!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false); + info!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + info!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + info!(parent: ::core::option::Option::None, "foo"); + info!(parent: ::core::option::Option::None, "foo: {}", 3); + info!(parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + info!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + info!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + info!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 78 }, "quux"); + info!(parent: ::core::option::Option::None, { foo = ?2, bar.baz = %78 }, "quux"); + info!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + info!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + info!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + info!(target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3); + info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn warn_root() { - warn!(parent: None, foo = ?3, bar.baz = %2, quux = false); - warn!(parent: None, foo = 3, bar.baz = 2, quux = false); - warn!(parent: None, foo = 3, bar.baz = 3,); - warn!(parent: None, "foo"); - warn!(parent: None, "foo: {}", 3); - warn!(parent: None, { foo = 3, bar.baz = 80 }, "quux"); - warn!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - warn!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - warn!(parent: None, { foo = 2, bar.baz = 78 }, "quux"); - warn!(parent: None, { foo = ?2, bar.baz = %78 }, "quux"); - warn!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false); - warn!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,); - warn!(target: "foo_events", parent: None, "foo"); - warn!(target: "foo_events", parent: None, "foo: {}", 3); - warn!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux"); - warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - warn!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux"); + warn!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false); + warn!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + warn!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + warn!(parent: ::core::option::Option::None, "foo"); + warn!(parent: ::core::option::Option::None, "foo: {}", 3); + warn!(parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + warn!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + warn!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + warn!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 78 }, "quux"); + warn!(parent: ::core::option::Option::None, { foo = ?2, bar.baz = %78 }, "quux"); + warn!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + warn!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + warn!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + warn!(target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3); + warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[test] fn error_root() { - error!(parent: None, foo = ?3, bar.baz = %2, quux = false); - error!(parent: None, foo = 3, bar.baz = 2, quux = false); - error!(parent: None, foo = 3, bar.baz = 3,); - error!(parent: None, "foo"); - error!(parent: None, "foo: {}", 3); - error!(parent: None, { foo = 3, bar.baz = 80 }, "quux"); - error!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - error!(parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - error!(parent: None, { foo = 2, bar.baz = 78 }, "quux"); - error!(parent: None, { foo = ?2, bar.baz = %78 }, "quux"); - error!(target: "foo_events", parent: None, foo = 3, bar.baz = 2, quux = false); - error!(target: "foo_events", parent: None, foo = 3, bar.baz = 3,); - error!(target: "foo_events", parent: None, "foo"); - error!(target: "foo_events", parent: None, "foo: {}", 3); - error!(target: "foo_events", parent: None, { foo = 3, bar.baz = 80 }, "quux"); - error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); - error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); - error!(target: "foo_events", parent: None, { foo = 2, bar.baz = 78, }, "quux"); + error!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false); + error!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + error!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + error!(parent: ::core::option::Option::None, "foo"); + error!(parent: ::core::option::Option::None, "foo: {}", 3); + error!(parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + error!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + error!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + error!(parent: ::core::option::Option::None, { foo = 2, bar.baz = 78 }, "quux"); + error!(parent: ::core::option::Option::None, { foo = ?2, bar.baz = %78 }, "quux"); + error!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false); + error!(target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,); + error!(target: "foo_events", parent: ::core::option::Option::None, "foo"); + error!(target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3); + error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux"); + error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true); + error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false); + error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux"); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] @@ -913,7 +917,7 @@ fn field_shorthand_only() { #[test] fn borrow_val_events() { // Reproduces https://github.com/tokio-rs/tracing/issues/954 - let mut foo = (String::new(), String::new()); + let mut foo = (::std::string::String::new(), ::std::string::String::new()); let zero = &mut foo.0; trace!(one = ?foo.1); debug!(one = ?foo.1); @@ -927,7 +931,7 @@ fn borrow_val_events() { #[test] fn borrow_val_spans() { // Reproduces https://github.com/tokio-rs/tracing/issues/954 - let mut foo = (String::new(), String::new()); + let mut foo = (::std::string::String::new(), ::std::string::String::new()); let zero = &mut foo.0; let _span = trace_span!("span", one = ?foo.1); let _span = debug_span!("span", one = ?foo.1); From ef201ab3a9134c25b3d344a2df9c3f9ce74db88f Mon Sep 17 00:00:00 2001 From: David Barsky Date: Thu, 3 Aug 2023 18:38:14 -0400 Subject: [PATCH 029/101] chores: fix clippy lints (#2673) --- tracing-journald/src/socket.rs | 2 +- tracing-subscriber/src/fmt/time/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tracing-journald/src/socket.rs b/tracing-journald/src/socket.rs index bba53d8658..6803bf34e0 100644 --- a/tracing-journald/src/socket.rs +++ b/tracing-journald/src/socket.rs @@ -68,7 +68,7 @@ pub fn send_one_fd_to>(socket: &UnixDatagram, fd: RawFd, path: P) msg.msg_control = unsafe { cmsg_buffer.buffer.as_mut_ptr() as _ }; msg.msg_controllen = unsafe { CMSG_SPACE(size_of::() as _) as _ }; - let mut cmsg: &mut cmsghdr = + let cmsg: &mut cmsghdr = unsafe { CMSG_FIRSTHDR(&msg).as_mut() }.expect("Control message buffer exhausted"); cmsg.cmsg_level = SOL_SOCKET; diff --git a/tracing-subscriber/src/fmt/time/mod.rs b/tracing-subscriber/src/fmt/time/mod.rs index fd77b2c750..21d692bbb0 100644 --- a/tracing-subscriber/src/fmt/time/mod.rs +++ b/tracing-subscriber/src/fmt/time/mod.rs @@ -48,7 +48,7 @@ pub trait FormatTime { /// # } /// ``` pub fn time() -> SystemTime { - SystemTime::default() + SystemTime } /// Returns a new `Uptime` timestamp provider. From 79dd541ff63f9f860a954955a3123f63cf7173c0 Mon Sep 17 00:00:00 2001 From: Yuta Yamaguchi Date: Tue, 8 Aug 2023 03:28:05 +0900 Subject: [PATCH 030/101] tracing: add tracing-cloudwatch to related cates in README (#2667) --- README.md | 2 ++ tracing/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/README.md b/README.md index 7fb11378ff..a9474453ca 100644 --- a/README.md +++ b/README.md @@ -403,6 +403,7 @@ are not maintained by the `tokio` project. These include: - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format. - [`tracing-chrome`] provides a layer that exports trace data that can be viewed in `chrome://tracing`. - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests. +- [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs. (if you're the maintainer of a `tracing` ecosystem crate not in this list, please let us know!) @@ -445,6 +446,7 @@ please let us know!) [`tracing-chrome`]: https://crates.io/crates/tracing-chrome [`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing [`reqwest`]: https://crates.io/crates/reqwest +[`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch **Note:** that some of the ecosystem crates are currently unreleased and undergoing active development. They may be less stable than `tracing` and diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 1494048523..f7439c75d1 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -808,6 +808,7 @@ //! - [`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. //! - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests. +//! - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs. //! //! If you're the maintainer of a `tracing` ecosystem crate not listed above, //! please let us know! We'd love to add your project to the list! @@ -848,6 +849,7 @@ //! [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt //! [`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing //! [`reqwest`]: https://crates.io/crates/reqwest +//! [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch //! //!
//!

From 941b1591faeea55d62c1c8cf524ffa3e95b66887 Mon Sep 17 00:00:00 2001
From: Ana Hobden 
Date: Wed, 9 Aug 2023 11:52:16 -0700
Subject: [PATCH 031/101] documentation: Add article and talk by hoverbear
 (#2679)

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index a9474453ca..3eedc95d07 100644
--- a/README.md
+++ b/README.md
@@ -462,14 +462,17 @@ Tracing.
 * [Diagnostics with Tracing][tokio-blog-2019-08] on the Tokio blog, August 2019
 * [Production-Grade Logging in Rust Applications][production-logging-2020], November 2020
 * [Custom Logging in Rust using `tracing` and `tracing-subscriber`, part 1][custom-logging-part-1] and [part 2][custom-logging-part-2], October 2021
+* [Instrumenting Axum projects][detsys-blog-2023-08], August 2023
 
 [tokio-blog-2019-08]: https://tokio.rs/blog/2019-08-tracing/
+[detsys-blog-2023-08]: https://determinate.systems/posts/instrumenting-axum
 
 #### Talks
 
 * [Bay Area Rust Meetup talk and Q&A][bay-rust-2019-03], March 2019
 * [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
 
 [bay-rust-2019-03]: https://www.youtube.com/watch?v=j_kXRg3zlec
 [rust-conf-2019-08-video]: https://www.youtube.com/watch?v=JjItsfqFIdo
@@ -479,6 +482,7 @@ Tracing.
 [production-logging-2020]: https://medium.com/better-programming/production-grade-logging-in-rust-applications-2c7fffd108a6
 [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
 
 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 5321e52263e24788a78363bd4411d8c04671bee3 Mon Sep 17 00:00:00 2001
From: Nikita Popov 
Date: Mon, 14 Aug 2023 19:08:45 +0200
Subject: [PATCH 032/101] core: ensure callsites in test have unique addresses
 (#2681)

The test relies on TEST_CALLSITE_1 and TEST_CALLSITE_2 to have
different addresses. However, as they are zero-sized types, this
is not guaranteed.

This fixes the test failure with LLVM 17 and certain optimization
options reported at https://github.com/rust-lang/rust/issues/114699.
---
 tracing-core/src/field.rs | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index 80ca065ba3..2c8596117c 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -978,8 +978,9 @@ mod test {
     use super::*;
     use crate::metadata::{Kind, Level, Metadata};
 
-    struct TestCallsite1;
-    static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1;
+    // Make sure TEST_CALLSITE_* have non-zero size, so they can't be located at the same address.
+    struct TestCallsite1(u8);
+    static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1(0);
     static TEST_META_1: Metadata<'static> = metadata! {
         name: "field_test1",
         target: module_path!(),
@@ -999,8 +1000,8 @@ mod test {
         }
     }
 
-    struct TestCallsite2;
-    static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2;
+    struct TestCallsite2(u8);
+    static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2(0);
     static TEST_META_2: Metadata<'static> = metadata! {
         name: "field_test2",
         target: module_path!(),

From 8f4157e0f8ee3bfaba075601d99b94cd48e63c24 Mon Sep 17 00:00:00 2001
From: Alexander Tesfamichael 
Date: Mon, 14 Aug 2023 19:09:18 +0200
Subject: [PATCH 033/101] docs(field): remove duplicate wording (#2674)

---
 tracing-core/src/field.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index 2c8596117c..af884da429 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -1,8 +1,8 @@
 //! Span and `Event` key-value data.
 //!
-//! Spans and events may be annotated with key-value data, referred to as known
-//! as _fields_. These fields consist of a mapping from a key (corresponding to
-//! a `&str` but represented internally as an array index) to a [`Value`].
+//! Spans and events may be annotated with key-value data, known as _fields_.
+//! These fields consist of a mapping from a key (corresponding to a `&str` but
+//! represented internally as an array index) to a [`Value`].
 //!
 //! # `Value`s and `Collect`s
 //!

From 887b9e79bacc85f894688f4f4bc2ef93760c28f3 Mon Sep 17 00:00:00 2001
From: Jonathan Woollett-Light 
Date: Mon, 14 Aug 2023 18:11:28 +0100
Subject: [PATCH 034/101] docs: Add `clippy-tracing` to related crates (#2628)

## Motivation

Sharing tooling.

## Solution

Adds `clippy-tracing` to related crates.

Closes #2627
---
 README.md          | 2 ++
 tracing/src/lib.rs | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/README.md b/README.md
index 3eedc95d07..054f999b48 100644
--- a/README.md
+++ b/README.md
@@ -404,6 +404,7 @@ are not maintained by the `tokio` project. These include:
 - [`tracing-chrome`] provides a layer that exports trace data that can be viewed in `chrome://tracing`.
 - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
 - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs.
+- [`clippy-tracing`] provides a tool to add, remove and check for `tracing::instrument`.
 
 (if you're the maintainer of a `tracing` ecosystem crate not in this list,
 please let us know!)
@@ -447,6 +448,7 @@ please let us know!)
 [`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing
 [`reqwest`]: https://crates.io/crates/reqwest
 [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch
+[`clippy-tracing`]: https://crates.io/crates/clippy-tracing
 
 **Note:** that some of the ecosystem crates are currently unreleased and
 undergoing active development. They may be less stable than `tracing` and
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index f7439c75d1..f698648232 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -809,6 +809,7 @@
 //!  - [`tracing-logfmt`] provides a layer that formats events and spans into the logfmt format.
 //!  - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests.
 //!  - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs.
+//!  - [`clippy-tracing`] provides a tool to add, remove and check for `tracing::instrument`.
 //!
 //! If you're the maintainer of a `tracing` ecosystem crate not listed above,
 //! please let us know! We'd love to add your project to the list!
@@ -850,6 +851,7 @@
 //! [`reqwest-tracing`]: https://crates.io/crates/reqwest-tracing
 //! [`reqwest`]: https://crates.io/crates/reqwest
 //! [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch
+//! [`clippy-tracing`]: https://crates.io/crates/clippy-tracing
 //!
 //! 
//!

From 6c530d5b07c678d8f103c216edd62bc22c370679 Mon Sep 17 00:00:00 2001
From: Harry Barber <106155934+hlbarber@users.noreply.github.com>
Date: Thu, 17 Aug 2023 17:36:34 +0100
Subject: [PATCH 035/101] tracing: fix `wasm_bindgen_test` macros (#2675)

## Motivation

Tests involving `wasm_bindgen_test` currently fail:

https://github.com/tokio-rs/tracing/actions/runs/5756318807/job/15605512576

## Solution

- [x] Use `extern crate wasm_bindgen_test` to side-step `no_implicit_prelude`.
- [x] https://github.com/rustwasm/wasm-bindgen/pull/3549
- [ ] Consume the release `wasm_bindgen_test` containing said change.
---
 tracing/tests/macros.rs | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index 423984da27..6d9a333b68 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -2,6 +2,12 @@
 // We call all macros in this module with `no_implicit_prelude` to ensure they do not depend on the standard prelude.
 #![no_implicit_prelude]
 extern crate tracing;
+#[cfg(target_arch = "wasm32")]
+extern crate wasm_bindgen_test;
+
+// TODO: remove this once https://github.com/tokio-rs/tracing/pull/2675#issuecomment-1667628907 is resolved
+#[cfg(target_arch = "wasm32")]
+use ::core::option::Option::None;
 
 use tracing::{
     callsite, debug, debug_span, enabled, error, error_span, event, event_enabled, info, info_span,

From 81ab9d6faedaf5a1e4b9d69567949052a401e723 Mon Sep 17 00:00:00 2001
From: "David M. Lary" 
Date: Thu, 17 Aug 2023 11:56:29 -0500
Subject: [PATCH 036/101] subscriber: support `NO_COLOR` in `fmt::Subscriber`
 (#2647)

## Motivation

It's necessary at times to be able to disable ANSI color output for
rust utilities using `tracing`.  The informal standard for this is the
`NO_COLOR` environment variable described here: https://no-color.org/

Further details/discussion in #2388

## Solution

This commit updates `fmt::Subscriber` to check the `NO_COLOR`
environment variable when determining whether ANSI color output is
enabled by default. As described in the spec, any non-empty value set
for `NO_COLOR` will cause ANSI color support to be disabled by default.

If the user manually overrides ANSI color support, such as by calling
`with_ansi(true)`, this will still enable ANSI colors, even if
`NO_COLOR` is set. The `NO_COLOR` env var only effects the default
behavior.

Fixes #2388
---
 tracing-subscriber/src/fmt/fmt_subscriber.rs | 89 +++++++++++++++++++-
 1 file changed, 87 insertions(+), 2 deletions(-)

diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs
index 366660fb82..64f9dfc1bb 100644
--- a/tracing-subscriber/src/fmt/fmt_subscriber.rs
+++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs
@@ -6,7 +6,7 @@ use crate::{
 };
 use format::{FmtSpan, TimingDisplay};
 use std::{
-    any::TypeId, cell::RefCell, fmt, io, marker::PhantomData, ops::Deref, ptr::NonNull,
+    any::TypeId, cell::RefCell, env, fmt, io, marker::PhantomData, ops::Deref, ptr::NonNull,
     time::Instant,
 };
 use tracing_core::{
@@ -277,6 +277,15 @@ impl Subscriber {
     /// Sets whether or not the formatter emits ANSI terminal escape codes
     /// for colors and other text formatting.
     ///
+    /// When the "ansi" crate feature flag is enabled, ANSI colors are enabled
+    /// by default unless the [`NO_COLOR`] environment variable is set to
+    /// a non-empty value.  If the [`NO_COLOR`] environment variable is set to
+    /// any non-empty value, then ANSI colors will be suppressed by default.
+    /// The [`with_ansi`] and [`set_ansi`] methods can be used to forcibly
+    /// enable ANSI colors, overriding any [`NO_COLOR`] environment variable.
+    ///
+    /// [`NO_COLOR`]: https://no-color.org/
+    ///
     /// Enabling ANSI escapes (calling `with_ansi(true)`) requires the "ansi"
     /// crate feature flag. Calling `with_ansi(true)` without the "ansi"
     /// feature flag enabled will panic if debug assertions are enabled, or
@@ -289,6 +298,9 @@ impl Subscriber {
     /// ANSI escape codes can ensure that they are not used, regardless of
     /// whether or not other crates in the dependency graph enable the "ansi"
     /// feature flag.
+    ///
+    /// [`with_ansi`]: Subscriber::with_ansi
+    /// [`set_ansi`]: Subscriber::set_ansi
     pub fn with_ansi(self, ansi: bool) -> Self {
         #[cfg(not(feature = "ansi"))]
         if ansi {
@@ -680,12 +692,16 @@ impl Subscriber {
 
 impl Default for Subscriber {
     fn default() -> Self {
+        // only enable ANSI when the feature is enabled, and the NO_COLOR
+        // environment variable is unset or empty.
+        let ansi = cfg!(feature = "ansi") && env::var("NO_COLOR").map_or(true, |v| v.is_empty());
+
         Subscriber {
             fmt_fields: format::DefaultFields::default(),
             fmt_event: format::Format::default(),
             fmt_span: format::FmtSpanConfig::default(),
             make_writer: io::stdout,
-            is_ansi: cfg!(feature = "ansi"),
+            is_ansi: ansi,
             log_internal_errors: false,
             _inner: PhantomData,
         }
@@ -1505,4 +1521,73 @@ mod test {
             actual.as_str()
         );
     }
+
+    // Because we need to modify an environment variable for these test cases,
+    // we do them all in a single test.
+    #[cfg(feature = "ansi")]
+    #[test]
+    fn subscriber_no_color() {
+        const NO_COLOR: &str = "NO_COLOR";
+
+        // Restores the previous value of the `NO_COLOR` env variable when
+        // dropped.
+        //
+        // This is done in a `Drop` implementation, rather than just resetting
+        // the value at the end of the test, so that the previous value is
+        // restored even if the test panics.
+        struct RestoreEnvVar(Result);
+        impl Drop for RestoreEnvVar {
+            fn drop(&mut self) {
+                match self.0 {
+                    Ok(ref var) => env::set_var(NO_COLOR, var),
+                    Err(_) => env::remove_var(NO_COLOR),
+                }
+            }
+        }
+
+        let _saved_no_color = RestoreEnvVar(env::var(NO_COLOR));
+
+        let cases: Vec<(Option<&str>, bool)> = vec![
+            (Some("0"), false),   // any non-empty value disables ansi
+            (Some("off"), false), // any non-empty value disables ansi
+            (Some("1"), false),
+            (Some(""), true), // empty value does not disable ansi
+            (None, true),
+        ];
+
+        for (var, ansi) in cases {
+            if let Some(value) = var {
+                env::set_var(NO_COLOR, value);
+            } else {
+                env::remove_var(NO_COLOR);
+            }
+
+            let subscriber: Subscriber<()> = fmt::Subscriber::default();
+            assert_eq!(
+                subscriber.is_ansi, ansi,
+                "NO_COLOR={:?}; Subscriber::default().is_ansi should be {}",
+                var, ansi
+            );
+
+            // with_ansi should override any `NO_COLOR` value
+            let subscriber: Subscriber<()> = fmt::Subscriber::default().with_ansi(true);
+            assert!(
+                subscriber.is_ansi,
+                "NO_COLOR={:?}; Subscriber::default().with_ansi(true).is_ansi should be true",
+                var
+            );
+
+            // set_ansi should override any `NO_COLOR` value
+            let mut subscriber: Subscriber<()> = fmt::Subscriber::default();
+            subscriber.set_ansi(true);
+            assert!(
+                subscriber.is_ansi,
+                "NO_COLOR={:?}; subscriber.set_ansi(true); subscriber.is_ansi should be true",
+                var
+            );
+        }
+
+        // dropping `_saved_no_color` will restore the previous value of
+        // `NO_COLOR`.
+    }
 }

From bf05c61b7234eb1a196ef8d30f773d3ea6b60ce9 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Tue, 29 Aug 2023 11:16:59 -0700
Subject: [PATCH 037/101] chore: fix new warnings in Rust 1.72.0 (#2700)

This branch fixes a handful of new warnings which have shown up after
updating to Rust 1.72.0.

This includes:

* `clippy::redundant_closure_call` in macros --- allowed because the
  macro sometimes calls a function that isn't a closure, and the closure
  is just used in the case where it's not a function.
* Unnecessary uses of `#` in raw string literals that don't contain `"`
  characters.
* Dead code warnings with specific feature flag combinations in
  `tracing-subscriber`.

In addition, I've fixed a broken RustDoc link that was making the
Netlify build sad.
---
 netlify.toml                                  |  1 +
 tracing-core/src/field.rs                     |  8 +++++++
 tracing-subscriber/src/field/mod.rs           |  3 +--
 tracing-subscriber/src/filter/directive.rs    |  5 ++--
 .../src/filter/env/directive.rs               | 23 ++++++++++++-------
 tracing-subscriber/src/fmt/format/mod.rs      | 12 ++++++----
 tracing-subscriber/src/fmt/mod.rs             | 14 +++++++----
 tracing-subscriber/src/fmt/writer.rs          | 11 ++++-----
 tracing-subscriber/src/registry/mod.rs        |  6 ++---
 tracing-subscriber/src/subscribe/mod.rs       |  9 ++++----
 tracing/src/lib.rs                            |  2 +-
 11 files changed, 56 insertions(+), 38 deletions(-)

diff --git a/netlify.toml b/netlify.toml
index a59c675e27..d552741a23 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -8,6 +8,7 @@
 [build.environment]
   RUSTDOCFLAGS="""
     -D warnings \
+    --force-warn rustdoc::redundant-explicit-links \
     --force-warn renamed-and-removed-lints \
     --cfg docsrs \
     --html-before-content /opt/build/repo/assets/warning.html \
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index af884da429..de564d8b3c 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -373,6 +373,10 @@ macro_rules! impl_one_value {
         impl $crate::sealed::Sealed for $value_ty {}
         impl $crate::field::Value for $value_ty {
             fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) {
+                // `op` is always a function; the closure is used because
+                // sometimes there isn't a real function corresponding to that
+                // operation. the clippy warning is not that useful here.
+                #[allow(clippy::redundant_closure_call)]
                 visitor.$record(key, $op(*self))
             }
         }
@@ -388,6 +392,10 @@ macro_rules! impl_one_value {
         impl $crate::sealed::Sealed for ty_to_nonzero!($value_ty) {}
         impl $crate::field::Value for ty_to_nonzero!($value_ty) {
             fn record(&self, key: &$crate::field::Field, visitor: &mut dyn $crate::field::Visit) {
+                // `op` is always a function; the closure is used because
+                // sometimes there isn't a real function corresponding to that
+                // operation. the clippy warning is not that useful here.
+                #[allow(clippy::redundant_closure_call)]
                 visitor.$record(key, $op(self.get()))
             }
         }
diff --git a/tracing-subscriber/src/field/mod.rs b/tracing-subscriber/src/field/mod.rs
index 705fa5841c..e55ec82d6e 100644
--- a/tracing-subscriber/src/field/mod.rs
+++ b/tracing-subscriber/src/field/mod.rs
@@ -55,7 +55,7 @@ pub trait VisitOutput: Visit {
 /// Extension trait implemented by types which can be recorded by a [visitor].
 ///
 /// This allows writing code that is generic over `tracing_core`'s
-/// [`span::Attributes`][attr], [`span::Record`][rec], and [`Event`][event]
+/// [`span::Attributes`][attr], [`span::Record`][rec], and [`Event`]
 /// types. These types all provide inherent `record` methods that allow a
 /// visitor to record their fields, but there is no common trait representing this.
 ///
@@ -85,7 +85,6 @@ pub trait VisitOutput: Visit {
 /// [visitor]: tracing_core::field::Visit
 /// [attr]: tracing_core::span::Attributes
 /// [rec]: tracing_core::span::Record
-/// [event]: tracing_core::event::Event
 pub trait RecordFields: crate::sealed::Sealed {
     /// Record all the fields in `self` with the provided `visitor`.
     fn record(&self, visitor: &mut dyn Visit);
diff --git a/tracing-subscriber/src/filter/directive.rs b/tracing-subscriber/src/filter/directive.rs
index 3745662385..8e00a57a43 100644
--- a/tracing-subscriber/src/filter/directive.rs
+++ b/tracing-subscriber/src/filter/directive.rs
@@ -45,7 +45,8 @@ enum ParseErrorKind {
 // === impl DirectiveSet ===
 
 impl DirectiveSet {
-    #[cfg(feature = "std")]
+    // this is only used by `env-filter`.
+    #[cfg(all(feature = "std", feature = "env-filter"))]
     pub(crate) fn is_empty(&self) -> bool {
         self.directives.is_empty()
     }
@@ -388,7 +389,7 @@ impl FromStr for StaticDirective {
 // === impl ParseError ===
 
 impl ParseError {
-    #[cfg(feature = "std")]
+    #[cfg(all(feature = "std", feature = "env-filter"))]
     pub(crate) fn new() -> Self {
         ParseError {
             kind: ParseErrorKind::Other(None),
diff --git a/tracing-subscriber/src/filter/env/directive.rs b/tracing-subscriber/src/filter/env/directive.rs
index f0ccbd7ed3..1f0b118661 100644
--- a/tracing-subscriber/src/filter/env/directive.rs
+++ b/tracing-subscriber/src/filter/env/directive.rs
@@ -120,8 +120,9 @@ impl Directive {
     }
 
     pub(super) fn parse(from: &str, regex: bool) -> Result {
-        static DIRECTIVE_RE: Lazy = Lazy::new(|| Regex::new(
-            r"(?x)
+        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
@@ -135,15 +136,18 @@ impl Directive {
                     #              `note: we match log level names case-insensitively
             )?
             $
-            "
-        )
-        .unwrap());
+            ",
+            )
+            .unwrap()
+        });
         static SPAN_PART_RE: Lazy =
-            Lazy::new(|| Regex::new(r#"(?P[^\]\{]+)?(?:\{(?P[^\}]*)\})?"#).unwrap());
+            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)
+            Lazy::new(|| {
+                Regex::new(
+                    r"(?x)
                 (
                     # field name
                     [[:word:]][[[:word:]]\.]*
@@ -152,7 +156,10 @@ impl Directive {
                 )
                 # trailing comma or EOS
                 (?:,\s?|$)
-            "#).unwrap());
+            ",
+                )
+                .unwrap()
+            });
 
         let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?;
 
diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs
index c9760138ab..2d0fbfa6d2 100644
--- a/tracing-subscriber/src/fmt/format/mod.rs
+++ b/tracing-subscriber/src/fmt/format/mod.rs
@@ -333,9 +333,8 @@ pub struct FieldFnVisitor<'a, F> {
 /// The compact format includes fields from all currently entered spans, after
 /// the event's fields. Span fields are ordered (but not grouped) grouped by
 /// span, and span names are  not shown.A more compact representation of the
-/// event's [`Level`](tracing::Level) is used, and additional information, such
-/// as the event's target, is disabled by default (but can be enabled
-/// explicitly).
+/// event's [`Level`] is used, and additional information, such as the event's
+/// target, is disabled by default (but can be enabled explicitly).
 ///
 /// # Example Output
 ///
@@ -1067,7 +1066,12 @@ where
 
         let dimmed = writer.dimmed();
         if self.display_target {
-            write!(writer, "{}{}", dimmed.paint(meta.target()), dimmed.paint(":"))?;
+            write!(
+                writer,
+                "{}{}",
+                dimmed.paint(meta.target()),
+                dimmed.paint(":")
+            )?;
         }
 
         if self.display_filename {
diff --git a/tracing-subscriber/src/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs
index 38acf4d092..036099211b 100644
--- a/tracing-subscriber/src/fmt/mod.rs
+++ b/tracing-subscriber/src/fmt/mod.rs
@@ -733,7 +733,7 @@ where
 
     /// Sets the collector being built to use a less verbose formatter.
     ///
-    /// See [`format::Compact`].
+    /// See [`format::Compact`] for details.
     pub fn compact(self) -> CollectorBuilder, F, W>
     where
         N: for<'writer> FormatFields<'writer> + 'static,
@@ -758,7 +758,7 @@ where
 
     /// Sets the collector being built to use a JSON formatter.
     ///
-    /// See [`format::Json`](super::fmt::format::Json)
+    /// See [`format::Json`] for details.
     #[cfg(feature = "json")]
     #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
     pub fn json(self) -> CollectorBuilder, F, W>
@@ -777,7 +777,7 @@ where
 impl CollectorBuilder, F, W> {
     /// Sets the json collector being built to flatten event metadata.
     ///
-    /// See [`format::Json`](super::fmt::format::Json)
+    /// See [`format::Json`] for details.
     pub fn flatten_event(
         self,
         flatten_event: bool,
@@ -791,7 +791,7 @@ impl CollectorBuilder CollectorBuilder MutexGuard<'_, Vec> {
             self.buf.lock().unwrap()
         }
diff --git a/tracing-subscriber/src/fmt/writer.rs b/tracing-subscriber/src/fmt/writer.rs
index fc3d4f4e70..fcd529a563 100644
--- a/tracing-subscriber/src/fmt/writer.rs
+++ b/tracing-subscriber/src/fmt/writer.rs
@@ -17,7 +17,7 @@ use tracing_core::Metadata;
 /// This trait is already implemented for function pointers and
 /// immutably-borrowing closures that return an instance of [`io::Write`], such
 /// as [`io::stdout`] and [`io::stderr`]. Additionally, it is implemented for
-/// [`std::sync::Mutex`][mutex] when the type inside the mutex implements
+/// [`std::sync::Mutex`] when the type inside the mutex implements
 /// [`io::Write`].
 ///
 /// The [`MakeWriter::make_writer_for`] method takes [`Metadata`] describing a
@@ -79,7 +79,7 @@ use tracing_core::Metadata;
 /// ```
 ///
 /// A single instance of a type implementing [`io::Write`] may be used as a
-/// `MakeWriter` by wrapping it in a [`Mutex`][mutex]. For example, we could
+/// `MakeWriter` by wrapping it in a [`Mutex`]. For example, we could
 /// write to a file like so:
 ///
 /// ```
@@ -101,7 +101,6 @@ use tracing_core::Metadata;
 /// [`Event`]: tracing_core::event::Event
 /// [`io::stdout`]: std::io::stdout()
 /// [`io::stderr`]: std::io::stderr()
-/// [mutex]: std::sync::Mutex
 /// [`MakeWriter::make_writer_for`]: MakeWriter::make_writer_for
 /// [`Metadata`]: tracing_core::Metadata
 /// [levels]: tracing_core::Level
@@ -339,7 +338,7 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> {
 
     /// Wraps `self` with a predicate that takes a span or event's [`Metadata`]
     /// and returns a `bool`. The returned [`MakeWriter`]'s
-    /// [`MakeWriter::make_writer_for`][mwf] method will check the predicate to
+    /// [`MakeWriter::make_writer_for`] method will check the predicate to
     /// determine if  a writer should be produced for a given span or event.
     ///
     /// If the predicate returns `false`, the wrapped [`MakeWriter`]'s
@@ -486,7 +485,6 @@ pub trait MakeWriterExt<'a>: MakeWriter<'a> {
 /// requires the `Writer` type to implement [`io::Write`], it's necessary to add
 /// a newtype that forwards the trait implementation.
 ///
-/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
 /// [`MutexGuard`]: https://doc.rust-lang.org/std/sync/struct.MutexGuard.html
 /// [`Mutex`]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
 /// [`MakeWriter`]: trait.MakeWriter.html
@@ -540,13 +538,12 @@ pub struct TestWriter {
 /// ```
 ///
 /// [`Collect`]: tracing::Collect
-/// [`io::Write`]: std::io::Write
 pub struct BoxMakeWriter {
     inner: Box MakeWriter<'a, Writer = Box> + Send + Sync>,
     name: &'static str,
 }
 
-/// A [writer] that is one of two types implementing [`io::Write`][writer].
+/// A [writer] that is one of two types implementing [`io::Write`].
 ///
 /// This may be used by [`MakeWriter`] implementations that may conditionally
 /// return one of two writers.
diff --git a/tracing-subscriber/src/registry/mod.rs b/tracing-subscriber/src/registry/mod.rs
index 48b378d1ed..b712aa73ab 100644
--- a/tracing-subscriber/src/registry/mod.rs
+++ b/tracing-subscriber/src/registry/mod.rs
@@ -205,11 +205,9 @@ pub trait SpanData<'a> {
 
 /// A reference to [span data] and the associated [registry].
 ///
-/// This type implements all the same methods as [`SpanData`][span data], and
-/// provides additional methods for querying the registry based on values from
-/// the span.
+/// This type implements all the same methods as [`SpanData`], and provides
+/// additional methods for querying the registry based on values from the span.
 ///
-/// [span data]: SpanData
 /// [registry]: LookupSpan
 #[derive(Debug)]
 pub struct SpanRef<'a, R: LookupSpan<'a>> {
diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs
index 8601ca3287..6f375654a9 100644
--- a/tracing-subscriber/src/subscribe/mod.rs
+++ b/tracing-subscriber/src/subscribe/mod.rs
@@ -1388,11 +1388,10 @@ pub trait Filter {
     /// multiple invocations of this method. However, note that changes in the
     /// maximum level will **only** be reflected after the callsite [`Interest`]
     /// cache is rebuilt, by calling the
-    /// [`tracing_core::callsite::rebuild_interest_cache`][rebuild] function.
+    /// [`tracing_core::callsite::rebuild_interest_cache`] function.
     /// Therefore, if the `Filter will change the value returned by this
-    /// method, it is responsible for ensuring that
-    /// [`rebuild_interest_cache`][rebuild] is called after the value of the max
-    /// level changes.
+    /// method, it is responsible for ensuring that [`rebuild_interest_cache`]
+    /// is called after the value of the max level changes.
     ///
     /// ## Default Implementation
     ///
@@ -1402,7 +1401,7 @@ pub trait Filter {
     /// [level]: tracing_core::metadata::Level
     /// [`LevelFilter`]: crate::filter::LevelFilter
     /// [`Interest`]: tracing_core::collect::Interest
-    /// [rebuild]: tracing_core::callsite::rebuild_interest_cache
+    /// [`rebuild_interest_cache`]: tracing_core::callsite::rebuild_interest_cache
     fn max_level_hint(&self) -> Option {
         None
     }
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index f698648232..de54bffbd2 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -902,7 +902,7 @@
 //! [event]: Event
 //! [events]: Event
 //! [`collect`]: collect::Collect
-//! [Collect::event]: collect::Collect::event
+//! [Collect::event]: fn@collect::Collect::event
 //! [`enter`]: collect::Collect::enter
 //! [`exit`]: collect::Collect::exit
 //! [`enabled`]: collect::Collect::enabled

From 81ceb65008892826f3a813e9029a4e678beff87a Mon Sep 17 00:00:00 2001
From: Michael Wigard 
Date: Tue, 5 Sep 2023 19:18:40 +0200
Subject: [PATCH 038/101] attributes: fix instrument with "log" feature (#2599)

## Motivation

The instrument macro currently doesn't work with the "log" crate
feature: #2585

## Solution

Change the generated code to create a span if either
`tracing::if_log_enabled!` or `tracing::level_enabled!`. I'm not sure
how to add a test for this or if this is the best solution.

Fixes #2585
---
 tracing-attributes/src/expand.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index bedff8e833..2a55bd0f7f 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -343,7 +343,7 @@ fn gen_block(
         // regression in case the level is enabled.
         let __tracing_attr_span;
         let __tracing_attr_guard;
-        if tracing::level_enabled!(#level) {
+        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();

From 9a7257d7ffcd3ab4de313c524c0a5bf83deee9f0 Mon Sep 17 00:00:00 2001
From: C L Eckhardt <29362105+CLEckhardt@users.noreply.github.com>
Date: Tue, 5 Sep 2023 12:20:06 -0500
Subject: [PATCH 039/101] appender: clarify file appender docs (#2689)

## Motivation

There are a few errors in the file appender docs - this fixes them.

It also wasn't clear/apparent to me that you can create a non-rolling
file appender with the `rolling` module - this calls that out more
clearly.

## Solution

Updates to docs.
---
 tracing-appender/src/lib.rs     | 26 ++++++++++++++++++++------
 tracing-appender/src/rolling.rs | 10 +++++-----
 2 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index 97ad8d4d58..0555ba0435 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -26,20 +26,34 @@
 //!  - Using a combination of [`NonBlocking`] and [`RollingFileAppender`] to allow writes to a log file
 //! without blocking.
 //!
-//! ## Rolling File Appender
+//! ## File Appender
+//!
+//! The [`rolling` module][rolling] provides functions to create rolling and non-rolling file
+//! appenders.
+//!
+//! Rolling file appender rotation options are [`Rotation::MINUTELY`](rolling::Rotation::MINUTELY),
+//! [`Rotation::HOURLY`](rolling::Rotation::HOURLY), and
+//! [`Rotation::DAILY`](rolling::Rotation::DAILY).
+//!
+//! To create a non-rolling file appender, use
+//! [`tracing_appender::rolling::never(/*...*/)`](rolling::never) or
+//! [`Rotation::NEVER`](rolling::Rotation::NEVER).
+//!
+//! The following example creates an hourly rotating file appender that writes to
+//! `/some/directory/prefix.log.YYYY-MM-DD-HH`:
 //!
 //! ```rust
 //! # fn docs() {
 //! let file_appender = tracing_appender::rolling::hourly("/some/directory", "prefix.log");
 //! # }
 //! ```
-//! This creates an hourly rotating file appender that writes to `/some/directory/prefix.log.YYYY-MM-DD-HH`.
-//! [`Rotation::DAILY`](rolling::Rotation::DAILY) and [`Rotation::NEVER`](rolling::Rotation::NEVER) are the other available options.
 //!
-//! The file appender implements [`std::io::Write`][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.
+//! The file appender implements [`std::io::Write`][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.
 //!
-//! The [`rolling` module][rolling]'s documentation provides more detail on how to use this file appender.
+//! See the [`rolling` module][rolling]'s documentation for more detail on how to use this file
+//! appender.
 //!
 //! ## Non-Blocking Writer
 //!
diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs
index 1585c3a087..d82646933a 100644
--- a/tracing-appender/src/rolling.rs
+++ b/tracing-appender/src/rolling.rs
@@ -264,7 +264,7 @@ impl fmt::Debug for RollingFileAppender {
     }
 }
 
-/// Creates a minutely, rolling file appender. This will rotate the log file once per minute.
+/// Creates a minutely-rotating file appender. This will rotate the log file once per minute.
 ///
 /// The appender returned by `rolling::minutely` can be used with `non_blocking` to create
 /// a non-blocking, minutely file appender.
@@ -299,7 +299,7 @@ pub fn minutely(
     RollingFileAppender::new(Rotation::MINUTELY, directory, file_name_prefix)
 }
 
-/// Creates an hourly, rolling file appender.
+/// Creates an hourly-rotating file appender.
 ///
 /// The appender returned by `rolling::hourly` can be used with `non_blocking` to create
 /// a non-blocking, hourly file appender.
@@ -334,7 +334,7 @@ pub fn hourly(
     RollingFileAppender::new(Rotation::HOURLY, directory, file_name_prefix)
 }
 
-/// Creates a file appender that rotates daily.
+/// Creates a daily-rotating file appender.
 ///
 /// The appender returned by `rolling::daily` can be used with `non_blocking` to create
 /// a non-blocking, daily file appender.
@@ -370,13 +370,13 @@ pub fn daily(
     RollingFileAppender::new(Rotation::DAILY, directory, file_name_prefix)
 }
 
-/// Creates a non-rolling, file appender
+/// Creates a non-rolling file appender.
 ///
 /// The appender returned by `rolling::never` can be used with `non_blocking` to create
 /// a non-blocking, non-rotating appender.
 ///
 /// The location of the log file will be specified the `directory` passed in.
-/// `file_name` specifies the prefix of the log file. No date or time is appended.
+/// `file_name` specifies the complete name of the log file (no date or time is appended).
 ///
 /// # Examples
 ///

From bab2e29955c3d0e44edbb3a7541f643f91f55b9d Mon Sep 17 00:00:00 2001
From: Gabi 
Date: Tue, 5 Sep 2023 14:26:03 -0300
Subject: [PATCH 040/101] flame: fix folded formatting (#2710)

## Motivation

The `.folded` format expects a `;`-separated list of the stack function,
optionally followed up by a sample count.

The implementation before this commit added a blank space after each `;`
which made parsers, such as `inferno-flamegraph` mis-interpret the data.

Furthermore, normally one wouldn't expect the filename and line-number
in such stack trace.

## Solution

Remove white-space between `;` for the generated file and remove
filename and line-number by default.
---
 tracing-flame/src/lib.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs
index b123d80b8a..8c37486533 100644
--- a/tracing-flame/src/lib.rs
+++ b/tracing-flame/src/lib.rs
@@ -233,7 +233,7 @@ impl Default for Config {
             empty_samples: true,
             threads_collapsed: false,
             module_path: true,
-            file_and_line: true,
+            file_and_line: false,
         }
     }
 }
@@ -404,7 +404,7 @@ where
 
         if let Some(second) = first.parent() {
             for parent in second.scope().from_root() {
-                stack += "; ";
+                stack += ";";
                 write(&mut stack, parent, &self.config)
                     .expect("expected: write to String never fails");
             }
@@ -446,7 +446,7 @@ where
         }
 
         for parent in first.scope().from_root() {
-            stack += "; ";
+            stack += ";";
             expect!(
                 write(&mut stack, parent, &self.config),
                 "expected: write to String never fails"

From 683cc8a1a4eb2fdf7fda25de6287887bc0e4e958 Mon Sep 17 00:00:00 2001
From: Kornel 
Date: Tue, 5 Sep 2023 18:37:53 +0100
Subject: [PATCH 041/101] attributes: generate less dead code for async block
 return type hint (#2709)

## Motivation

`#[tracing::instrument]` uses `unreachable!()` macro which needlessly
expands to panicking and formatting code. It only needs any `!` type.

## Solution

`loop {}` works just as well for a `!` type, and it crates less work for
the compiler. The code is truly unreachable, so the message would never
be useful. Rust used to be concerned about semantics of empty loops in
LLVM, but this [has been solved](https://reviews.llvm.org/D85393).
---
 tracing-attributes/src/expand.rs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 2a55bd0f7f..196bd90a8e 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -66,11 +66,11 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
     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::let_unit_value, clippy::unreachable, clippy::let_with_type_underscore,
+            clippy::empty_loop
         )]
         if false {
-            let __tracing_attr_fake_return: #return_type =
-                unreachable!("this is just for type inference, and is unreachable code");
+            let __tracing_attr_fake_return: #return_type = loop {};
             return __tracing_attr_fake_return;
         }
     };

From 1c855ae04528e3f6cf1d681827fd2f468edc21fc Mon Sep 17 00:00:00 2001
From: Ethan Brierley 
Date: Tue, 5 Sep 2023 18:57:39 +0100
Subject: [PATCH 042/101] core: allow `ValueSet`s of any length (#2508)

## Motivation

Fixes: #1566

## Solution

This change removes the maximum of 32 fields limitation using const
generics.

Having this arbitrary restriction in place to prevent stack overflows
feels a little misplaced to me since stack size varies between
environments.
---
 tracing-core/src/field.rs | 25 ++---------------------
 tracing/src/lib.rs        | 16 ---------------
 tracing/src/span.rs       |  2 +-
 tracing/tests/macros.rs   | 42 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 45 insertions(+), 40 deletions(-)

diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index de564d8b3c..d7a01bb814 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -756,9 +756,6 @@ impl FieldSet {
     }
 
     /// Returns a new `ValueSet` with entries for this `FieldSet`'s values.
-    ///
-    /// Note that a `ValueSet` may not be constructed with arrays of over 32
-    /// elements.
     #[doc(hidden)]
     pub fn value_set<'v, V>(&'v self, values: &'v V) -> ValueSet<'v>
     where
@@ -957,28 +954,10 @@ impl<'a> fmt::Display for ValueSet<'a> {
 mod private {
     use super::*;
 
-    /// Marker trait implemented by arrays which are of valid length to
-    /// construct a `ValueSet`.
-    ///
-    /// `ValueSet`s may only be constructed from arrays containing 32 or fewer
-    /// elements, to ensure the array is small enough to always be allocated on the
-    /// stack. This trait is only implemented by arrays of an appropriate length,
-    /// ensuring that the correct size arrays are used at compile-time.
+    /// Restrictions on `ValueSet` lengths were removed in #2508 but this type remains for backwards compatibility.
     pub trait ValidLen<'a>: Borrow<[(&'a Field, Option<&'a (dyn Value + 'a)>)]> {}
-}
-
-macro_rules! impl_valid_len {
-    ( $( $len:tt ),+ ) => {
-        $(
-            impl<'a> private::ValidLen<'a> for
-                [(&'a Field, ::core::option::Option<&'a (dyn Value + 'a)>); $len] {}
-        )+
-    }
-}
 
-impl_valid_len! {
-    0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
-    21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
+    impl<'a, const N: usize> ValidLen<'a> for [(&'a Field, Option<&'a (dyn Value + 'a)>); N] {}
 }
 
 #[cfg(test)]
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index de54bffbd2..93cf6b2e37 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -386,22 +386,6 @@
 //! span.record("parting", &"goodbye world!");
 //! ```
 //!
-//! Note that a span may have up to 32 fields. The following will not compile:
-//!
-//! ```rust,compile_fail
-//! # use tracing::Level;
-//! # fn main() {
-//! let bad_span = span!(
-//!     Level::TRACE,
-//!     "too many fields!",
-//!     a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7, h = 8, i = 9,
-//!     j = 10, k = 11, l = 12, m = 13, n = 14, o = 15, p = 16, q = 17,
-//!     r = 18, s = 19, t = 20, u = 21, v = 22, w = 23, x = 24, y = 25,
-//!     z = 26, aa = 27, bb = 28, cc = 29, dd = 30, ee = 31, ff = 32, gg = 33
-//! );
-//! # }
-//! ```
-//!
 //! Finally, events may also include human-readable messages, in the form of a
 //! [format string][fmt] and (optional) arguments, **after** the event's
 //! key-value fields. If a format string and arguments are provided,
diff --git a/tracing/src/span.rs b/tracing/src/span.rs
index 98d6ee12ef..ed58997b5a 100644
--- a/tracing/src/span.rs
+++ b/tracing/src/span.rs
@@ -22,7 +22,7 @@
 //!   override their default values.
 //! - The span's [verbosity level]
 //! - A string literal providing the span's name.
-//! - Finally, between zero and 32 arbitrary key/value fields.
+//! - Finally, zero or more arbitrary key/value fields.
 //!
 //! [`target`]: super::Metadata::target()
 //!
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index 6d9a333b68..6844315305 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -308,6 +308,48 @@ fn span_with_non_rust_symbol() {
     span!(Level::TRACE, "non-rust", "guid:x-request-id" = "abcdef");
 }
 
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn large_span() {
+    span!(
+        Level::TRACE,
+        "spans with more than 32 fields have been supported since #2508",
+        a = 1,
+        b = 2,
+        c = 3,
+        d = 4,
+        e = 5,
+        f = 6,
+        g = 7,
+        h = 8,
+        i = 9,
+        j = 10,
+        k = 11,
+        l = 12,
+        m = 13,
+        n = 14,
+        o = 15,
+        p = 16,
+        q = 17,
+        r = 18,
+        s = 19,
+        t = 20,
+        u = 21,
+        v = 22,
+        w = 23,
+        x = 24,
+        y = 25,
+        z = 26,
+        aa = 27,
+        bb = 28,
+        cc = 29,
+        dd = 30,
+        ee = 31,
+        ff = 32,
+        gg = 33
+    );
+}
+
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
 #[test]
 fn event() {

From c5736519c31cca95f8c195fd0c4c6312b910ba9a Mon Sep 17 00:00:00 2001
From: Aaron Roney 
Date: Tue, 5 Sep 2023 13:09:00 -0700
Subject: [PATCH 043/101] tracing: allow setting event names in macros (#2699)

## Motivation

The motivation is #1426.  Currently, event names are set to a default
value of `event file:line`, which (1) doesn't allow for customization,
and (2) prevents events from working for some opentelemetry endpoints
(they often use the event name `exception` to designate something like a
panic).

## Solution

Went through the event macros, and added new parameterization that
allows for setting the `name`.  In addition, added some comments, and
reordering, to make life a bit better for the next person that comes
along to edit those macros.  Finally, added tests for the macro
expansion alongside the existing tests with a reasonable amount of
coverage (though, admittedly, more could be added for all of the macro
invocation types

Fixes #1426
---
 tracing/src/lib.rs      |  13 +-
 tracing/src/macros.rs   | 767 +++++++++++++++++++++++++++++++++++-----
 tracing/tests/macros.rs | 143 ++++++++
 3 files changed, 842 insertions(+), 81 deletions(-)

diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 93cf6b2e37..c1c6d2fa98 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -214,7 +214,7 @@
 //! ### Configuring Attributes
 //!
 //! Both macros require a [`Level`] specifying the verbosity of the span or
-//! event. Optionally, the [target] and [parent span] may be overridden. If the
+//! event. Optionally, the, [target] and [parent span] may be overridden. If the
 //! target and parent span are not overridden, they will default to the
 //! module path where the macro was invoked and the current span (as determined
 //! by the collector), respectively.
@@ -237,7 +237,16 @@
 //! ```
 //!
 //! The span macros also take a string literal after the level, to set the name
-//! of the span.
+//! of the span (as above).  In the case of the event macros, the name of the event can
+//! be overridden (the default is `event file:line`) using the `name:` specifier.
+//!
+//! ```
+//! # use tracing::{span, event, Level};
+//! # fn main() {
+//! span!(Level::TRACE, "my span");
+//! event!(name: "some_info", Level::INFO, "something has happened!");
+//! # }
+//! ```
 //!
 //! ### Recording Fields
 //!
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index f47f9bbb2b..082be6dd46 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -565,6 +565,7 @@ macro_rules! error_span {
 ///     "App warning: {}",
 ///     error
 /// );
+/// event!(name: "answer", Level::INFO, the_answer = data.0);
 /// event!(Level::INFO, the_answer = data.0);
 /// # }
 /// ```
@@ -583,6 +584,112 @@ macro_rules! error_span {
 // /// ```
 #[macro_export]
 macro_rules! event {
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+        use $crate::__macro_support::Callsite as _;
+        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::EVENT,
+            target: $target,
+            level: $lvl,
+            fields: $($fields)*
+        };
+
+        let enabled = $crate::level_enabled!($lvl) && {
+            let interest = CALLSITE.interest();
+            !interest.is_never() && CALLSITE.is_enabled(interest)
+        };
+        if enabled {
+            (|value_set: $crate::field::ValueSet| {
+                $crate::__tracing_log!(
+                    $lvl,
+                    CALLSITE,
+                    &value_set
+                );
+                let meta = CALLSITE.metadata();
+                // event with explicit parent
+                $crate::Event::child_of(
+                    $parent,
+                    meta,
+                    &value_set
+                );
+            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+        } else {
+            $crate::__tracing_log!(
+                $lvl,
+                CALLSITE,
+                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+            );
+        }
+    });
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+        $crate::event!(
+            name: $name,
+            target: $target,
+            parent: $parent,
+            $lvl,
+            { message = format_args!($($arg)+), $($fields)* }
+        )
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $lvl, { $($arg)+ })
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+        use $crate::__macro_support::Callsite as _;
+        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::EVENT,
+            target: $target,
+            level: $lvl,
+            fields: $($fields)*
+        };
+        let enabled = $crate::level_enabled!($lvl) && {
+            let interest = CALLSITE.interest();
+            !interest.is_never() && CALLSITE.is_enabled(interest)
+        };
+        if enabled {
+            (|value_set: $crate::field::ValueSet| {
+                let meta = CALLSITE.metadata();
+                // event with contextual parent
+                $crate::Event::dispatch(
+                    meta,
+                    &value_set
+                );
+                $crate::__tracing_log!(
+                    $lvl,
+                    CALLSITE,
+                    &value_set
+                );
+            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+        } else {
+            $crate::__tracing_log!(
+                $lvl,
+                CALLSITE,
+                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+            );
+        }
+    });
+    (name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+        $crate::event!(
+            name: $name,
+            target: $target,
+            $lvl,
+            { message = format_args!($($arg)+), $($fields)* }
+        )
+    );
+    (name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $lvl, { $($k).+ = $($fields)* })
+    );
+    (name: $name:expr, target: $target:expr, $lvl:expr, $($arg:tt)+) => (
+        $crate::event!(name: $name, target: $target, $lvl, { $($arg)+ })
+    );
+
+    // Target / parent.
     (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
         static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
@@ -625,7 +732,6 @@ macro_rules! event {
             );
         }
     });
-
     (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
         $crate::event!(
             target: $target,
@@ -640,6 +746,111 @@ macro_rules! event {
     (target: $target:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
         $crate::event!(target: $target, parent: $parent, $lvl, { $($arg)+ })
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+        use $crate::__macro_support::Callsite as _;
+        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::EVENT,
+            target: module_path!(),
+            level: $lvl,
+            fields: $($fields)*
+        };
+
+        let enabled = $crate::level_enabled!($lvl) && {
+            let interest = CALLSITE.interest();
+            !interest.is_never() && CALLSITE.is_enabled(interest)
+        };
+        if enabled {
+            (|value_set: $crate::field::ValueSet| {
+                $crate::__tracing_log!(
+                    $lvl,
+                    CALLSITE,
+                    &value_set
+                );
+                let meta = CALLSITE.metadata();
+                // event with explicit parent
+                $crate::Event::child_of(
+                    $parent,
+                    meta,
+                    &value_set
+                );
+            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+        } else {
+            $crate::__tracing_log!(
+                $lvl,
+                CALLSITE,
+                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+            );
+        }
+    });
+    (name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+        $crate::event!(
+            name: $name,
+            parent: $parent,
+            $lvl,
+            { message = format_args!($($arg)+), $($fields)* }
+        )
+    );
+    (name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $lvl, { $($k).+ = $($fields)* })
+    );
+    (name: $name:expr, parent: $parent:expr, $lvl:expr, $($arg:tt)+) => (
+        $crate::event!(name: $name, parent: $parent, $lvl, { $($arg)+ })
+    );
+
+    // Name.
+    (name: $name:expr, $lvl:expr, { $($fields:tt)* } )=> ({
+        use $crate::__macro_support::Callsite as _;
+        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            name: $name,
+            kind: $crate::metadata::Kind::EVENT,
+            target: module_path!(),
+            level: $lvl,
+            fields: $($fields)*
+        };
+        let enabled = $crate::level_enabled!($lvl) && {
+            let interest = CALLSITE.interest();
+            !interest.is_never() && CALLSITE.is_enabled(interest)
+        };
+        if enabled {
+            (|value_set: $crate::field::ValueSet| {
+                let meta = CALLSITE.metadata();
+                // event with contextual parent
+                $crate::Event::dispatch(
+                    meta,
+                    &value_set
+                );
+                $crate::__tracing_log!(
+                    $lvl,
+                    CALLSITE,
+                    &value_set
+                );
+            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+        } else {
+            $crate::__tracing_log!(
+                $lvl,
+                CALLSITE,
+                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+            );
+        }
+    });
+    (name: $name:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
+        $crate::event!(
+            name: $name,
+            $lvl,
+            { message = format_args!($($arg)+), $($fields)* }
+        )
+    );
+    (name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
+        $crate::event!(name: $name, $lvl, { $($k).+ = $($fields)* })
+    );
+    (name: $name:expr, $lvl:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $lvl, { $($arg)+ })
+    );
+
+    // Target.
     (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
         static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
@@ -693,6 +904,8 @@ macro_rules! event {
     (target: $target:expr, $lvl:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, $lvl, { $($arg)+ })
     );
+
+    // Parent.
     (parent: $parent:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -752,6 +965,8 @@ macro_rules! event {
     (parent: $parent:expr, $lvl:expr, $($arg:tt)+ ) => (
         $crate::event!(target: module_path!(), parent: $parent, $lvl, { $($arg)+ })
     );
+
+    // ...
     ( $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1079,10 +1294,46 @@ macro_rules! enabled {
 ///     if pos.x >= 0.0 { "positive" } else { "negative" },
 ///     if pos.y >= 0.0 { "positive" } else { "negative" }
 /// );
+/// trace!(name: "completed", position = ?pos);
 /// # }
 /// ```
 #[macro_export]
 macro_rules! trace {
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
+    );
+
+    // Target / parent.
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
@@ -1098,6 +1349,59 @@ macro_rules! trace {
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
+    );
+
+    // Name.
+    (name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::TRACE, { $($k).+ $($field)* })
+    );
+    (name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+    );
+    (name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::TRACE, { %$($k).+ $($field)* })
+    );
+    (name: $name:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $crate::Level::TRACE, {}, $($arg)+)
+    );
+
+    // Target.
+    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
+    );
+    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
+    );
+    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
+    );
+    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
+    );
+    (target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+)
+    );
+
+    // Parent.
     (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1164,21 +1468,8 @@ macro_rules! trace {
             $($arg)+
         )
     );
-    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
-    );
-    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
-    );
-    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
-    );
-    (target: $target:expr, $($arg:tt)+ ) => (
-        $crate::event!(target: $target, $crate::Level::TRACE, {}, $($arg)+)
-    );
+
+    // ...
     ({ $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1194,6 +1485,20 @@ macro_rules! trace {
             { $($k).+ = $($field)*}
         )
     );
+    (?$($k:ident).+ = $($field:tt)*) => (
+        $crate::event!(
+            target: module_path!(),
+            $crate::Level::TRACE,
+            { ?$($k).+ = $($field)*}
+        )
+    );
+    (%$($k:ident).+ = $($field:tt)*) => (
+        $crate::event!(
+            target: module_path!(),
+            $crate::Level::TRACE,
+            { %$($k).+ = $($field)*}
+        )
+    );
     ($($k:ident).+, $($field:tt)*) => (
         $crate::event!(
             target: module_path!(),
@@ -1266,10 +1571,46 @@ macro_rules! trace {
 ///
 /// debug!(?pos.x, ?pos.y);
 /// debug!(target: "app_events", position = ?pos, "New position");
+/// debug!(name: "completed", position = ?pos);
 /// # }
 /// ```
 #[macro_export]
 macro_rules! debug {
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+    );
+
+    // Target / parent.
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
@@ -1285,6 +1626,59 @@ macro_rules! debug {
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
+    );
+
+    // Name.
+    (name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::DEBUG, { $($k).+ $($field)* })
+    );
+    (name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+    );
+    (name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+    );
+    (name: $name:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $crate::Level::DEBUG, {}, $($arg)+)
+    );
+
+    // Target.
+    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
+    );
+    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
+    );
+    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
+    );
+    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
+    );
+    (target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+)
+    );
+
+    // Parent.
     (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1351,21 +1745,8 @@ macro_rules! debug {
             $($arg)+
         )
     );
-    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
-    );
-    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
-    );
-    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
-    );
-    (target: $target:expr, $($arg:tt)+ ) => (
-        $crate::event!(target: $target, $crate::Level::DEBUG, {}, $($arg)+)
-    );
+
+    // ...
     ({ $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1478,11 +1859,47 @@ macro_rules! debug {
 ///     conn.port,
 ///     ?conn.speed,
 /// );
+/// info!(name: "completed", "completed connection to {:?}", addr);
 /// # }
 /// ```
 #[macro_export]
 macro_rules! info {
-     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
+    );
+
+    // Target / parent.
+    (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
     (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
@@ -1497,6 +1914,59 @@ macro_rules! info {
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
+    );
+
+    // Name.
+    (name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::INFO, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::INFO, { $($k).+ $($field)* })
+    );
+    (name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::INFO, { ?$($k).+ $($field)* })
+    );
+    (name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::INFO, { %$($k).+ $($field)* })
+    );
+    (name: $name:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $crate::Level::INFO, {}, $($arg)+)
+    );
+
+    // Target.
+    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
+    );
+    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
+    );
+    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
+    );
+    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
+    );
+    (target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+)
+    );
+
+    // Parent.
     (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1563,21 +2033,8 @@ macro_rules! info {
             $($arg)+
         )
     );
-    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
-    );
-    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
-    );
-    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, $($arg:tt)+ ) => (
-        $crate::event!(target: $target, $crate::Level::INFO, {}, $($arg)+)
-    );
+
+    // ...
     ({ $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1683,11 +2140,47 @@ macro_rules! info {
 ///     warning = warn_description,
 ///     "Received warning for input: {:?}", input,
 /// );
+/// warn!(name: "invalid", ?input);
 /// # }
 /// ```
 #[macro_export]
 macro_rules! warn {
-     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
+    );
+
+    // Target / parent.
+    (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
     (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
@@ -1702,6 +2195,59 @@ macro_rules! warn {
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
+    );
+
+    // Name.
+    (name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::WARN, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::WARN, { $($k).+ $($field)* })
+    );
+    (name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::WARN, { ?$($k).+ $($field)* })
+    );
+    (name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::WARN, { %$($k).+ $($field)* })
+    );
+    (name: $name:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $crate::Level::WARN, {}, $($arg)+)
+    );
+
+    // Target.
+    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
+    );
+    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
+    );
+    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
+    );
+    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
+    );
+    (target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+)
+    );
+
+    // Parent.
     (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1768,21 +2314,8 @@ macro_rules! warn {
             $($arg)+
         )
     );
-    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
-    );
-    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
-    );
-    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
-    );
-    (target: $target:expr, $($arg:tt)+ ) => (
-        $crate::event!(target: $target, $crate::Level::WARN, {}, $($arg)+)
-    );
+
+    // ...
     ({ $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1884,11 +2417,47 @@ macro_rules! warn {
 /// error!(port, error = %err_info);
 /// error!(target: "app_events", "App Error: {}", err_info);
 /// error!({ info = err_info }, "error on port: {}", port);
+/// error!(name: "invalid_input", "Invalid input: {}", err_info);
 /// # }
 /// ```
 #[macro_export]
 macro_rules! error {
-     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+    // Name / target / parent.
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+    );
+
+    // Name / target.
+    (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
+    );
+
+    // Target / parent.
+    (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
     (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
@@ -1903,6 +2472,59 @@ macro_rules! error {
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
     );
+
+    // Name / parent.
+    (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    );
+    (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
+    );
+
+    // Name.
+    (name: $name:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+    );
+    (name: $name:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::ERROR, { $($k).+ $($field)* })
+    );
+    (name: $name:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+    );
+    (name: $name:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, $crate::Level::ERROR, { %$($k).+ $($field)* })
+    );
+    (name: $name:expr, $($arg:tt)+ ) => (
+        $crate::event!(name: $name, $crate::Level::ERROR, {}, $($arg)+)
+    );
+
+    // Target.
+    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
+    );
+    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
+    );
+    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
+    );
+    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
+    );
+    (target: $target:expr, $($arg:tt)+ ) => (
+        $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+)
+    );
+
+    // Parent.
     (parent: $parent:expr, { $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
@@ -1969,21 +2591,8 @@ macro_rules! error {
             $($arg)+
         )
     );
-    (target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
-    );
-    (target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
-    );
-    (target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
-    );
-    (target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
-        $crate::event!(target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
-    );
-    (target: $target:expr, $($arg:tt)+ ) => (
-        $crate::event!(target: $target, $crate::Level::ERROR, {}, $($arg)+)
-    );
+
+    // ...
     ({ $($field:tt)+ }, $($arg:tt)+ ) => (
         $crate::event!(
             target: module_path!(),
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index 6844315305..5c993ccffa 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -451,6 +451,13 @@ fn locals_no_message() {
     let data = (42, "forty-two");
     let private_data = "private";
     let error = "a bad error";
+    event!(
+        name: "foo",
+        target: "app_events",
+        Level::WARN,
+        private_data,
+        ?data,
+    );
     event!(
         target: "app_events",
         Level::WARN,
@@ -490,6 +497,8 @@ fn trace() {
     trace!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     trace!({ foo = 2, bar.baz = 78 }, "quux");
     trace!({ foo = ?2, bar.baz = %78 }, "quux");
+    trace!(name: "foo", foo = 3, bar.baz = 2, quux = false);
+    trace!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     trace!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     trace!(target: "foo_events", foo = 3, bar.baz = 3,);
     trace!(target: "foo_events", "foo");
@@ -502,6 +511,12 @@ fn trace() {
     trace!(?foo);
     trace!(%foo);
     trace!(foo);
+    trace!(name: "foo", ?foo);
+    trace!(name: "foo", %foo);
+    trace!(name: "foo", foo);
+    trace!(name: "foo", ?foo, true, "message");
+    trace!(name: "foo", %foo, true, "message");
+    trace!(name: "foo", foo, true, "message");
     trace!(target: "foo_events", ?foo);
     trace!(target: "foo_events", %foo);
     trace!(target: "foo_events", foo);
@@ -526,6 +541,8 @@ fn debug() {
     debug!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     debug!({ foo = 2, bar.baz = 78 }, "quux");
     debug!({ foo = ?2, bar.baz = %78 }, "quux");
+    debug!(name: "foo", foo = 3, bar.baz = 2, quux = false);
+    debug!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     debug!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     debug!(target: "foo_events", foo = 3, bar.baz = 3,);
     debug!(target: "foo_events", "foo");
@@ -538,6 +555,12 @@ fn debug() {
     debug!(?foo);
     debug!(%foo);
     debug!(foo);
+    debug!(name: "foo", ?foo);
+    debug!(name: "foo", %foo);
+    debug!(name: "foo", foo);
+    debug!(name: "foo", ?foo, true, "message");
+    debug!(name: "foo", %foo, true, "message");
+    debug!(name: "foo", foo, true, "message");
     debug!(target: "foo_events", ?foo);
     debug!(target: "foo_events", %foo);
     debug!(target: "foo_events", foo);
@@ -562,6 +585,8 @@ fn info() {
     info!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     info!({ foo = 2, bar.baz = 78 }, "quux");
     info!({ foo = ?2, bar.baz = %78 }, "quux");
+    info!(name: "foo", foo = 3, bar.baz = 2, quux = false);
+    info!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     info!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     info!(target: "foo_events", foo = 3, bar.baz = 3,);
     info!(target: "foo_events", "foo");
@@ -574,6 +599,12 @@ fn info() {
     info!(?foo);
     info!(%foo);
     info!(foo);
+    info!(name: "foo", ?foo);
+    info!(name: "foo", %foo);
+    info!(name: "foo", foo);
+    info!(name: "foo", ?foo, true, "message");
+    info!(name: "foo", %foo, true, "message");
+    info!(name: "foo", foo, true, "message");
     info!(target: "foo_events", ?foo);
     info!(target: "foo_events", %foo);
     info!(target: "foo_events", foo);
@@ -598,6 +629,8 @@ fn warn() {
     warn!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     warn!({ foo = 2, bar.baz = 78 }, "quux");
     warn!({ foo = ?2, bar.baz = %78 }, "quux");
+    warn!(name: "foo", foo = 3, bar.baz = 2, quux = false);
+    warn!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     warn!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     warn!(target: "foo_events", foo = 3, bar.baz = 3,);
     warn!(target: "foo_events", "foo");
@@ -610,6 +643,12 @@ fn warn() {
     warn!(?foo);
     warn!(%foo);
     warn!(foo);
+    warn!(name: "foo", ?foo);
+    warn!(name: "foo", %foo);
+    warn!(name: "foo", foo);
+    warn!(name: "foo", ?foo, true, "message");
+    warn!(name: "foo", %foo, true, "message");
+    warn!(name: "foo", foo, true, "message");
     warn!(target: "foo_events", ?foo);
     warn!(target: "foo_events", %foo);
     warn!(target: "foo_events", foo);
@@ -634,6 +673,8 @@ fn error() {
     error!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     error!({ foo = 2, bar.baz = 78, }, "quux");
     error!({ foo = ?2, bar.baz = %78 }, "quux");
+    error!(name: "foo", foo = 3, bar.baz = 2, quux = false);
+    error!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     error!(target: "foo_events", foo = 3, bar.baz = 2, quux = false);
     error!(target: "foo_events", foo = 3, bar.baz = 3,);
     error!(target: "foo_events", "foo");
@@ -646,6 +687,12 @@ fn error() {
     error!(?foo);
     error!(%foo);
     error!(foo);
+    error!(name: "foo", ?foo);
+    error!(name: "foo", %foo);
+    error!(name: "foo", foo);
+    error!(name: "foo", ?foo, true, "message");
+    error!(name: "foo", %foo, true, "message");
+    error!(name: "foo", foo, true, "message");
     error!(target: "foo_events", ?foo);
     error!(target: "foo_events", %foo);
     error!(target: "foo_events", foo);
@@ -680,6 +727,14 @@ fn event_root() {
     event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     event!(target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux");
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 2, quux = false);
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 3,);
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, "foo");
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, "foo: {}", 3);
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux");
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    event!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -703,6 +758,14 @@ fn trace_root() {
     trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     trace!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo");
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3);
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux");
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    trace!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -726,6 +789,14 @@ fn debug_root() {
     debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     debug!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo");
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3);
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux");
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    debug!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -749,6 +820,14 @@ fn info_root() {
     info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     info!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo");
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3);
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux");
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    info!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -772,6 +851,14 @@ fn warn_root() {
     warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     warn!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo");
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3);
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux");
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    warn!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -795,6 +882,14 @@ fn error_root() {
     error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     error!(target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo");
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, "foo: {}", 3);
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 3, bar.baz = 80 }, "quux");
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    error!(name: "foo", target: "foo_events", parent: ::core::option::Option::None, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -818,6 +913,14 @@ fn event_with_parent() {
     event!(target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     event!(target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     event!(target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux");
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, foo = 3, bar.baz = 2, quux = false);
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, foo = 3, bar.baz = 3,);
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, "foo");
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, "foo: {}", 3);
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux");
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    event!(name: "foo", target: "foo_events", parent: &p, Level::DEBUG, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -842,6 +945,14 @@ fn trace_with_parent() {
     trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     trace!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
+    trace!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
+    trace!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
+    trace!(name: "foo", target: "foo_events", parent: &p, "foo");
+    trace!(name: "foo", target: "foo_events", parent: &p, "foo: {}", 3);
+    trace!(name: "foo", target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
+    trace!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    trace!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    trace!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -866,6 +977,14 @@ fn debug_with_parent() {
     debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     debug!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
+    debug!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
+    debug!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
+    debug!(name: "foo", target: "foo_events", parent: &p, "foo");
+    debug!(name: "foo", target: "foo_events", parent: &p, "foo: {}", 3);
+    debug!(name: "foo", target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
+    debug!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    debug!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    debug!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -890,6 +1009,14 @@ fn info_with_parent() {
     info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     info!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
+    info!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
+    info!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
+    info!(name: "foo", target: "foo_events", parent: &p, "foo");
+    info!(name: "foo", target: "foo_events", parent: &p, "foo: {}", 3);
+    info!(name: "foo", target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
+    info!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    info!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    info!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -914,6 +1041,14 @@ fn warn_with_parent() {
     warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     warn!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
+    warn!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
+    warn!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
+    warn!(name: "foo", target: "foo_events", parent: &p, "foo");
+    warn!(name: "foo", target: "foo_events", parent: &p, "foo: {}", 3);
+    warn!(name: "foo", target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
+    warn!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    warn!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    warn!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -938,6 +1073,14 @@ fn error_with_parent() {
     error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     error!(target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
+    error!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 2, quux = false);
+    error!(name: "foo", target: "foo_events", parent: &p, foo = 3, bar.baz = 3,);
+    error!(name: "foo", target: "foo_events", parent: &p, "foo");
+    error!(name: "foo", target: "foo_events", parent: &p, "foo: {}", 3);
+    error!(name: "foo", target: "foo_events", parent: &p, { foo = 3, bar.baz = 80 }, "quux");
+    error!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
+    error!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
+    error!(name: "foo", target: "foo_events", parent: &p, { foo = 2, bar.baz = 78, }, "quux");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]

From 672dfdf694eed7f2284a8c7537083e085c18706b Mon Sep 17 00:00:00 2001
From: Finomnis 
Date: Tue, 5 Sep 2023 22:13:43 +0200
Subject: [PATCH 044/101] journald: allow custom journal fields (#2708)

## Motivation

It's currently not possible to customize how messages will get send to journald.

This became apparent in #2425, where first a specific API got designed, but then
it was decided that users should not get restricted in only a subset of fields,
but should be able to simply choose by themselves what fields get set with what
values.

So in a sense, this is the successor/rework of #2425.

## Solution

Allow custom fields to be set in tracing-journald.

## Open Questions

- [x] How should we deal with fields that also get supplied by other options?
  For example, setting `SYSLOG_IDENTIFIER` here and also setting
  `.with_syslog_identifier()` will send said field twice, potentially with
  differing values. Is that a problem?
    - Answer: No, this is not a problem.

Closes #2425
---
 tracing-journald/src/lib.rs       | 37 +++++++++++++++++++++++++++++++
 tracing-journald/tests/journal.rs | 22 ++++++++++++++++++
 2 files changed, 59 insertions(+)

diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs
index 237a0de647..4ad5a58d5b 100644
--- a/tracing-journald/src/lib.rs
+++ b/tracing-journald/src/lib.rs
@@ -85,6 +85,7 @@ pub struct Subscriber {
     socket: UnixDatagram,
     field_prefix: Option,
     syslog_identifier: String,
+    additional_fields: Vec,
 }
 
 #[cfg(unix)]
@@ -109,6 +110,7 @@ impl Subscriber {
                     .map(|n| n.to_string_lossy().into_owned())
                     // If we fail to get the name of the current executable fall back to an empty string.
                     .unwrap_or_else(String::new),
+                additional_fields: Vec::new(),
             };
             // Check that we can talk to journald, by sending empty payload which journald discards.
             // However if the socket didn't exist or if none listened we'd get an error here.
@@ -150,6 +152,40 @@ impl Subscriber {
         self
     }
 
+    /// Adds fields that will get be passed to journald with every log entry.
+    ///
+    /// The input values of this function are interpreted as `(field, value)` pairs.
+    ///
+    /// This can for example be used to configure the syslog facility.
+    /// See [Journal Fields](https://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html)
+    /// and [journalctl](https://www.freedesktop.org/software/systemd/man/journalctl.html)
+    /// for more information.
+    ///
+    /// Fields specified using this method will be added to the journald
+    /// message alongside fields generated from the event's fields, its
+    /// metadata, and the span context. If the name of a field provided using
+    /// this method is the same as the name of a field generated by the
+    /// subscriber, both fields will be sent to journald.
+    ///
+    /// ```no_run
+    /// # use tracing_journald::Subscriber;
+    /// let sub = Subscriber::new()
+    ///     .unwrap()
+    ///     .with_custom_fields([("SYSLOG_FACILITY", "17")]);
+    /// ```
+    ///
+    pub fn with_custom_fields, U: AsRef<[u8]>>(
+        mut self,
+        fields: impl IntoIterator,
+    ) -> Self {
+        for (name, value) in fields {
+            put_field_length_encoded(&mut self.additional_fields, name.as_ref(), |buf| {
+                buf.extend_from_slice(value.as_ref())
+            })
+        }
+        self
+    }
+
     /// Returns the syslog identifier in use.
     pub fn syslog_identifier(&self) -> &str {
         &self.syslog_identifier
@@ -257,6 +293,7 @@ where
         put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| {
             write!(buf, "{}", self.syslog_identifier).unwrap()
         });
+        buf.extend_from_slice(&self.additional_fields);
 
         event.record(&mut EventVisitor::new(
             &mut buf,
diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs
index 5eab5f79cd..24a7fc7611 100644
--- a/tracing-journald/tests/journal.rs
+++ b/tracing-journald/tests/journal.rs
@@ -237,6 +237,28 @@ fn simple_metadata() {
     });
 }
 
+#[test]
+fn journal_fields() {
+    let sub = Subscriber::new()
+        .unwrap()
+        .with_field_prefix(None)
+        .with_custom_fields([("SYSLOG_FACILITY", "17")])
+        .with_custom_fields([("ABC", "dEf"), ("XYZ", "123")]);
+    with_journald_subscriber(sub, || {
+        info!(test.name = "journal_fields", "Hello World");
+
+        let message = retry_read_one_line_from_journal("journal_fields");
+        assert_eq!(message["MESSAGE"], "Hello World");
+        assert_eq!(message["PRIORITY"], "5");
+        assert_eq!(message["TARGET"], "journal");
+        assert_eq!(message["SYSLOG_FACILITY"], "17");
+        assert_eq!(message["ABC"], "dEf");
+        assert_eq!(message["XYZ"], "123");
+        assert!(message["CODE_FILE"].as_text().is_some());
+        assert!(message["CODE_LINE"].as_text().is_some());
+    });
+}
+
 #[test]
 fn span_metadata() {
     with_journald(|| {

From 245d6075533651b2ebfaed7f06fc80e6ea76fc53 Mon Sep 17 00:00:00 2001
From: Joseph Perez 
Date: Wed, 6 Sep 2023 00:33:44 +0200
Subject: [PATCH 045/101] tracing: allow constant field names in macros (#2617)

## Motivation

I've found myself in the case where I wanted to have customized event field name
for different trait implementations. In fact, these implementations are
completely unrelated (in separate applications), so, in this use case, I find
more readable to have `foo="some_id"` and `bar=16` instead of `resource="foo"
value="some_id"` and `resource=bar value=16`

Because events only accept identifier or literal as field name, this is quite
cumbersome/impossible to do. A simple solution could be to make events accept
constant expression too; in my use case, I could then add a associated constant
to my trait.


## Solution

This PR proposes a new syntax for using constant field names:
```rust
tracing::debug!({ CONSTANT_EXPR } = "foo");
```
This is the same syntax than constant expression, so it should be quite intuitive.

To avoid constant expression names conflict, internal variables of macro
expansion have been prefixed with `__`, e.g. `__CALLSITE`.

Co-authored-by: Joseph Perez 
Co-authored-by: Eliza Weisman 
---
 tracing/src/lib.rs     |  13 +++
 tracing/src/macros.rs  | 202 ++++++++++++++++++++++++++---------------
 tracing/tests/event.rs |  42 +++++++++
 tracing/tests/span.rs  |  30 ++++++
 4 files changed, 212 insertions(+), 75 deletions(-)

diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index c1c6d2fa98..58baa95659 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -319,6 +319,19 @@
 //! # }
 //!```
 //!
+//! Constant expressions can also be used as field names. Constants
+//! must be enclosed in curly braces (`{}`) to indicate that the *value*
+//! of the constant is to be used as the field name, rather than the
+//! constant's name. For example:
+//! ```
+//! # use tracing::{span, Level};
+//! # fn main() {
+//! const RESOURCE_NAME: &str = "foo";
+//! // this span will have the field `foo = "some_id"`
+//! span!(Level::TRACE, "get", { RESOURCE_NAME } = "some_id");
+//! # }
+//!```
+//!
 //! The `?` sigil is shorthand that specifies a field should be recorded using
 //! its [`fmt::Debug`] implementation:
 //! ```
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 082be6dd46..31890b69ee 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -24,7 +24,7 @@ macro_rules! span {
     (target: $target:expr, parent: $parent:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
         {
             use $crate::__macro_support::Callsite as _;
-            static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
                 name: $name,
                 kind: $crate::metadata::Kind::SPAN,
                 target: $target,
@@ -33,10 +33,10 @@ macro_rules! span {
             };
             let mut interest = $crate::collect::Interest::never();
             if $crate::level_enabled!($lvl)
-                && { interest = CALLSITE.interest(); !interest.is_never() }
-                && CALLSITE.is_enabled(interest)
+                && { interest = __CALLSITE.interest(); !interest.is_never() }
+                && __CALLSITE.is_enabled(interest)
             {
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // span with explicit parent
                 $crate::Span::child_of(
                     $parent,
@@ -44,9 +44,9 @@ macro_rules! span {
                     &$crate::valueset!(meta.fields(), $($fields)*),
                 )
             } else {
-                let span = CALLSITE.disabled_span();
+                let span = __CALLSITE.disabled_span();
                 $crate::if_log_enabled! { $lvl, {
-                    span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+                    span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
                 }};
                 span
             }
@@ -55,7 +55,7 @@ macro_rules! span {
     (target: $target:expr, $lvl:expr, $name:expr, $($fields:tt)*) => {
         {
             use $crate::__macro_support::{Callsite as _, Registration};
-            static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
                 name: $name,
                 kind: $crate::metadata::Kind::SPAN,
                 target: $target,
@@ -65,19 +65,19 @@ macro_rules! span {
 
             let mut interest = $crate::collect::Interest::never();
             if $crate::level_enabled!($lvl)
-                && { interest = CALLSITE.interest(); !interest.is_never() }
-                && CALLSITE.is_enabled(interest)
+                && { interest = __CALLSITE.interest(); !interest.is_never() }
+                && __CALLSITE.is_enabled(interest)
             {
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // span with contextual parent
                 $crate::Span::new(
                     meta,
                     &$crate::valueset!(meta.fields(), $($fields)*),
                 )
             } else {
-                let span = CALLSITE.disabled_span();
+                let span = __CALLSITE.disabled_span();
                 $crate::if_log_enabled! { $lvl, {
-                    span.record_all(&$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+                    span.record_all(&$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
                 }};
                 span
             }
@@ -587,7 +587,7 @@ macro_rules! event {
     // Name / target / parent.
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: $name,
             kind: $crate::metadata::Kind::EVENT,
             target: $target,
@@ -596,29 +596,29 @@ macro_rules! event {
         };
 
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with explicit parent
                 $crate::Event::child_of(
                     $parent,
                     meta,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -641,7 +641,7 @@ macro_rules! event {
     // Name / target.
     (name: $name:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: $name,
             kind: $crate::metadata::Kind::EVENT,
             target: $target,
@@ -649,12 +649,12 @@ macro_rules! event {
             fields: $($fields)*
         };
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with contextual parent
                 $crate::Event::dispatch(
                     meta,
@@ -662,15 +662,15 @@ macro_rules! event {
                 );
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -692,7 +692,7 @@ macro_rules! event {
     // Target / parent.
     (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: concat!(
                 "event ",
                 file!(),
@@ -706,29 +706,29 @@ macro_rules! event {
         };
 
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with explicit parent
                 $crate::Event::child_of(
                     $parent,
                     meta,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -750,7 +750,7 @@ macro_rules! event {
     // Name / parent.
     (name: $name:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: $name,
             kind: $crate::metadata::Kind::EVENT,
             target: module_path!(),
@@ -759,29 +759,29 @@ macro_rules! event {
         };
 
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with explicit parent
                 $crate::Event::child_of(
                     $parent,
                     meta,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -803,7 +803,7 @@ macro_rules! event {
     // Name.
     (name: $name:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: $name,
             kind: $crate::metadata::Kind::EVENT,
             target: module_path!(),
@@ -811,12 +811,12 @@ macro_rules! event {
             fields: $($fields)*
         };
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with contextual parent
                 $crate::Event::dispatch(
                     meta,
@@ -824,15 +824,15 @@ macro_rules! event {
                 );
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -853,7 +853,7 @@ macro_rules! event {
     // Target.
     (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         use $crate::__macro_support::Callsite as _;
-        static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+        static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
             name: concat!(
                 "event ",
                 file!(),
@@ -866,12 +866,12 @@ macro_rules! event {
             fields: $($fields)*
         };
         let enabled = $crate::level_enabled!($lvl) && {
-            let interest = CALLSITE.interest();
-            !interest.is_never() && CALLSITE.is_enabled(interest)
+            let interest = __CALLSITE.interest();
+            !interest.is_never() && __CALLSITE.is_enabled(interest)
         };
         if enabled {
             (|value_set: $crate::field::ValueSet| {
-                let meta = CALLSITE.metadata();
+                let meta = __CALLSITE.metadata();
                 // event with contextual parent
                 $crate::Event::dispatch(
                     meta,
@@ -879,15 +879,15 @@ macro_rules! event {
                 );
                 $crate::__tracing_log!(
                     $lvl,
-                    CALLSITE,
+                    __CALLSITE,
                     &value_set
                 );
-            })($crate::valueset!(CALLSITE.metadata().fields(), $($fields)*));
+            })($crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*));
         } else {
             $crate::__tracing_log!(
                 $lvl,
-                CALLSITE,
-                &$crate::valueset!(CALLSITE.metadata().fields(), $($fields)*)
+                __CALLSITE,
+                &$crate::valueset!(__CALLSITE.metadata().fields(), $($fields)*)
             );
         }
     });
@@ -1183,7 +1183,7 @@ macro_rules! enabled {
     (kind: $kind:expr, target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({
         if $crate::level_enabled!($lvl) {
             use $crate::__macro_support::Callsite as _;
-            static CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
+            static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! {
                 name: concat!(
                     "enabled ",
                     file!(),
@@ -1195,9 +1195,9 @@ macro_rules! enabled {
                 level: $lvl,
                 fields: $($fields)*
             };
-            let interest = CALLSITE.interest();
-            if !interest.is_never() && CALLSITE.is_enabled(interest)  {
-                let meta = CALLSITE.metadata();
+            let interest = __CALLSITE.interest();
+            if !interest.is_never() && __CALLSITE.is_enabled(interest)  {
+                let meta = __CALLSITE.metadata();
                 $crate::dispatch::get_default(|current| current.enabled(meta))
             } else {
                 false
@@ -2709,20 +2709,20 @@ macro_rules! callsite {
         fields: $($fields:tt)*
     ) => {{
         use $crate::__macro_support::{MacroCallsite, Registration};
-        static META: $crate::Metadata<'static> = {
+        static __META: $crate::Metadata<'static> = {
             $crate::metadata! {
                 name: $name,
                 target: $target,
                 level: $lvl,
                 fields: $crate::fieldset!( $($fields)* ),
-                callsite: &CALLSITE,
+                callsite: &__CALLSITE,
                 kind: $kind,
             }
         };
-        static REG: Registration = Registration::new(&CALLSITE);
-        static CALLSITE: MacroCallsite = MacroCallsite::new(&META, ®);
-        CALLSITE.register();
-        &CALLSITE
+        static REG: Registration = Registration::new(&__CALLSITE);
+        static __CALLSITE: MacroCallsite = MacroCallsite::new(&__META, ®);
+        __CALLSITE.register();
+        &__CALLSITE
     }};
 }
 
@@ -2761,19 +2761,19 @@ macro_rules! callsite2 {
         fields: $($fields:tt)*
     ) => {{
         use $crate::__macro_support::{MacroCallsite, Registration};
-        static META: $crate::Metadata<'static> = {
+        static __META: $crate::Metadata<'static> = {
             $crate::metadata! {
                 name: $name,
                 target: $target,
                 level: $lvl,
                 fields: $crate::fieldset!( $($fields)* ),
-                callsite: &CALLSITE,
+                callsite: &__CALLSITE,
                 kind: $kind,
             }
         };
-        static REG: Registration = Registration::new(&CALLSITE);
+        static REG: Registration = Registration::new(&__CALLSITE);
 
-        MacroCallsite::new(&META, ®)
+        MacroCallsite::new(&__META, ®)
     }};
 }
 
@@ -2923,6 +2923,47 @@ macro_rules! valueset {
         )
     };
 
+    // Handle constant names
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+            $next,
+            $($rest)*
+        )
+    };
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr, $($rest:tt)*) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+            $next,
+            $($rest)*
+        )
+    };
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr, $($rest:tt)*) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+            $next,
+            $($rest)*
+        )
+    };
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = ?$val:expr) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&debug(&$val) as &dyn Value)) },
+            $next,
+        )
+    };
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = %$val:expr) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&display(&$val) as &dyn Value)) },
+            $next,
+        )
+    };
+    (@ { $(,)* $($out:expr),* }, $next:expr, { $k:expr } = $val:expr) => {
+        $crate::valueset!(
+            @ { $($out),*, (&$next, Some(&$val as &dyn Value)) },
+            $next,
+        )
+    };
+
     // Remainder is unparsable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
         $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
@@ -2992,6 +3033,17 @@ macro_rules! fieldset {
         $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
     };
 
+    // Handle constant names
+    (@ { $(,)* $($out:expr),* } { $k:expr } = ?$val:expr, $($rest:tt)*) => {
+        $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+    };
+    (@ { $(,)* $($out:expr),* } { $k:expr } = %$val:expr, $($rest:tt)*) => {
+        $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+    };
+    (@ { $(,)* $($out:expr),* } { $k:expr } = $val:expr, $($rest:tt)*) => {
+        $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
+    };
+
     // Remainder is unparseable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
         $crate::fieldset!(@ { "message", $($out),*, })
@@ -3045,7 +3097,7 @@ macro_rules! __tracing_log {
             if level <= log::max_level() {
                 let log_meta = log::Metadata::builder()
                     .level(level)
-                    .target(CALLSITE.metadata().target())
+                    .target(__CALLSITE.metadata().target())
                     .build();
                 let logger = log::logger();
                 if logger.enabled(&log_meta) {
diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs
index 7e4f87c6d3..55594ea913 100644
--- a/tracing/tests/event.rs
+++ b/tracing/tests/event.rs
@@ -401,3 +401,45 @@ fn string_field() {
 
     handle.assert_finished();
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn constant_field_name() {
+    let expect_event = || {
+        expect::event().with_fields(
+            expect::field("foo")
+                .with_value(&"bar")
+                .and(expect::field("constant string").with_value(&"also works"))
+                .and(expect::field("foo.bar").with_value(&"baz"))
+                .and(expect::field("message").with_value(&debug(format_args!("quux"))))
+                .only(),
+        )
+    };
+    let (collector, handle) = collector::mock()
+        .event(expect_event())
+        .event(expect_event())
+        .only()
+        .run_with_handle();
+
+    with_default(collector, || {
+        const FOO: &str = "foo";
+        tracing::event!(
+            Level::INFO,
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "quux"
+        );
+        tracing::event!(
+            Level::INFO,
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "quux"
+        );
+    });
+
+    handle.assert_finished();
+}
diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs
index 14c4417cb6..62a9fe6145 100644
--- a/tracing/tests/span.rs
+++ b/tracing/tests/span.rs
@@ -836,3 +836,33 @@ fn both_shorthands() {
 
     handle.assert_finished();
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn constant_field_name() {
+    let (collector, handle) = collector::mock()
+        .new_span(
+            expect::span().named("my_span").with_field(
+                expect::field("foo")
+                    .with_value(&"bar")
+                    .and(expect::field("constant string").with_value(&"also works"))
+                    .and(expect::field("foo.bar").with_value(&"baz"))
+                    .only(),
+            ),
+        )
+        .only()
+        .run_with_handle();
+
+    with_default(collector, || {
+        const FOO: &str = "foo";
+        tracing::span!(
+            Level::TRACE,
+            "my_span",
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+        );
+    });
+
+    handle.assert_finished();
+}

From b8c45ccd067a6aebdd9257c5edda6bc90bcf9028 Mon Sep 17 00:00:00 2001
From: Aaron Roney 
Date: Wed, 6 Sep 2023 16:09:29 -0700
Subject: [PATCH 046/101] docs: add `axum-insights` to relevant crates. (#2713)

## Motivation

Adding a relevant library to the list of `tracing`-enabled crates.

## Solution

Added to READMEs and documentation.
---
 README.md          | 2 ++
 tracing/README.md  | 2 ++
 tracing/src/lib.rs | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/README.md b/README.md
index 054f999b48..a31a7dbe54 100644
--- a/README.md
+++ b/README.md
@@ -371,6 +371,7 @@ are not maintained by the `tokio` project. These include:
 - [`tracing-actix-web`] provides `tracing` integration for the `actix-web` web framework.
 - [`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
   GELF format.
 - [`tracing-coz`] provides integration with the [coz] causal profiler
@@ -415,6 +416,7 @@ please let us know!)
 [honeycomb.io]: https://www.honeycomb.io/
 [`tracing-actix`]: https://crates.io/crates/tracing-actix
 [`tracing-actix-web`]: https://crates.io/crates/tracing-actix-web
+[`axum-insights`]: https://crates.io/crates/axum-insights
 [`tracing-gelf`]: https://crates.io/crates/tracing-gelf
 [`tracing-coz`]: https://crates.io/crates/tracing-coz
 [coz]: https://github.com/plasma-umass/coz
diff --git a/tracing/README.md b/tracing/README.md
index 8f7f0a6241..ff34b135d2 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -361,6 +361,7 @@ maintained by the `tokio` project. These include:
 - [`tracing-distributed`] Provides a generic implementation of a layer that reports traces spanning multiple machines to some backend.
 - [`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
   GELF format.
 - [`tracing-coz`] provides integration with the [coz] causal profiler
@@ -389,6 +390,7 @@ please let us know! We'd love to add your project to the list!
 [`tracing-distributed`]: https://crates.io/crates/tracing-distributed
 [honeycomb.io]: https://www.honeycomb.io/
 [`tracing-actix`]: https://crates.io/crates/tracing-actix
+[`axum-insights`]: https://crates.io/crates/axum-insights
 [`tracing-gelf`]: https://crates.io/crates/tracing-gelf
 [`tracing-coz`]: https://crates.io/crates/tracing-coz
 [coz]: https://github.com/plasma-umass/coz
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 58baa95659..84a7d77674 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -786,6 +786,7 @@
 //!  - [`tracing-actix-web`] provides `tracing` integration for the `actix-web` web framework.
 //!  - [`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
 //!    GELF format.
 //!  - [`tracing-coz`] provides integration with the [coz] causal profiler
@@ -827,6 +828,7 @@
 //! [honeycomb.io]: https://www.honeycomb.io/
 //! [`tracing-actix-web`]: https://crates.io/crates/tracing-actix-web
 //! [`tracing-actix`]: https://crates.io/crates/tracing-actix
+//! [`axum-insights`]: https://crates.io/crates/axum-insights
 //! [`tracing-gelf`]: https://crates.io/crates/tracing-gelf
 //! [`tracing-coz`]: https://crates.io/crates/tracing-coz
 //! [coz]: https://github.com/plasma-umass/coz

From 2f8f2495cf085247d45131b11b5d410ebae07b97 Mon Sep 17 00:00:00 2001
From: Shayne Fletcher 
Date: Mon, 25 Sep 2023 13:51:03 -0400
Subject: [PATCH 047/101] [tracing-subscriber]: add chrono crate
 implementations of FormatTime (#2690)

## Motivation

Issue https://github.com/tokio-rs/tracing/issues/2080 explains that it's not
possible to soundly use
[`tracing_subscriber::fmt::time::LocalTime`](https://docs.rs/tracing-subscriber/latest/tracing_subscriber/fmt/time/struct.LocalTime.html)
in a multithreaded context. It proposes adding alternative time formatters that
use the [chrono crate](https://docs.rs/chrono/latest/chrono/) to workaround
which is what this PR offers.

## Solution

A new source file 'chrono_crate.rs' is added to the 'tracing-subscriber'
package implementing `mod chrono_crate` providing two new tag types `LocalTime`
and `Utc` with associated `time::FormatTime` trait implementations that call
`chrono::Local::now().to_rfc3339()` and `chrono::Utc::now().to_rfc3339()`
respectively. Simple unit-tests of the new functionality accompany the
additions.
---------

Co-authored-by: David Barsky 
Co-authored-by: Shayne Fletcher 
---
 tracing-subscriber/Cargo.toml                 |   2 +
 .../src/fmt/time/chrono_crate.rs              | 177 ++++++++++++++++++
 tracing-subscriber/src/fmt/time/mod.rs        |  13 ++
 3 files changed, 192 insertions(+)
 create mode 100644 tracing-subscriber/src/fmt/time/chrono_crate.rs

diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index 100da643ca..b73272ba1e 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -32,6 +32,7 @@ fmt = ["registry", "std"]
 ansi = ["fmt", "nu-ansi-term"]
 registry = ["sharded-slab", "thread_local", "std"]
 json = ["tracing-serde", "serde", "serde_json"]
+
 # Enables support for local time when using the `time` crate timestamp
 # formatters.
 local-time = ["time/local-offset"]
@@ -58,6 +59,7 @@ tracing-serde = { path = "../tracing-serde", version = "0.2", optional = true }
 
 # opt-in deps
 parking_lot = { version = "0.12.1", optional = true }
+chrono = { version = "0.4.26", default-features = false, features = ["clock", "std"], optional = true }
 
 # registry
 sharded-slab = { version = "0.1.4", optional = true }
diff --git a/tracing-subscriber/src/fmt/time/chrono_crate.rs b/tracing-subscriber/src/fmt/time/chrono_crate.rs
new file mode 100644
index 0000000000..1a831efa1b
--- /dev/null
+++ b/tracing-subscriber/src/fmt/time/chrono_crate.rs
@@ -0,0 +1,177 @@
+use crate::fmt::format::Writer;
+use crate::fmt::time::FormatTime;
+
+use std::sync::Arc;
+
+/// Formats [local time]s and [UTC time]s with `FormatTime` implementations
+/// that use the [`chrono` crate].
+///
+/// [local time]: [`chrono::offset::Local`]
+/// [UTC time]: [`chrono::offset::Utc`]
+/// [`chrono` crate]: [`chrono`]
+
+/// Formats the current [local time] using a [formatter] from the [`chrono`] crate.
+///
+/// [local time]: chrono::Local::now()
+/// [formatter]: chrono::format
+#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
+#[derive(Debug, Clone, Eq, PartialEq, Default)]
+pub struct ChronoLocal {
+    format: Arc,
+}
+
+impl ChronoLocal {
+    /// Format the time using the [`RFC 3339`] format
+    /// (a subset of [`ISO 8601`]).
+    ///
+    /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
+    /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
+    pub fn rfc_3339() -> Self {
+        Self {
+            format: Arc::new(ChronoFmtType::Rfc3339),
+        }
+    }
+
+    /// Format the time using the given format string.
+    ///
+    /// See [`chrono::format::strftime`] for details on the supported syntax.
+    pub fn new(format_string: String) -> Self {
+        Self {
+            format: Arc::new(ChronoFmtType::Custom(format_string)),
+        }
+    }
+}
+
+impl FormatTime for ChronoLocal {
+    fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
+        let t = chrono::Local::now();
+        match self.format.as_ref() {
+            ChronoFmtType::Rfc3339 => {
+                use chrono::format::{Fixed, Item};
+                write!(
+                    w,
+                    "{}",
+                    t.format_with_items(core::iter::once(Item::Fixed(Fixed::RFC3339)))
+                )
+            }
+            ChronoFmtType::Custom(fmt) => {
+                write!(w, "{}", t.format(fmt))
+            }
+        }
+    }
+}
+
+/// Formats the current [UTC time] using a [formatter] from the [`chrono`] crate.
+///
+/// [UTC time]: chrono::Utc::now()
+/// [formatter]: chrono::format
+#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
+#[derive(Debug, Clone, Eq, PartialEq, Default)]
+pub struct ChronoUtc {
+    format: Arc,
+}
+
+impl ChronoUtc {
+    /// Format the time using the [`RFC 3339`] format
+    /// (a subset of [`ISO 8601`]).
+    ///
+    /// [`RFC 3339`]: https://tools.ietf.org/html/rfc3339
+    /// [`ISO 8601`]: https://en.wikipedia.org/wiki/ISO_8601
+    pub fn rfc_3339() -> Self {
+        Self {
+            format: Arc::new(ChronoFmtType::Rfc3339),
+        }
+    }
+
+    /// Format the time using the given format string.
+    ///
+    /// See [`chrono::format::strftime`] for details on the supported syntax.
+    pub fn new(format_string: String) -> Self {
+        Self {
+            format: Arc::new(ChronoFmtType::Custom(format_string)),
+        }
+    }
+}
+
+impl FormatTime for ChronoUtc {
+    fn format_time(&self, w: &mut Writer<'_>) -> alloc::fmt::Result {
+        let t = chrono::Utc::now();
+        match self.format.as_ref() {
+            ChronoFmtType::Rfc3339 => w.write_str(&t.to_rfc3339()),
+            ChronoFmtType::Custom(fmt) => w.write_str(&format!("{}", t.format(fmt))),
+        }
+    }
+}
+
+/// The RFC 3339 format is used by default but a custom format string
+/// can be used. See [`chrono::format::strftime`]for details on
+/// the supported syntax.
+///
+/// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html
+#[derive(Debug, Clone, Eq, PartialEq)]
+enum ChronoFmtType {
+    /// Format according to the RFC 3339 convention.
+    Rfc3339,
+    /// Format according to a custom format string.
+    Custom(String),
+}
+
+impl Default for ChronoFmtType {
+    fn default() -> Self {
+        ChronoFmtType::Rfc3339
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::fmt::format::Writer;
+    use crate::fmt::time::FormatTime;
+
+    use std::sync::Arc;
+
+    use super::ChronoFmtType;
+    use super::ChronoLocal;
+    use super::ChronoUtc;
+
+    #[test]
+    fn test_chrono_format_time_utc_default() {
+        let mut buf = String::new();
+        let mut dst: Writer<'_> = Writer::new(&mut buf);
+        assert!(FormatTime::format_time(&ChronoUtc::default(), &mut dst).is_ok());
+        // e.g. `buf` contains "2023-08-18T19:05:08.662499+00:00"
+        assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
+    }
+
+    #[test]
+    fn test_chrono_format_time_utc_custom() {
+        let fmt = ChronoUtc {
+            format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
+        };
+        let mut buf = String::new();
+        let mut dst: Writer<'_> = Writer::new(&mut buf);
+        assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
+        // e.g. `buf` contains "Wed Aug 23 15:53:23 2023"
+        assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
+    }
+
+    #[test]
+    fn test_chrono_format_time_local_default() {
+        let mut buf = String::new();
+        let mut dst: Writer<'_> = Writer::new(&mut buf);
+        assert!(FormatTime::format_time(&ChronoLocal::default(), &mut dst).is_ok());
+        // e.g. `buf` contains "2023-08-18T14:59:08.662499-04:00".
+        assert!(chrono::DateTime::parse_from_str(&buf, "%FT%H:%M:%S%.6f%z").is_ok());
+    }
+
+    #[test]
+    fn test_chrono_format_time_local_custom() {
+        let fmt = ChronoLocal {
+            format: Arc::new(ChronoFmtType::Custom("%a %b %e %T %Y".to_owned())),
+        };
+        let mut buf = String::new();
+        let mut dst: Writer<'_> = Writer::new(&mut buf);
+        assert!(FormatTime::format_time(&fmt, &mut dst).is_ok());
+        // e.g. `buf` contains "Wed Aug 23 15:55:46 2023".
+        assert!(chrono::NaiveDateTime::parse_from_str(&buf, "%a %b %e %T %Y").is_ok());
+    }
+}
diff --git a/tracing-subscriber/src/fmt/time/mod.rs b/tracing-subscriber/src/fmt/time/mod.rs
index 21d692bbb0..87886ec797 100644
--- a/tracing-subscriber/src/fmt/time/mod.rs
+++ b/tracing-subscriber/src/fmt/time/mod.rs
@@ -7,6 +7,7 @@ mod datetime;
 
 #[cfg(feature = "time")]
 mod time_crate;
+
 #[cfg(feature = "time")]
 #[cfg_attr(docsrs, doc(cfg(feature = "time")))]
 pub use time_crate::UtcTime;
@@ -15,6 +16,18 @@ pub use time_crate::UtcTime;
 #[cfg_attr(docsrs, doc(cfg(all(unsound_local_offset, feature = "local-time"))))]
 pub use time_crate::LocalTime;
 
+/// [`chrono`]-based implementation for [`FormatTime`].
+#[cfg(feature = "chrono")]
+mod chrono_crate;
+
+#[cfg(feature = "chrono")]
+#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
+pub use chrono_crate::ChronoLocal;
+
+#[cfg(feature = "chrono")]
+#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
+pub use chrono_crate::ChronoUtc;
+
 /// A type that can measure and format the current time.
 ///
 /// This trait is used by `Format` to include a timestamp with each `Event` when it is logged.

From cf8a8c598095cd007ed29044daca0f982561b292 Mon Sep 17 00:00:00 2001
From: David Barsky 
Date: Thu, 28 Sep 2023 16:22:44 -0700
Subject: [PATCH 048/101] attributes: bump minimum version of proc-macro2 to
 1.0.60 (#2732)

As part of landing #2728, I noticed that the `-Zminimal-versions` check fails
due to proc-macro2 1.0.40 referencing a since-removed, nightly-only
feature (`proc_macro_span_shrink`). Since this change doesn't change the MSRV
of `proc-macro2` (or `tracing`, for that matter), this feels like a safe change
to make.
---
 tracing-attributes/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index ae20aa23da..57dd7415b3 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -34,7 +34,7 @@ rust-version = "1.56.0"
 proc-macro = true
 
 [dependencies]
-proc-macro2 = "1.0.40"
+proc-macro2 = "1.0.60"
 syn = { version = "2.0", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] }
 quote = "1.0.20"
 

From 62d57a62c327e387a7bb91d3f503be6162b81db3 Mon Sep 17 00:00:00 2001
From: Jeremy Fitzhardinge 
Date: Thu, 28 Sep 2023 16:57:49 -0700
Subject: [PATCH 049/101] subscriber: Implement subscribe::Filter for
 Option  (#2407)

## Motivation

It's currently awkward to have an optional per-subscriber filter.

## Solution

Implement `Filter` for `Option where F: Filter`, following the example
of `Subscribe`. A `None` filter passes everything through.

Also, it looks like Filter for Arc/Box doesn't pass through all the methods, so
extend the `filter_impl_body` macro to include them.

This also adds a couple of tests and updates the docs.

---------

Co-authored-by: David Barsky 
Co-authored-by: Ryan Johnson 
Co-authored-by: Eliza Weisman 
---
 .../src/filter/subscriber_filters/mod.rs      |  99 ++++++++++++
 tracing-subscriber/src/subscribe/mod.rs       |  24 ++-
 .../tests/option_filter_interest_caching.rs   |  53 +++++++
 .../tests/subscriber_filters/main.rs          |   1 +
 .../tests/subscriber_filters/option.rs        | 149 ++++++++++++++++++
 5 files changed, 323 insertions(+), 3 deletions(-)
 create mode 100644 tracing-subscriber/tests/option_filter_interest_caching.rs
 create mode 100644 tracing-subscriber/tests/subscriber_filters/option.rs

diff --git a/tracing-subscriber/src/filter/subscriber_filters/mod.rs b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
index c7837ec927..d519aa4d7c 100644
--- a/tracing-subscriber/src/filter/subscriber_filters/mod.rs
+++ b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
@@ -478,6 +478,36 @@ macro_rules! filter_impl_body {
         fn max_level_hint(&self) -> Option {
             self.deref().max_level_hint()
         }
+
+        #[inline]
+        fn event_enabled(&self, event: &Event<'_>, cx: &Context<'_, S>) -> bool {
+            self.deref().event_enabled(event, cx)
+        }
+
+        #[inline]
+        fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+            self.deref().on_new_span(attrs, id, ctx)
+        }
+
+        #[inline]
+        fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+            self.deref().on_record(id, values, ctx)
+        }
+
+        #[inline]
+        fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+            self.deref().on_enter(id, ctx)
+        }
+
+        #[inline]
+        fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+            self.deref().on_exit(id, ctx)
+        }
+
+        #[inline]
+        fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+            self.deref().on_close(id, ctx)
+        }
     };
 }
 
@@ -493,6 +523,75 @@ impl subscribe::Filter for Box + Send + Sync + 's
     filter_impl_body!();
 }
 
+// Implement Filter for Option where None => allow
+#[cfg(feature = "registry")]
+#[cfg_attr(docsrs, doc(cfg(feature = "registry")))]
+impl subscribe::Filter for Option
+where
+    F: subscribe::Filter,
+{
+    #[inline]
+    fn enabled(&self, meta: &Metadata<'_>, ctx: &Context<'_, S>) -> bool {
+        self.as_ref()
+            .map(|inner| inner.enabled(meta, ctx))
+            .unwrap_or(true)
+    }
+
+    #[inline]
+    fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest {
+        self.as_ref()
+            .map(|inner| inner.callsite_enabled(meta))
+            .unwrap_or_else(Interest::always)
+    }
+
+    #[inline]
+    fn max_level_hint(&self) -> Option {
+        self.as_ref().and_then(|inner| inner.max_level_hint())
+    }
+
+    #[inline]
+    fn event_enabled(&self, event: &Event<'_>, ctx: &Context<'_, S>) -> bool {
+        self.as_ref()
+            .map(|inner| inner.event_enabled(event, ctx))
+            .unwrap_or(true)
+    }
+
+    #[inline]
+    fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
+        if let Some(inner) = self {
+            inner.on_new_span(attrs, id, ctx)
+        }
+    }
+
+    #[inline]
+    fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
+        if let Some(inner) = self {
+            inner.on_record(id, values, ctx)
+        }
+    }
+
+    #[inline]
+    fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) {
+        if let Some(inner) = self {
+            inner.on_enter(id, ctx)
+        }
+    }
+
+    #[inline]
+    fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
+        if let Some(inner) = self {
+            inner.on_exit(id, ctx)
+        }
+    }
+
+    #[inline]
+    fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
+        if let Some(inner) = self {
+            inner.on_close(id, ctx)
+        }
+    }
+}
+
 // === impl Filtered ===
 
 impl Filtered {
diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs
index 6f375654a9..61a2e4b080 100644
--- a/tracing-subscriber/src/subscribe/mod.rs
+++ b/tracing-subscriber/src/subscribe/mod.rs
@@ -464,12 +464,30 @@
 //!
 //! This crate's [`filter`] module provides a number of types which implement
 //! the [`Filter`] trait, such as [`LevelFilter`], [`Targets`], and
-//! [`FilterFn`]. These [`Filter`]s provide ready-made implementations of
-//! common forms of filtering. For custom filtering policies, the [`FilterFn`]
-//! and [`DynFilterFn`] types allow implementing a [`Filter`] with a closure or
+//! [`FilterFn`]. These [`Filter`]s provide ready-made implementations of common
+//! forms of filtering. For custom filtering policies, the [`FilterFn`] and
+//! [`DynFilterFn`] types allow implementing a [`Filter`] with a closure or
 //! function pointer. In addition, when more control is required, the [`Filter`]
 //! trait may also be implemented for user-defined types.
 //!
+//! [`Option`] also implements [`Filter`], which allows for an optional
+//! filter. [`None`](Option::None) filters out _nothing_ (that is, allows
+//! everything through). For example:
+//!
+//! ```rust
+//! # use tracing_subscriber::{filter::filter_fn, Subscribe};
+//! # use tracing_core::{Metadata, collect::Collect};
+//! # struct MySubscriber(std::marker::PhantomData);
+//! # impl MySubscriber { fn new() -> Self { Self(std::marker::PhantomData)} }
+//! # impl Subscribe for MySubscriber {}
+//! # fn my_filter(_: &str) -> impl Fn(&Metadata) -> bool { |_| true  }
+//! fn setup_tracing(filter_config: Option<&str>) {
+//!     let layer = MySubscriber::::new()
+//!         .with_filter(filter_config.map(|config| filter_fn(my_filter(config))));
+//! //...
+//! }
+//! ```
+//!
 //! 
//!
 //!     Warning: Currently, the 
diff --git a/tracing-subscriber/tests/option_filter_interest_caching.rs b/tracing-subscriber/tests/option_filter_interest_caching.rs
new file mode 100644
index 0000000000..5853d65718
--- /dev/null
+++ b/tracing-subscriber/tests/option_filter_interest_caching.rs
@@ -0,0 +1,53 @@
+// A separate test crate for `Option` for isolation from other tests
+// that may influence the interest cache.
+
+use std::sync::{
+    atomic::{AtomicUsize, Ordering},
+    Arc,
+};
+use tracing_mock::{expect, subscriber};
+use tracing_subscriber::{filter, prelude::*, Subscribe};
+
+/// A `None` filter should always be interested in events, and it should not
+/// needlessly degrade the caching of other filters.
+#[test]
+fn none_interest_cache() {
+    let (subscribe_none, handle_none) = subscriber::mock()
+        .event(expect::event())
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe_none = subscribe_none.with_filter(None::>);
+
+    let times_filtered = Arc::new(AtomicUsize::new(0));
+    let (subscribe_filter_fn, handle_filter_fn) = subscriber::mock()
+        .event(expect::event())
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe_filter_fn = subscribe_filter_fn.with_filter(filter::filter_fn({
+        let times_filtered = Arc::clone(×_filtered);
+        move |_| {
+            times_filtered.fetch_add(1, Ordering::Relaxed);
+            true
+        }
+    }));
+
+    let subscriber = tracing_subscriber::registry()
+        .with(subscribe_none)
+        .with(subscribe_filter_fn);
+
+    let _guard = subscriber.set_default();
+    for _ in 0..2 {
+        tracing::debug!(target: "always_interesting", x="bar");
+    }
+
+    // The `None` filter is unchanging and performs no filtering, so it should
+    // be cacheable and always be interested in events. The filter fn is a
+    // non-dynamic filter fn, which means the result can be cached per callsite.
+    // The filter fn should only need to be called once, and the `Option` filter
+    // should not interfere in the caching of that result.
+    assert_eq!(times_filtered.load(Ordering::Relaxed), 1);
+    handle_none.assert_finished();
+    handle_filter_fn.assert_finished();
+}
diff --git a/tracing-subscriber/tests/subscriber_filters/main.rs b/tracing-subscriber/tests/subscriber_filters/main.rs
index 0ec43b67e1..79aacbfca2 100644
--- a/tracing-subscriber/tests/subscriber_filters/main.rs
+++ b/tracing-subscriber/tests/subscriber_filters/main.rs
@@ -1,5 +1,6 @@
 #![cfg(feature = "registry")]
 mod filter_scopes;
+mod option;
 mod per_event;
 mod targets;
 mod trees;
diff --git a/tracing-subscriber/tests/subscriber_filters/option.rs b/tracing-subscriber/tests/subscriber_filters/option.rs
new file mode 100644
index 0000000000..0bdf356d33
--- /dev/null
+++ b/tracing-subscriber/tests/subscriber_filters/option.rs
@@ -0,0 +1,149 @@
+use super::*;
+use tracing::Collect;
+use tracing_subscriber::{
+    filter::{self, LevelFilter},
+    prelude::*,
+    Subscribe,
+};
+
+fn filter_out_everything() -> filter::DynFilterFn {
+    // Use dynamic filter fn to disable interest caching and max-level hints,
+    // allowing us to put all of these tests in the same file.
+    filter::dynamic_filter_fn(|_, _| false)
+}
+
+#[test]
+fn option_some() {
+    let (subscribe, handle) = subscriber::mock().only().run_with_handle();
+    let subscribe = subscribe.with_filter(Some(filter_out_everything()));
+
+    let _guard = tracing_subscriber::registry().with(subscribe).set_default();
+
+    for i in 0..2 {
+        tracing::info!(i);
+    }
+
+    handle.assert_finished();
+}
+
+#[test]
+fn option_none() {
+    let (subscribe, handle) = subscriber::mock()
+        .event(expect::event())
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe = subscribe.with_filter(None::>);
+
+    let _guard = tracing_subscriber::registry().with(subscribe).set_default();
+
+    for i in 0..2 {
+        tracing::info!(i);
+    }
+
+    handle.assert_finished();
+}
+
+#[test]
+fn option_mixed() {
+    let (subscribe, handle) = subscriber::mock()
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe = subscribe
+        .with_filter(filter::dynamic_filter_fn(|meta, _ctx| {
+            meta.target() == "interesting"
+        }))
+        .with_filter(None::>);
+
+    let _guard = tracing_subscriber::registry().with(subscribe).set_default();
+
+    tracing::info!(target: "interesting", x="foo");
+    tracing::info!(target: "boring", x="bar");
+
+    handle.assert_finished();
+}
+
+/// The lack of a max level hint from a `None` filter should result in no max
+/// level hint when combined with other filters/subscribers.
+#[test]
+fn none_max_level_hint() {
+    let (subscribe_none, handle_none) = subscriber::mock()
+        .event(expect::event())
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe_none = subscribe_none.with_filter(None::>);
+    assert!(subscribe_none.max_level_hint().is_none());
+
+    let (subscribe_filter_fn, handle_filter_fn) = subscriber::mock()
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let max_level = Level::INFO;
+    let subscribe_filter_fn = subscribe_filter_fn.with_filter(
+        filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &max_level)
+            .with_max_level_hint(max_level),
+    );
+    assert_eq!(
+        subscribe_filter_fn.max_level_hint(),
+        Some(LevelFilter::INFO)
+    );
+
+    let subscriber = tracing_subscriber::registry()
+        .with(subscribe_none)
+        .with(subscribe_filter_fn);
+    // The absence of a hint from the `None` filter upgrades the `INFO` hint
+    // from the filter fn subscriber.
+    assert!(subscriber.max_level_hint().is_none());
+
+    let _guard = subscriber.set_default();
+    tracing::info!(target: "interesting", x="foo");
+    tracing::debug!(target: "sometimes_interesting", x="bar");
+
+    handle_none.assert_finished();
+    handle_filter_fn.assert_finished();
+}
+
+/// The max level hint from inside a `Some(filter)` filter should be propagated
+/// and combined with other filters/subscribers.
+#[test]
+fn some_max_level_hint() {
+    let (subscribe_some, handle_some) = subscriber::mock()
+        .event(expect::event())
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe_some = subscribe_some.with_filter(Some(
+        filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &Level::DEBUG)
+            .with_max_level_hint(Level::DEBUG),
+    ));
+    assert_eq!(subscribe_some.max_level_hint(), Some(LevelFilter::DEBUG));
+
+    let (subscribe_filter_fn, handle_filter_fn) = subscriber::mock()
+        .event(expect::event())
+        .only()
+        .run_with_handle();
+    let subscribe_filter_fn = subscribe_filter_fn.with_filter(
+        filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &Level::INFO)
+            .with_max_level_hint(Level::INFO),
+    );
+    assert_eq!(
+        subscribe_filter_fn.max_level_hint(),
+        Some(LevelFilter::INFO)
+    );
+
+    let subscriber = tracing_subscriber::registry()
+        .with(subscribe_some)
+        .with(subscribe_filter_fn);
+    // The `DEBUG` hint from the `Some` filter upgrades the `INFO` hint from the
+    // filter fn subscriber.
+    assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG));
+
+    let _guard = subscriber.set_default();
+    tracing::info!(target: "interesting", x="foo");
+    tracing::debug!(target: "sometimes_interesting", x="bar");
+
+    handle_some.assert_finished();
+    handle_filter_fn.assert_finished();
+}

From 4be2eabd4f9761e2efd04c983383c0f0e8d090a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Beno=C3=AEt=20Burnichon?= 
Date: Wed, 4 Oct 2023 20:09:04 +0200
Subject: [PATCH 050/101] subscriber: update documentation link to latest
 (#2434)

## Motivation

Currently, link points to an outdated version.

## Solution

Make the link point to latest released version
---
 tracing-subscriber/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md
index 9e5c0b645e..f1b7a03f2f 100644
--- a/tracing-subscriber/README.md
+++ b/tracing-subscriber/README.md
@@ -21,7 +21,7 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers.
 [crates-badge]: https://img.shields.io/crates/v/tracing-subscriber.svg
 [crates-url]: https://crates.io/crates/tracing-subscriber
 [docs-badge]: https://docs.rs/tracing-subscriber/badge.svg
-[docs-url]: https://docs.rs/tracing-subscriber/0.2.12
+[docs-url]: https://docs.rs/tracing-subscriber/latest
 [docs-master-badge]: https://img.shields.io/badge/docs-master-blue
 [docs-master-url]: https://tracing-rs.netlify.com/tracing_subscriber
 [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg

From 180684a3db0774947f7fd0d3cac2960fdfe420cd Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Mon, 9 Oct 2023 09:28:14 -0700
Subject: [PATCH 051/101] journald: fix clippy `unwrap_or_default` warning
 (#2742)

The latest Clippy emits warnings for uses of `unwrap_or_else` with
functions that return a type's `Default::default` value, such as
`.unwrap_or_else(String::new)`. Clippy would prefer us to use
`unwrap_or_default` instead, which does the same thing.

This commit fixes the lint. Personally, I don't really care about this,
but it makes the warning go away...
---
 tracing-journald/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs
index 4ad5a58d5b..34ed915bbb 100644
--- a/tracing-journald/src/lib.rs
+++ b/tracing-journald/src/lib.rs
@@ -109,7 +109,7 @@ impl Subscriber {
                     .and_then(|p| p.file_name())
                     .map(|n| n.to_string_lossy().into_owned())
                     // If we fail to get the name of the current executable fall back to an empty string.
-                    .unwrap_or_else(String::new),
+                    .unwrap_or_default(),
                 additional_fields: Vec::new(),
             };
             // Check that we can talk to journald, by sending empty payload which journald discards.

From f085b8905745a7d30a16a77fda851d3b683d693b Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Mon, 9 Oct 2023 11:43:36 -0700
Subject: [PATCH 052/101] attributes: fix clippy warning in attributes tests
 (#2742)

Clippy doesn't like the redefinition of a binding with itself. I don't
think this was necessary for the bug we were reproducing here (as the
error test doesn't do this), so I removed it.
---
 tracing-attributes/tests/async_fn.rs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 29481c4e7c..568798d1f1 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -17,7 +17,6 @@ async fn test_async_fn(polls: usize) -> Result<(), ()> {
 #[allow(dead_code)] // this is just here to test whether it compiles.
 #[instrument]
 async fn test_ret_impl_trait(n: i32) -> Result, ()> {
-    let n = n;
     Ok((0..10).filter(move |x| *x < n))
 }
 

From 99e0377a6c48dd88b06ed2ae0259c62d8312c58d Mon Sep 17 00:00:00 2001
From: Kaifas 
Date: Tue, 10 Oct 2023 20:02:34 -0400
Subject: [PATCH 053/101] subscriber: make `format::Writer::new()` public
 (#2680)

## Motivation

As seen here #2512 and #2223. Previously pr'ed here #2525, but no progress has
been made there for quite some. I have applied the suggested changes. Not sure
the formatting of the doc is sufficient or otherwise correct

## Solution

Make the `format::Writer::new()` function public. I don't see any obvious
trade-offs, but I am not familiar with the larger direction of
tracing/subscriber, so I may be wrong.

Closes  #2512
Closes #2223

Co-authored-by: Cephas Storm 
Co-authored-by: Eliza Weisman 
---
 tracing-subscriber/src/fmt/format/mod.rs | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs
index 2d0fbfa6d2..d4c619b3fb 100644
--- a/tracing-subscriber/src/fmt/format/mod.rs
+++ b/tracing-subscriber/src/fmt/format/mod.rs
@@ -422,7 +422,19 @@ impl<'writer> Writer<'writer> {
     // We may not want to do that if we choose to expose specialized
     // constructors instead (e.g. `from_string` that stores whether the string
     // is empty...?)
-    pub(crate) fn new(writer: &'writer mut impl fmt::Write) -> Self {
+    //(@kaifastromai) I suppose having dedicated constructors may have certain benefits
+    // but I am not privy to the larger direction of tracing/subscriber.
+    /// Create a new [`Writer`] from any type that implements [`fmt::Write`].
+    ///
+    /// The returned `Writer` value may be passed as an argument to methods
+    /// such as [`Format::format_event`]. Since constructing a `Writer`
+    /// mutably borrows the underlying [`fmt::Write`] instance, that value may
+    /// be accessed again once the `Writer` is dropped. For example, if the
+    /// value implementing [`fmt::Write`] is a [`String`], it will contain
+    /// the formatted output of [`Format::format_event`], which may then be
+    /// used for other purposes.
+    #[must_use]
+    pub fn new(writer: &'writer mut impl fmt::Write) -> Self {
         Self {
             writer: writer as &mut dyn fmt::Write,
             is_ansi: false,

From 9eda68bb9c4123d52ac454952448e6d248e36553 Mon Sep 17 00:00:00 2001
From: Toby Murray 
Date: Thu, 12 Oct 2023 15:04:26 -0400
Subject: [PATCH 054/101] attributes: fix typo "overriden" => "overridden"
 (#2719)

Co-authored-by: Eliza Weisman 
---
 tracing-attributes/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index fcde9ae8c2..ca3fe77d14 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -88,7 +88,7 @@ mod expand;
 /// Instruments a function to create and enter a `tracing` [span] every time
 /// the function is called.
 ///
-/// Unless overriden, a span with `info` level will be generated.
+/// Unless overridden, a span with `info` level will be generated.
 /// The generated span's name will be the name of the function.
 /// By default, all arguments to the function are included as fields on the
 /// span. Arguments that are `tracing` [primitive types] implementing the

From 6cb6c2722f773dcd8fa7d8f5186f3e20be757f2c Mon Sep 17 00:00:00 2001
From: Max Burke 
Date: Thu, 12 Oct 2023 12:02:11 -0700
Subject: [PATCH 055/101] log: update to env_logger 0.10 to fix
 GHSA-g98v-hv3f-hcfr (#2740)

The package `atty`, a dependent of `env_logger` < 0.10, has a RUSTSEC advisory
raised against it (GHSA-g98v-hv3f-hcfr). This branch updates `env_logger` to
0.10 to fix this issue.
; Conflicts:
;	tracing-log/Cargo.toml
---
 tracing-log/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml
index 61e68b376c..28fbf30d22 100644
--- a/tracing-log/Cargo.toml
+++ b/tracing-log/Cargo.toml
@@ -26,7 +26,7 @@ log-tracer = []
 tracing-core = { path = "../tracing-core", version = "0.2"}
 log = "0.4.17"
 once_cell = "1.13.0"
-env_logger = { version = "0.8.4", optional = true }
+env_logger = { version = "0.10", optional = true }
 
 [dev-dependencies]
 tracing = { path = "../tracing", version = "0.2"}

From 84f0a608123a651f268ff76347f81182f487d3b1 Mon Sep 17 00:00:00 2001
From: Eliza Weisman 
Date: Thu, 12 Oct 2023 12:12:17 -0700
Subject: [PATCH 056/101] chore: remove `env_logger` from `hyper` example

Currently, the `hyper_echo` example uses the `tracing-log` env logger
support for some weird reason. I believe this is due to Hyper previously
using `log` rather than `tracing`. However, `hyper` now emits native
`tracing` diagnostics, so all the `env_logger` nonsense can just be
removed from the example.

This branch does that.
---
 examples/Cargo.toml             |  3 ---
 examples/examples/hyper-echo.rs | 13 ++-----------
 2 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/examples/Cargo.toml b/examples/Cargo.toml
index b33c85f1c4..e8878eae15 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -30,9 +30,6 @@ serde_json = "1.0.82"
 futures = "0.3.21"
 tokio = { version = "1.20.0", features = ["full"] }
 
-# env-logger example
-env_logger = "0.8.4"
-
 # tower examples
 tower = { version = "0.4.13", features = ["full"] }
 http = "0.2.8"
diff --git a/examples/examples/hyper-echo.rs b/examples/examples/hyper-echo.rs
index ffdd0a6368..8c09ac1ccd 100644
--- a/examples/examples/hyper-echo.rs
+++ b/examples/examples/hyper-echo.rs
@@ -89,18 +89,9 @@ async fn echo(req: Request) -> Result, hyper::Error> {
 
 #[tokio::main]
 async fn main() -> Result<(), Box> {
-    use tracing_log::env_logger::BuilderExt;
-
-    let collector = tracing_subscriber::fmt()
+    tracing_subscriber::fmt()
         .with_max_level(Level::TRACE)
-        .finish();
-    let mut builder = env_logger::Builder::new();
-    builder
-        .filter(Some("hyper_echo"), log::LevelFilter::Off)
-        .filter(Some("hyper"), log::LevelFilter::Trace)
-        .emit_traces() // from `tracing_log::env_logger::BuilderExt`
-        .try_init()?;
-    tracing::collect::set_global_default(collector)?;
+        .init();
 
     let local_addr: std::net::SocketAddr = ([127, 0, 0, 1], 3000).into();
     let server_span = span!(Level::TRACE, "server", %local_addr);

From 22975803faca6a5e600a4b784b79302871b3eef6 Mon Sep 17 00:00:00 2001
From: David Barsky 
Date: Thu, 12 Oct 2023 19:17:26 -0400
Subject: [PATCH 057/101] Revert "log: update to env_logger 0.10 to fix
 GHSA-g98v-hv3f-hcfr (#2740)" (#2750)

---
 tracing-log/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml
index 28fbf30d22..61e68b376c 100644
--- a/tracing-log/Cargo.toml
+++ b/tracing-log/Cargo.toml
@@ -26,7 +26,7 @@ log-tracer = []
 tracing-core = { path = "../tracing-core", version = "0.2"}
 log = "0.4.17"
 once_cell = "1.13.0"
-env_logger = { version = "0.10", optional = true }
+env_logger = { version = "0.8.4", optional = true }
 
 [dev-dependencies]
 tracing = { path = "../tracing", version = "0.2"}

From b16965b102c46cb47538cb9b37e7bfc7d5e9120d Mon Sep 17 00:00:00 2001
From: Gabriel Goller 
Date: Fri, 13 Oct 2023 20:03:57 +0200
Subject: [PATCH 058/101] tracing: use full path when calling `format_args!`
 (#2757)

When a custom macro "format_args" is defined, macros such as
`tracing::warn`, `tracing::debug`, etc. will fail. Adding the
full path `::core::format_args!` to the calls fixes this.

Fixes: #2721
---
 tracing/src/macros.rs   |  18 ++---
 tracing/tests/macros.rs | 151 +++++++++++++++++++++++++++++++++++-----
 2 files changed, 141 insertions(+), 28 deletions(-)

diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 31890b69ee..be84c8c195 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -628,7 +628,7 @@ macro_rules! event {
             target: $target,
             parent: $parent,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -679,7 +679,7 @@ macro_rules! event {
             name: $name,
             target: $target,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -737,7 +737,7 @@ macro_rules! event {
             target: $target,
             parent: $parent,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -790,7 +790,7 @@ macro_rules! event {
             name: $name,
             parent: $parent,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -840,7 +840,7 @@ macro_rules! event {
         $crate::event!(
             name: $name,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -895,7 +895,7 @@ macro_rules! event {
         $crate::event!(
             target: $target,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -911,7 +911,7 @@ macro_rules! event {
             target: module_path!(),
             parent: $parent,
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     (parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => (
@@ -971,7 +971,7 @@ macro_rules! event {
         $crate::event!(
             target: module_path!(),
             $lvl,
-            { message = format_args!($($arg)+), $($fields)* }
+            { message = ::core::format_args!($($arg)+), $($fields)* }
         )
     );
     ($lvl:expr, $($k:ident).+ = $($field:tt)*) => (
@@ -2966,7 +2966,7 @@ macro_rules! valueset {
 
     // Remainder is unparsable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
-        $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
+        $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&::core::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
     };
 
     // === entry ===
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index 5c993ccffa..81b929d1d3 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -126,8 +126,20 @@ fn span_root() {
     span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo", bar.baz = 2, quux = 4,);
     span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "foo");
     span!(target: "foo_events", parent: ::core::option::Option::None, Level::TRACE, "bar",);
-    span!(parent: ::core::option::Option::None, Level::DEBUG, "foo", bar.baz = 2, quux = 3);
-    span!(parent: ::core::option::Option::None, Level::DEBUG, "foo", bar.baz = 2, quux = 4,);
+    span!(
+        parent: ::core::option::Option::None,
+        Level::DEBUG,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    span!(
+        parent: ::core::option::Option::None,
+        Level::DEBUG,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     span!(parent: ::core::option::Option::None, Level::DEBUG, "foo");
     span!(parent: ::core::option::Option::None, Level::DEBUG, "bar",);
 }
@@ -139,8 +151,18 @@ fn trace_span_root() {
     trace_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
     trace_span!(target: "foo_events", parent: ::core::option::Option::None, "foo");
     trace_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",);
-    trace_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3);
-    trace_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
+    trace_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    trace_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     trace_span!(parent: ::core::option::Option::None, "foo");
     trace_span!(parent: ::core::option::Option::None, "bar",);
 }
@@ -152,8 +174,18 @@ fn debug_span_root() {
     debug_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
     debug_span!(target: "foo_events", parent: ::core::option::Option::None, "foo");
     debug_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",);
-    debug_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3);
-    debug_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
+    debug_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    debug_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     debug_span!(parent: ::core::option::Option::None, "foo");
     debug_span!(parent: ::core::option::Option::None, "bar",);
 }
@@ -165,8 +197,18 @@ fn info_span_root() {
     info_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
     info_span!(target: "foo_events", parent: ::core::option::Option::None, "foo");
     info_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",);
-    info_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3);
-    info_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
+    info_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    info_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     info_span!(parent: ::core::option::Option::None, "foo");
     info_span!(parent: ::core::option::Option::None, "bar",);
 }
@@ -178,8 +220,18 @@ fn warn_span_root() {
     warn_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
     warn_span!(target: "foo_events", parent: ::core::option::Option::None, "foo");
     warn_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",);
-    warn_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3);
-    warn_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
+    warn_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    warn_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     warn_span!(parent: ::core::option::Option::None, "foo");
     warn_span!(parent: ::core::option::Option::None, "bar",);
 }
@@ -191,8 +243,18 @@ fn error_span_root() {
     error_span!(target: "foo_events", parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
     error_span!(target: "foo_events", parent: ::core::option::Option::None, "foo");
     error_span!(target: "foo_events", parent: ::core::option::Option::None, "bar",);
-    error_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 3);
-    error_span!(parent: ::core::option::Option::None, "foo", bar.baz = 2, quux = 4,);
+    error_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 3
+    );
+    error_span!(
+        parent: ::core::option::Option::None,
+        "foo",
+        bar.baz = 2,
+        quux = 4,
+    );
     error_span!(parent: ::core::option::Option::None, "foo");
     error_span!(parent: ::core::option::Option::None, "bar",);
 }
@@ -712,9 +774,19 @@ fn event_root() {
         bar.baz = 2,
         quux = false
     );
-    event!(parent: ::core::option::Option::None, Level::DEBUG, foo = 3, bar.baz = 3,);
+    event!(
+        parent: ::core::option::Option::None,
+        Level::DEBUG,
+        foo = 3,
+        bar.baz = 3,
+    );
     event!(parent: ::core::option::Option::None, Level::DEBUG, "foo");
-    event!(parent: ::core::option::Option::None, Level::DEBUG, "foo: {}", 3);
+    event!(
+        parent: ::core::option::Option::None,
+        Level::DEBUG,
+        "foo: {}",
+        3
+    );
     event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 3, bar.baz = 80 }, "quux");
     event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}", true);
     event!(parent: ::core::option::Option::None, Level::DEBUG, { foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
@@ -741,7 +813,12 @@ fn event_root() {
 #[test]
 fn trace_root() {
     trace!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false);
-    trace!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    trace!(
+        parent: ::core::option::Option::None,
+        foo = 3,
+        bar.baz = 2,
+        quux = false
+    );
     trace!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
     trace!(parent: ::core::option::Option::None, "foo");
     trace!(parent: ::core::option::Option::None, "foo: {}", 3);
@@ -772,7 +849,12 @@ fn trace_root() {
 #[test]
 fn debug_root() {
     debug!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false);
-    debug!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    debug!(
+        parent: ::core::option::Option::None,
+        foo = 3,
+        bar.baz = 2,
+        quux = false
+    );
     debug!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
     debug!(parent: ::core::option::Option::None, "foo");
     debug!(parent: ::core::option::Option::None, "foo: {}", 3);
@@ -803,7 +885,12 @@ fn debug_root() {
 #[test]
 fn info_root() {
     info!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false);
-    info!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    info!(
+        parent: ::core::option::Option::None,
+        foo = 3,
+        bar.baz = 2,
+        quux = false
+    );
     info!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
     info!(parent: ::core::option::Option::None, "foo");
     info!(parent: ::core::option::Option::None, "foo: {}", 3);
@@ -834,7 +921,12 @@ fn info_root() {
 #[test]
 fn warn_root() {
     warn!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false);
-    warn!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    warn!(
+        parent: ::core::option::Option::None,
+        foo = 3,
+        bar.baz = 2,
+        quux = false
+    );
     warn!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
     warn!(parent: ::core::option::Option::None, "foo");
     warn!(parent: ::core::option::Option::None, "foo: {}", 3);
@@ -865,7 +957,12 @@ fn warn_root() {
 #[test]
 fn error_root() {
     error!(parent: ::core::option::Option::None, foo = ?3, bar.baz = %2, quux = false);
-    error!(parent: ::core::option::Option::None, foo = 3, bar.baz = 2, quux = false);
+    error!(
+        parent: ::core::option::Option::None,
+        foo = 3,
+        bar.baz = 2,
+        quux = false
+    );
     error!(parent: ::core::option::Option::None, foo = 3, bar.baz = 3,);
     error!(parent: ::core::option::Option::None, "foo");
     error!(parent: ::core::option::Option::None, "foo: {}", 3);
@@ -1156,3 +1253,19 @@ fn callsite_macro_api() {
         fields: foo,
     };
 }
+
+#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
+#[test]
+fn format_args_already_defined() {
+    // Reproduces: https://github.com/tokio-rs/tracing/issues/2721
+    #[allow(unused)]
+    macro_rules! format_args {
+        ($i:expr) => {};
+    }
+    event!(Level::DEBUG, "foo: {}", 3);
+    trace!("foo: {}", 3);
+    debug!("foo: {}", 3);
+    info!("foo: {}", 3);
+    warn!("foo: {}", 3);
+    error!("foo: {}", 3);
+}

From 3a80127aad7e2285e782897a28210169aa2a6a20 Mon Sep 17 00:00:00 2001
From: AnthonyMikh 
Date: Sun, 15 Oct 2023 22:48:38 +0400
Subject: [PATCH 059/101] appender: remove `Sync` bound from writer for
 `NonBlocking` (#2607)

## Motivation

`NonBlocking` from `tracing-appender` wraps a writer and requires that
writer to implement `Sync` (among other bounds). However it is not clear
why this bound is necessary in first place: this writer is sent to a
dedicated thread and never used directly concurrently.

#1934 demonstrates that it is a real problem for some people. Also at my
current work we hit this issue as well when a third-party writer did not
implement `Sync`. Our workaround was to wrap that writer in our own type
and manually implement `Send` and `Sync` for it. Needless to say that it
is more a hack than a proper solution.

## Solution

Remove `Sync` bound in relevant places. Yep, that simple. Probably
having it in first place was just an oversight.

Closes #1934
---
 tracing-appender/src/lib.rs          | 2 +-
 tracing-appender/src/non_blocking.rs | 6 +++---
 tracing-appender/src/worker.rs       | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index 0555ba0435..03ce53fec0 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -188,7 +188,7 @@ pub(crate) mod sync;
 /// });
 /// # }
 /// ```
-pub fn non_blocking(writer: T) -> (NonBlocking, WorkerGuard) {
+pub fn non_blocking(writer: T) -> (NonBlocking, WorkerGuard) {
     NonBlocking::new(writer)
 }
 
diff --git a/tracing-appender/src/non_blocking.rs b/tracing-appender/src/non_blocking.rs
index ce25565054..c61e85c082 100644
--- a/tracing-appender/src/non_blocking.rs
+++ b/tracing-appender/src/non_blocking.rs
@@ -146,11 +146,11 @@ impl NonBlocking {
     ///
     /// [default]: NonBlockingBuilder::default()
     /// [builder]: NonBlockingBuilder
-    pub fn new(writer: T) -> (NonBlocking, WorkerGuard) {
+    pub fn new(writer: T) -> (NonBlocking, WorkerGuard) {
         NonBlockingBuilder::default().finish(writer)
     }
 
-    fn create(
+    fn create(
         writer: T,
         buffered_lines_limit: usize,
         is_lossy: bool,
@@ -221,7 +221,7 @@ impl NonBlockingBuilder {
     }
 
     /// Completes the builder, returning the configured `NonBlocking`.
-    pub fn finish(self, writer: T) -> (NonBlocking, WorkerGuard) {
+    pub fn finish(self, writer: T) -> (NonBlocking, WorkerGuard) {
         NonBlocking::create(
             writer,
             self.buffered_lines_limit,
diff --git a/tracing-appender/src/worker.rs b/tracing-appender/src/worker.rs
index b2579fb2cd..4262257ccd 100644
--- a/tracing-appender/src/worker.rs
+++ b/tracing-appender/src/worker.rs
@@ -4,7 +4,7 @@ use std::fmt::Debug;
 use std::io::Write;
 use std::{io, thread};
 
-pub(crate) struct Worker {
+pub(crate) struct Worker {
     writer: T,
     receiver: Receiver,
     shutdown: Receiver<()>,
@@ -18,7 +18,7 @@ pub(crate) enum WorkerState {
     Shutdown,
 }
 
-impl Worker {
+impl Worker {
     pub(crate) fn new(receiver: Receiver, writer: T, shutdown: Receiver<()>) -> Worker {
         Self {
             writer,

From 82a22ee4cc4aeca2c3c1537e4115521c4a7c3c31 Mon Sep 17 00:00:00 2001
From: Manish Goregaokar 
Date: Wed, 18 Oct 2023 16:44:33 -0700
Subject: [PATCH 060/101] tracing: use ManuallyDrop instead of mem::forget
 (#2765)

The current code is UB and LLVM could choose to reuse the stack slot causing a UAF.

## Motivation

UB is bad.

## Solution

Don't do that.
---
 tracing/src/instrument.rs | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/tracing/src/instrument.rs b/tracing/src/instrument.rs
index 208c2bd0cf..d9010a34f4 100644
--- a/tracing/src/instrument.rs
+++ b/tracing/src/instrument.rs
@@ -2,7 +2,7 @@ use crate::span::Span;
 use core::{
     future::Future,
     marker::Sized,
-    mem::{self, ManuallyDrop},
+    mem::ManuallyDrop,
     pin::Pin,
     task::{Context, Poll},
 };
@@ -392,12 +392,11 @@ impl Instrumented {
     ///
     /// Note that this drops the span.
     pub fn into_inner(self) -> T {
-        // To manually destructure `Instrumented` without `Drop`, we save
-        // pointers to the fields and use `mem::forget` to leave those pointers
-        // valid.
-        let span: *const Span = &self.span;
-        let inner: *const ManuallyDrop = &self.inner;
-        mem::forget(self);
+        // To manually destructure `Instrumented` without `Drop`, we
+        // move it into a ManuallyDrop and use pointers to its fields
+        let this = ManuallyDrop::new(self);
+        let span: *const Span = &this.span;
+        let inner: *const ManuallyDrop = &this.inner;
         // SAFETY: Those pointers are valid for reads, because `Drop` didn't
         //         run, and properly aligned, because `Instrumented` isn't
         //         `#[repr(packed)]`.

From 90ef8e3acb5c408d701a6b0ceae8b9f53037200c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=B8ren=20Mortensen?= 
Date: Thu, 19 Oct 2023 18:44:00 +0100
Subject: [PATCH 061/101] core: fix incorrect (incorrectly updated) docs for
 LevelFilter (#2767)

These docs were updated (in response to #1669), but the "or equal to"
phrase should have been moved to the other case.

Fixes: #1993

## Motivation

When these docs were updated in response to #1669, they were updated
incompletely (a level that is equal to a filter is _enabled_, not
_disabled_) and therefore remained incorrect.

## Solution

The docs were updated, moving the "or equal to" phrase to the other
case.
---
 tracing-core/src/metadata.rs | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs
index 7487f9d403..76d84e6d1e 100644
--- a/tracing-core/src/metadata.rs
+++ b/tracing-core/src/metadata.rs
@@ -223,10 +223,9 @@ pub struct Level(LevelInner);
 
 /// A filter comparable to a verbosity [`Level`].
 ///
-/// If a [`Level`] is considered less than a `LevelFilter`, it should be
-/// considered enabled; if greater than or equal to the `LevelFilter`,
-/// that level is disabled. See [`LevelFilter::current`] for more
-/// details.
+/// If a [`Level`] is considered less than or equal to a `LevelFilter`, it
+/// should be considered enabled; if greater than the `LevelFilter`, that level
+/// is disabled. See [`LevelFilter::current`] for more details.
 ///
 /// Note that this is essentially identical to the `Level` type, but with the
 /// addition of an [`OFF`] level that completely disables all trace

From f93cfa087e6ebdcbd8ecdcccca47d73c3a89ab94 Mon Sep 17 00:00:00 2001
From: Gabriel Goller 
Date: Fri, 20 Oct 2023 19:01:19 +0200
Subject: [PATCH 062/101] tracing: removed core imports in macros (#2762)

When a user has a crate named `core`, it can cause issues
because our crates import from `::core::*`. Now we are
importing from `$crate::__macro_support::*` and there will
be no more name clashes.
---
 tracing/src/lib.rs    |  4 ++++
 tracing/src/macros.rs | 56 +++++++++++++++++++++----------------------
 2 files changed, 32 insertions(+), 28 deletions(-)

diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 84a7d77674..34aabca314 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -1001,6 +1001,10 @@ pub mod __macro_support {
     use crate::{collect::Interest, Metadata};
     use core::fmt;
     use core::sync::atomic::{AtomicU8, Ordering};
+    // 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, format_args, iter::Iterator, option::Option};
 
     /// Callsite implementation used by macro-generated code.
     ///
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index be84c8c195..3756186ff7 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -628,7 +628,7 @@ macro_rules! event {
             target: $target,
             parent: $parent,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -679,7 +679,7 @@ macro_rules! event {
             name: $name,
             target: $target,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -737,7 +737,7 @@ macro_rules! event {
             target: $target,
             parent: $parent,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (target: $target:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -790,7 +790,7 @@ macro_rules! event {
             name: $name,
             parent: $parent,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -840,7 +840,7 @@ macro_rules! event {
         $crate::event!(
             name: $name,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (name: $name:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -895,7 +895,7 @@ macro_rules! event {
         $crate::event!(
             target: $target,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (target: $target:expr, $lvl:expr, $($k:ident).+ = $($fields:tt)* ) => (
@@ -911,7 +911,7 @@ macro_rules! event {
             target: module_path!(),
             parent: $parent,
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     (parent: $parent:expr, $lvl:expr, $($k:ident).+ = $($field:tt)*) => (
@@ -971,7 +971,7 @@ macro_rules! event {
         $crate::event!(
             target: module_path!(),
             $lvl,
-            { message = ::core::format_args!($($arg)+), $($fields)* }
+            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
         )
     );
     ($lvl:expr, $($k:ident).+ = $($field:tt)*) => (
@@ -2805,79 +2805,79 @@ macro_rules! valueset {
     // };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = ?$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = %$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+ = $val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$($k).+ as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$($k).+ as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, ?$($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$($k).+) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$($k).+) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, %$($k:ident).+) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$($k).+) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$($k).+) as &dyn Value)) },
             $next,
         )
     };
@@ -2885,40 +2885,40 @@ macro_rules! valueset {
     // Handle literal names
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr, $($rest:tt)*) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
             $next,
             $($rest)*
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = ?$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&debug(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&debug(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = %$val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&display(&$val) as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&display(&$val) as &dyn Value)) },
             $next,
         )
     };
     (@ { $(,)* $($out:expr),* }, $next:expr, $k:literal = $val:expr) => {
         $crate::valueset!(
-            @ { $($out),*, (&$next, ::core::option::Option::Some(&$val as &dyn Value)) },
+            @ { $($out),*, (&$next, $crate::__macro_support::Option::Some(&$val as &dyn Value)) },
             $next,
         )
     };
@@ -2966,7 +2966,7 @@ macro_rules! valueset {
 
     // Remainder is unparsable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* }, $next:expr, $($rest:tt)+) => {
-        $crate::valueset!(@ { (&$next, ::core::option::Option::Some(&::core::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
+        $crate::valueset!(@ { (&$next, $crate::__macro_support::Option::Some(&$crate::__macro_support::format_args!($($rest)+) as &dyn Value)), $($out),* }, $next, )
     };
 
     // === entry ===
@@ -2977,7 +2977,7 @@ macro_rules! valueset {
             let mut iter = $fields.iter();
             $fields.value_set($crate::valueset!(
                 @ { },
-                ::core::iter::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
+                $crate::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                 $($kvs)+
             ))
         }

From bfc859314f7ea82c39184270a3cb6aff069c21d2 Mon Sep 17 00:00:00 2001
From: Nathaniel Cook 
Date: Mon, 30 Oct 2023 10:17:54 -0600
Subject: [PATCH 063/101] subcriber: update docs for EnvFilter Builder (#2782)

The `from_env` and `try_from_env` methods on the builder had the same documentation. This change updates their docs to correctly describe their difference in behavior.


## Motivation

Make the docs more clear, so that users need not look at the source to understand the difference between these two functions.

## Solution

Updated the docs

Co-authored-by: Eliza Weisman 
---
 tracing-subscriber/src/filter/env/builder.rs | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs
index c814707e6c..ebb7b45e72 100644
--- a/tracing-subscriber/src/filter/env/builder.rs
+++ b/tracing-subscriber/src/filter/env/builder.rs
@@ -170,15 +170,16 @@ impl Builder {
         self.parse_lossy(var)
     }
 
-    /// Returns a new [`EnvFilter`] from the directives in the in the configured
-    /// environment variable, or an error if the environment variable is not set
-    /// or contains invalid directives.
+    /// Returns a new [`EnvFilter`] from the directives in the configured
+    /// environment variable. If the environment variable is unset, no directive is added.
+    ///
+    /// An error is returned if the environment contains invalid directives.
     pub fn from_env(&self) -> Result {
         let var = env::var(self.env_var_name()).unwrap_or_default();
         self.parse(var).map_err(Into::into)
     }
 
-    /// Returns a new [`EnvFilter`] from the directives in the in the configured
+    /// 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.
     pub fn try_from_env(&self) -> Result {

From e06201ca27eb5bb2b7574acf7a085e71ece821a9 Mon Sep 17 00:00:00 2001
From: Gabriel Goller 
Date: Mon, 30 Oct 2023 22:13:19 +0100
Subject: [PATCH 064/101] attributes: added missing RecordTypes for instrument
 (#2781)

When using a function annotated with `#[instrument]` it parses the
parameters of the function and records them either using `Value` or
using `std::fmt::Debug`. There were a few types that implement `Value`
but were missing the RecordTypes array. Added them + a unit test for a
single one.

Fixed: #2775
---
 tracing-attributes/src/expand.rs       | 5 +++++
 tracing-attributes/tests/instrument.rs | 8 +++++---
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index 196bd90a8e..a92ef58aee 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -422,10 +422,13 @@ impl RecordType {
         "i32",
         "u64",
         "i64",
+        "u128",
+        "i128",
         "f32",
         "f64",
         "usize",
         "isize",
+        "String",
         "NonZeroU8",
         "NonZeroI8",
         "NonZeroU16",
@@ -434,6 +437,8 @@ impl RecordType {
         "NonZeroI32",
         "NonZeroU64",
         "NonZeroI64",
+        "NonZeroU128",
+        "NonZeroI128",
         "NonZeroUsize",
         "NonZeroIsize",
         "Wrapping",
diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs
index a512051656..4c6b0290c0 100644
--- a/tracing-attributes/tests/instrument.rs
+++ b/tracing-attributes/tests/instrument.rs
@@ -51,7 +51,7 @@ fn override_everything() {
 #[test]
 fn fields() {
     #[instrument(target = "my_target", level = "debug")]
-    fn my_fn(arg1: usize, arg2: bool) {}
+    fn my_fn(arg1: usize, arg2: bool, arg3: String) {}
 
     let span = expect::span()
         .named("my_fn")
@@ -68,6 +68,7 @@ fn fields() {
                 expect::field("arg1")
                     .with_value(&2usize)
                     .and(expect::field("arg2").with_value(&false))
+                    .and(expect::field("arg3").with_value(&"Cool".to_string()))
                     .only(),
             ),
         )
@@ -79,6 +80,7 @@ fn fields() {
                 expect::field("arg1")
                     .with_value(&3usize)
                     .and(expect::field("arg2").with_value(&true))
+                    .and(expect::field("arg3").with_value(&"Still Cool".to_string()))
                     .only(),
             ),
         )
@@ -89,8 +91,8 @@ fn fields() {
         .run_with_handle();
 
     with_default(collector, || {
-        my_fn(2, false);
-        my_fn(3, true);
+        my_fn(2, false, "Cool".to_string());
+        my_fn(3, true, "Still Cool".to_string());
     });
 
     handle.assert_finished();

From 011ff82ee37829e6a3ece2a038011ae7825d2159 Mon Sep 17 00:00:00 2001
From: David Barsky 
Date: Mon, 6 Nov 2023 19:07:37 -0500
Subject: [PATCH 065/101] chore: bump MSRV to 1.63 (#2793)

---
 .github/workflows/CI.yml          | 13 ++-----------
 Cargo.toml                        |  2 +-
 README.md                         |  2 +-
 examples/Cargo.toml               |  2 +-
 tracing-attributes/Cargo.toml     |  2 +-
 tracing-attributes/README.md      |  4 ++--
 tracing-attributes/src/attr.rs    |  9 ++-------
 tracing-attributes/src/lib.rs     |  4 ++--
 tracing-core/Cargo.toml           |  2 +-
 tracing-core/README.md            |  4 ++--
 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        |  4 ++--
 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     |  2 +-
 tracing-subscriber/README.md      |  4 ++--
 tracing-subscriber/src/fmt/mod.rs |  2 +-
 tracing-subscriber/src/lib.rs     |  4 ++--
 tracing-tower/Cargo.toml          |  2 +-
 tracing/Cargo.toml                |  4 ++--
 tracing/README.md                 |  4 ++--
 tracing/src/lib.rs                |  4 ++--
 tracing/tests/macros.rs           |  4 ----
 41 files changed, 64 insertions(+), 82 deletions(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index a672bb28dd..7ec7579c09 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.56.0). This
+    # Run `cargo check` on our minimum supported Rust version (1.63.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.56.0
+        - 1.63.0
         - stable
     steps:
     - uses: actions/checkout@v3
@@ -279,9 +279,6 @@ jobs:
 
   test-features-stable:
     # Feature flag tests that run on stable Rust.
-    # TODO(david): once tracing's MSRV goes up to Rust 1.51, we should be able to switch to
-    # using cargo's V2 feature resolver (https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions)
-    # and avoid cd'ing into each crate's directory.
     name: cargo test (feature-specific)
     needs: check
     runs-on: ubuntu-latest
@@ -290,24 +287,18 @@ jobs:
     - uses: dtolnay/rust-toolchain@stable
     - name: "Test log support"
       run: cargo test
-      working-directory: "tracing/test-log-support"
     - name: "Test static max level"
       run: cargo test
-      working-directory: "tracing/test_static_max_level_features"
     - name: "Test static max level (release)"
       run: cargo test --release
-      working-directory: "tracing/test_static_max_level_features"
     - name: "Test tracing-core no-std support"
       run: cargo test --no-default-features
-      working-directory: tracing
     - name: "Test tracing no-std support"
       run: cargo test --no-default-features
-      working-directory: tracing
       # this skips running doctests under the `--no-default-features` flag,
       # as rustdoc isn't aware of cargo's feature flags.
     - name: "Test tracing-subscriber with all features disabled"
       run: cargo test --lib --tests --no-default-features
-      working-directory: tracing-subscriber
 
   # all required checks except for the main test run (which we only require
   # specific matrix combinations from)
diff --git a/Cargo.toml b/Cargo.toml
index d272eef50a..65bb8fd092 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,5 @@
 [workspace]
-
+resolver = "2"
 members = [
     "tracing",
     "tracing-core",
diff --git a/README.md b/README.md
index a31a7dbe54..62c2baf646 100644
--- a/README.md
+++ b/README.md
@@ -254,7 +254,7 @@ attachment that `Future::instrument` does.
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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/examples/Cargo.toml b/examples/Cargo.toml
index e8878eae15..f10a324c79 100644
--- a/examples/Cargo.toml
+++ b/examples/Cargo.toml
@@ -3,7 +3,7 @@ name = "tracing-examples"
 version = "0.0.0"
 publish = false
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = []
diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index 57dd7415b3..aa625ee37c 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -28,7 +28,7 @@ keywords = ["logging", "tracing", "macro", "instrument", "log"]
 license = "MIT"
 readme = "README.md"
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [lib]
 proc-macro = true
diff --git a/tracing-attributes/README.md b/tracing-attributes/README.md
index 976aafe305..0e9ad0f6ad 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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/attr.rs b/tracing-attributes/src/attr.rs
index a1854c7e80..902ca1450b 100644
--- a/tracing-attributes/src/attr.rs
+++ b/tracing-attributes/src/attr.rs
@@ -255,19 +255,14 @@ impl Parse for Skips {
     }
 }
 
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
+#[derive(Clone, Debug, Hash, PartialEq, Eq, Default)]
 pub(crate) enum FormatMode {
+    #[default]
     Default,
     Display,
     Debug,
 }
 
-impl Default for FormatMode {
-    fn default() -> Self {
-        FormatMode::Default
-    }
-}
-
 #[derive(Clone, Debug)]
 pub(crate) struct Fields(pub(crate) Punctuated);
 
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index ca3fe77d14..857e0798b6 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 363d8fad53..c56b59f262 100644
--- a/tracing-core/Cargo.toml
+++ b/tracing-core/Cargo.toml
@@ -24,7 +24,7 @@ categories = [
 ]
 keywords = ["logging", "tracing", "profiling"]
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["std"]
diff --git a/tracing-core/README.md b/tracing-core/README.md
index 24f0fb1783..14e714ca83 100644
--- a/tracing-core/README.md
+++ b/tracing-core/README.md
@@ -53,7 +53,7 @@ The crate provides:
 In addition, it defines the global callsite registry and per-thread current
 dispatcher which other components of the tracing system rely on.
 
-*Compiler support: [requires `rustc` 1.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
@@ -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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 d01b4a0c7a..7aafb01151 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -109,7 +109,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 7db6fb8837..10312ac292 100644
--- a/tracing-error/Cargo.toml
+++ b/tracing-error/Cargo.toml
@@ -32,7 +32,7 @@ keywords = [
     "backtrace"
 ]
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["traced-error"]
diff --git a/tracing-error/README.md b/tracing-error/README.md
index ae2721233f..c1d0e530a9 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 c6dba25fe3..19f1d6e65e 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -166,7 +166,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 2ea6d0b807..597aefe668 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.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["smallvec"]
diff --git a/tracing-flame/README.md b/tracing-flame/README.md
index 5a6b9017af..8e47bc09d8 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 8c37486533..36d2f9c844 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 f3b4dc1d61..9d77ac3366 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.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["std-future", "std"]
diff --git a/tracing-futures/README.md b/tracing-futures/README.md
index a7d3331a76..d16046358f 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 262d15fc87..0b1c810c60 100644
--- a/tracing-futures/src/lib.rs
+++ b/tracing-futures/src/lib.rs
@@ -15,7 +15,7 @@
 //! * [`WithCollector`] allows a `tracing` [collector] to be attached to a
 //!   future, sink, stream, or executor.
 //!
-//! *Compiler support: [requires `rustc` 1.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -60,7 +60,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 a2c0755283..ef22014c3d 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.56.0"
+rust-version = "1.63.0"
 
 [dependencies]
 libc = "0.2.126"
diff --git a/tracing-journald/README.md b/tracing-journald/README.md
index 6e6ec5942a..b7176b432d 100644
--- a/tracing-journald/README.md
+++ b/tracing-journald/README.md
@@ -28,7 +28,7 @@ scoped, structured, and async-aware diagnostics. `tracing-journald` provides a
 and events to [`systemd-journald`][journald], on Linux distributions that use
 `systemd`.
  
-*Compiler support: [requires `rustc` 1.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 34ed915bbb..02b12c3e82 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //! [`tracing`]: https://crates.io/crates/tracing
@@ -21,7 +21,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 61e68b376c..8e51f2561c 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.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["log-tracer", "std"]
diff --git a/tracing-log/README.md b/tracing-log/README.md
index 3da5f6b7e1..8ccad1c506 100644
--- a/tracing-log/README.md
+++ b/tracing-log/README.md
@@ -56,14 +56,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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 a822051baf..aff6d42fe1 100644
--- a/tracing-log/src/lib.rs
+++ b/tracing-log/src/lib.rs
@@ -16,7 +16,7 @@
 //! - An [`env_logger`] module, with helpers for using the [`env_logger` crate]
 //!   with `tracing` (optional, enabled by the `env-logger` feature).
 //!
-//! *Compiler support: [requires `rustc` 1.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -76,7 +76,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 b457067e26..3efd4e4374 100644
--- a/tracing-macros/Cargo.toml
+++ b/tracing-macros/Cargo.toml
@@ -15,7 +15,7 @@ categories = [
 ]
 keywords = ["logging", "tracing"]
 license = "MIT"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [dependencies]
 tracing = { path = "../tracing", version = "0.2", default-features = false, features = ["std"] }
diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml
index e041338b0a..6cec7406be 100644
--- a/tracing-mock/Cargo.toml
+++ b/tracing-mock/Cargo.toml
@@ -14,7 +14,7 @@ readme = "README.md"
 repository = "https://github.com/tokio-rs/tracing"
 homepage = "https://tokio.rs"
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 publish = false
 
 [dependencies]
diff --git a/tracing-mock/README.md b/tracing-mock/README.md
index e561606119..0682687f97 100644
--- a/tracing-mock/README.md
+++ b/tracing-mock/README.md
@@ -29,7 +29,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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
@@ -154,7 +154,7 @@ handle.assert_finished();
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 7b027fc5ac..8fb16eeac1 100644
--- a/tracing-serde/Cargo.toml
+++ b/tracing-serde/Cargo.toml
@@ -16,7 +16,7 @@ categories = [
     "encoding",
 ]
 keywords = ["logging", "tracing", "serialization"]
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["std"]
diff --git a/tracing-serde/README.md b/tracing-serde/README.md
index b52b9ae88a..7b24517b75 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
@@ -113,7 +113,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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 640c8d7886..67535b8514 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -128,7 +128,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 b73272ba1e..524bf4b482 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -20,7 +20,7 @@ categories = [
     "asynchronous",
 ]
 keywords = ["logging", "tracing", "metrics", "subscriber"]
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 
diff --git a/tracing-subscriber/README.md b/tracing-subscriber/README.md
index f1b7a03f2f..cc5426d04d 100644
--- a/tracing-subscriber/README.md
+++ b/tracing-subscriber/README.md
@@ -32,14 +32,14 @@ Utilities for implementing and composing [`tracing`][tracing] subscribers.
 [discord-url]: https://discord.gg/EeF3cQw
 [maint-badge]: https://img.shields.io/badge/maintenance-experimental-blue.svg
 
-*Compiler support: [requires `rustc` 1.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
 ## Supported Rust Versions
 
 Tracing is built against the latest stable release. The minimum supported
-version is 1.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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/fmt/mod.rs b/tracing-subscriber/src/fmt/mod.rs
index 036099211b..49e23e6007 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: ../index.html#supported-rust-versions
 //!
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index 90757df699..c64a40eedf 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 `Collector` authors and
 //! application authors using `tracing` to instrument their applications.
 //!
-//! *Compiler support: [requires `rustc` 1.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //!
@@ -106,7 +106,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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 970886ae84..9153e9bdb7 100644
--- a/tracing-tower/Cargo.toml
+++ b/tracing-tower/Cargo.toml
@@ -15,7 +15,7 @@ categories = [
 ]
 keywords = ["logging", "tracing"]
 license = "MIT"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [features]
 default = ["tower-layer", "tower-make"]
diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml
index 2d7bf1120e..f01783c293 100644
--- a/tracing/Cargo.toml
+++ b/tracing/Cargo.toml
@@ -28,7 +28,7 @@ categories = [
 ]
 keywords = ["logging", "tracing", "metrics", "async"]
 edition = "2018"
-rust-version = "1.56.0"
+rust-version = "1.63.0"
 
 [dependencies]
 tracing-core = { path = "../tracing-core", version = "0.2", default-features = false }
@@ -43,7 +43,7 @@ log = "0.4.17"
 tracing-mock = { path = "../tracing-mock" }
 
 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
-wasm-bindgen-test = "0.3.31"
+wasm-bindgen-test = "0.3.38"
 
 [features]
 default = ["std", "attributes"]
diff --git a/tracing/README.md b/tracing/README.md
index ff34b135d2..f5852b818d 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.56+][msrv]*
+*Compiler support: [requires `rustc` 1.63+][msrv]*
 
 [msrv]: #supported-rust-versions
 
@@ -427,7 +427,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.56. The current Tracing version is not guaranteed to build on Rust
+version is 1.63. 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 34aabca314..a2fcc25a31 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.56+][msrv]*
+//! *Compiler support: [requires `rustc` 1.63+][msrv]*
 //!
 //! [msrv]: #supported-rust-versions
 //! # Core Concepts
@@ -892,7 +892,7 @@
 //! ## Supported Rust Versions
 //!
 //! Tracing is built against the latest stable release. The minimum supported
-//! version is 1.56. The current Tracing version is not guaranteed to build on
+//! version is 1.63. 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/tests/macros.rs b/tracing/tests/macros.rs
index 81b929d1d3..b6e568f4a6 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -5,10 +5,6 @@ extern crate tracing;
 #[cfg(target_arch = "wasm32")]
 extern crate wasm_bindgen_test;
 
-// TODO: remove this once https://github.com/tokio-rs/tracing/pull/2675#issuecomment-1667628907 is resolved
-#[cfg(target_arch = "wasm32")]
-use ::core::option::Option::None;
-
 use tracing::{
     callsite, debug, debug_span, enabled, error, error_span, event, event_enabled, info, info_span,
     span, span_enabled, trace, trace_span, warn, warn_span, Level,

From 91ca0e03d80e26919da44124cc1435ad9e1c6903 Mon Sep 17 00:00:00 2001
From: Hayden Stainsby 
Date: Tue, 7 Nov 2023 11:36:18 +0100
Subject: [PATCH 066/101] test: add `tracing-test` crate for non-publishable
 test utils (#2466)

## Motivation

There has been interest around publishing tracing-mock to crates.io
for some time. In order to make this possible, it needs to be cleaned up.

## Solution

There are some test utils in the `tracing-mock` crate which wouldn't
make sense to publish. They provide test futures that are needed in
multiple `tracing-*` crates, but would likely not be needed outside that
context.

This change moves that functionality into a separate `tracing-test`
crate, which should never be published to crates.io.

Refs: #539

Co-authored-by: David Barsky 
---
 Cargo.toml                                    |  1 +
 tracing-attributes/Cargo.toml                 | 17 ++++-
 tracing-attributes/tests/async_fn.rs          |  5 +-
 tracing-attributes/tests/err.rs               |  1 +
 tracing-attributes/tests/follows_from.rs      |  3 +-
 tracing-attributes/tests/ret.rs               |  3 +-
 tracing-futures/Cargo.toml                    |  3 +-
 tracing-futures/tests/std_future.rs           |  3 +-
 tracing-mock/Cargo.toml                       |  1 -
 tracing-mock/src/lib.rs                       | 65 -------------------
 tracing-test/Cargo.toml                       | 26 ++++++++
 tracing-test/LICENSE                          | 25 +++++++
 tracing-test/README.md                        | 58 +++++++++++++++++
 tracing-test/src/lib.rs                       | 65 +++++++++++++++++++
 .../test_static_max_level_features/Cargo.toml |  2 +-
 .../tests/test.rs                             |  2 +-
 16 files changed, 203 insertions(+), 77 deletions(-)
 create mode 100644 tracing-test/Cargo.toml
 create mode 100644 tracing-test/LICENSE
 create mode 100644 tracing-test/README.md
 create mode 100644 tracing-test/src/lib.rs

diff --git a/Cargo.toml b/Cargo.toml
index 65bb8fd092..b83b1fcc47 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,6 +13,7 @@ members = [
     "tracing-mock",
     "tracing-subscriber",
     "tracing-serde",
+    "tracing-test",
     "tracing-appender",
     "tracing-journald",
     "examples"
diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml
index aa625ee37c..33d988a5cf 100644
--- a/tracing-attributes/Cargo.toml
+++ b/tracing-attributes/Cargo.toml
@@ -35,14 +35,25 @@ proc-macro = true
 
 [dependencies]
 proc-macro2 = "1.0.60"
-syn = { version = "2.0", default-features = false, features = ["full", "parsing", "printing", "visit-mut", "clone-impls", "extra-traits", "proc-macro"] }
+syn = { version = "2.0", default-features = false, features = [
+    "full",
+    "parsing",
+    "printing",
+    "visit-mut",
+    "clone-impls",
+    "extra-traits",
+    "proc-macro",
+] }
 quote = "1.0.20"
 
 [dev-dependencies]
 tracing = { path = "../tracing", version = "0.2" }
-tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] }
+tracing-mock = { path = "../tracing-mock" }
 tokio-test = "0.4.2"
-tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = ["env-filter"] }
+tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", features = [
+    "env-filter",
+] }
+tracing-test = { path = "../tracing-test" }
 async-trait = "0.1.67"
 trybuild = "1.0.64"
 rustversion = "1.0.9"
diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 568798d1f1..7c9e89b941 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -1,9 +1,10 @@
-use tracing_mock::*;
-
 use std::convert::Infallible;
 use std::{future::Future, pin::Pin, sync::Arc};
+
 use tracing::collect::with_default;
 use tracing_attributes::instrument;
+use tracing_mock::{collector, expect};
+use tracing_test::{block_on_future, PollN};
 
 #[instrument]
 async fn test_async_fn(polls: usize) -> Result<(), ()> {
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index 8777502b26..f4480c7b0b 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -4,6 +4,7 @@ use tracing_attributes::instrument;
 use tracing_mock::*;
 use tracing_subscriber::filter::EnvFilter;
 use tracing_subscriber::subscribe::CollectExt;
+use tracing_test::{block_on_future, PollN};
 
 use std::convert::TryFrom;
 use std::num::TryFromIntError;
diff --git a/tracing-attributes/tests/follows_from.rs b/tracing-attributes/tests/follows_from.rs
index acfec81f4b..65fe9e66c0 100644
--- a/tracing-attributes/tests/follows_from.rs
+++ b/tracing-attributes/tests/follows_from.rs
@@ -1,6 +1,7 @@
 use tracing::{collect::with_default, Id, Level, Span};
 use tracing_attributes::instrument;
-use tracing_mock::*;
+use tracing_mock::{collector, expect};
+use tracing_test::block_on_future;
 
 #[instrument(follows_from = causes, skip(causes))]
 fn with_follows_from_sync(causes: impl IntoIterator>>) {}
diff --git a/tracing-attributes/tests/ret.rs b/tracing-attributes/tests/ret.rs
index b5c4b4e07b..19d01c9584 100644
--- a/tracing-attributes/tests/ret.rs
+++ b/tracing-attributes/tests/ret.rs
@@ -1,11 +1,12 @@
 use std::convert::TryFrom;
 use std::num::TryFromIntError;
-use tracing_mock::*;
 
 use tracing::{collect::with_default, Level};
 use tracing_attributes::instrument;
+use tracing_mock::{collector, expect};
 use tracing_subscriber::subscribe::CollectExt;
 use tracing_subscriber::EnvFilter;
+use tracing_test::block_on_future;
 
 #[instrument(ret)]
 fn ret() -> i32 {
diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml
index 9d77ac3366..dc48f28eed 100644
--- a/tracing-futures/Cargo.toml
+++ b/tracing-futures/Cargo.toml
@@ -43,7 +43,8 @@ mio = "0.6.23"
 futures = "0.3.21"
 tokio-test = "0.4.2"
 tracing-core = { path = "../tracing-core", version = "0.2" }
-tracing-mock = { path = "../tracing-mock", features = ["tokio-test"] }
+tracing-mock = { path = "../tracing-mock" }
+tracing-test = { path = "../tracing-test" }
 
 [badges]
 maintenance = { status = "actively-developed" }
diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs
index ebba8cf084..d550896524 100644
--- a/tracing-futures/tests/std_future.rs
+++ b/tracing-futures/tests/std_future.rs
@@ -3,7 +3,8 @@ use std::{future::Future, pin::Pin, task};
 use futures::FutureExt as _;
 use tracing::Instrument;
 use tracing::{collect::with_default, Level};
-use tracing_mock::*;
+use tracing_mock::{collector, expect};
+use tracing_test::{block_on_future, PollN};
 
 #[test]
 fn enter_exit_is_reasonable() {
diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml
index 6cec7406be..22689f3f29 100644
--- a/tracing-mock/Cargo.toml
+++ b/tracing-mock/Cargo.toml
@@ -21,7 +21,6 @@ publish = false
 tracing = { path = "../tracing", version = "0.2" }
 tracing-core = { path = "../tracing-core", version = "0.2", default-features = false }
 tracing-subscriber = { path = "../tracing-subscriber", version = "0.3", default-features = false, features = ["registry"], optional = true }
-tokio-test = { version = "0.4.2", optional = true }
 
 # Fix minimal-versions; tokio-test fails with otherwise acceptable 0.1.0
 tokio-stream = "0.1.9"
diff --git a/tracing-mock/src/lib.rs b/tracing-mock/src/lib.rs
index 965b36a5e5..9fdeab5866 100644
--- a/tracing-mock/src/lib.rs
+++ b/tracing-mock/src/lib.rs
@@ -1,9 +1,4 @@
 #![doc = include_str!("../README.md")]
-use std::{
-    pin::Pin,
-    task::{Context, Poll},
-};
-
 pub mod collector;
 pub mod event;
 pub mod expect;
@@ -22,12 +17,6 @@ pub enum Parent {
     Explicit(String),
 }
 
-pub struct PollN {
-    and_return: Option>,
-    finish_at: usize,
-    polls: usize,
-}
-
 impl Parent {
     pub fn check_parent_name(
         &self,
@@ -104,57 +93,3 @@ impl Parent {
         }
     }
 }
-
-impl std::future::Future for PollN
-where
-    T: Unpin,
-    E: Unpin,
-{
-    type Output = Result;
-    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll {
-        let this = self.get_mut();
-
-        this.polls += 1;
-        if this.polls == this.finish_at {
-            let value = this.and_return.take().expect("polled after ready");
-
-            Poll::Ready(value)
-        } else {
-            cx.waker().wake_by_ref();
-            Poll::Pending
-        }
-    }
-}
-
-impl PollN<(), ()> {
-    pub fn new_ok(finish_at: usize) -> Self {
-        Self {
-            and_return: Some(Ok(())),
-            finish_at,
-            polls: 0,
-        }
-    }
-
-    pub fn new_err(finish_at: usize) -> Self {
-        Self {
-            and_return: Some(Err(())),
-            finish_at,
-            polls: 0,
-        }
-    }
-}
-
-#[cfg(feature = "tokio-test")]
-pub fn block_on_future(future: F) -> F::Output
-where
-    F: std::future::Future,
-{
-    use tokio_test::task;
-
-    let mut task = task::spawn(future);
-    loop {
-        if let Poll::Ready(v) = task.poll() {
-            break v;
-        }
-    }
-}
diff --git a/tracing-test/Cargo.toml b/tracing-test/Cargo.toml
new file mode 100644
index 0000000000..40fd7c789c
--- /dev/null
+++ b/tracing-test/Cargo.toml
@@ -0,0 +1,26 @@
+## BIG SCARY NOTE
+# This crate is internal and to be used for testing only. It should not
+# be published to crates.io ever. If the functionality is needed outside
+# the tracing project, it should be moved back to tracing-mock.
+
+[package]
+name = "tracing-test"
+version = "0.1.0"
+authors = [
+    "Eliza Weisman ",
+    "Tokio Contributors ",
+]
+license = "MIT"
+readme = "README.md"
+repository = "https://github.com/tokio-rs/tracing"
+homepage = "https://tokio.rs"
+edition = "2018"
+rust-version = "1.49.0"
+publish = false
+
+[dependencies]
+tokio-test = "0.4.2"
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/tracing-test/LICENSE b/tracing-test/LICENSE
new file mode 100644
index 0000000000..cdb28b4b56
--- /dev/null
+++ b/tracing-test/LICENSE
@@ -0,0 +1,25 @@
+Copyright (c) 2019 Tokio Contributors
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/tracing-test/README.md b/tracing-test/README.md
new file mode 100644
index 0000000000..6cef11e90b
--- /dev/null
+++ b/tracing-test/README.md
@@ -0,0 +1,58 @@
+![Tracing — Structured, application-level diagnostics][splash]
+
+[splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg
+
+# tracing-test
+
+Utilities for testing [`tracing`][tracing] and crates that uses it.
+
+[![Documentation (master)][docs-master-badge]][docs-master-url]
+[![MIT licensed][mit-badge]][mit-url]
+[![Build Status][actions-badge]][actions-url]
+[![Discord chat][discord-badge]][discord-url]
+
+[Documentation][docs-master-url] | [Chat][discord-url]
+
+[docs-master-badge]: https://img.shields.io/badge/docs-master-blue
+[docs-master-url]: https://tracing-rs.netlify.com/tracing_mock
+[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
+[mit-url]: https://github.com/tokio-rs/tracing/blob/master/tracing-test/LICENSE
+[actions-badge]: https://github.com/tokio-rs/tracing/workflows/CI/badge.svg
+[actions-url]:https://github.com/tokio-rs/tracing/actions?query=workflow%3ACI
+[discord-badge]: https://img.shields.io/discord/500028886025895936?logo=discord&label=discord&logoColor=white
+[discord-url]: https://discord.gg/EeF3cQw
+
+## Overview
+
+[`tracing`] is a framework for instrumenting Rust programs to collect
+structured, event-based diagnostic information. `tracing-test` provides
+some reusable tools to aid in testing, but that are only intended for
+internal use. For mocks and expectations, see [`tracing-mock`].
+
+*Compiler support: [requires `rustc` 1.56+][msrv]*
+
+[msrv]: #supported-rust-versions
+
+## Supported Rust Versions
+
+Tracing is built against the latest stable release. The minimum supported
+version is 1.56. 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
+project. The current stable Rust compiler and the three most recent minor
+versions before it will always be supported. For example, if the current stable
+compiler version is 1.45, the minimum supported version will not be increased
+past 1.42, three minor versions prior. Increasing the minimum supported compiler
+version is not considered a semver breaking change as long as doing so complies
+with this policy.
+
+## License
+
+This project is licensed under the [MIT license][mit-url].
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in Tracing by you, shall be licensed as MIT, without any additional
+terms or conditions.
\ No newline at end of file
diff --git a/tracing-test/src/lib.rs b/tracing-test/src/lib.rs
new file mode 100644
index 0000000000..d89186a066
--- /dev/null
+++ b/tracing-test/src/lib.rs
@@ -0,0 +1,65 @@
+use std::{
+    pin::Pin,
+    task::{Context, Poll},
+};
+
+#[allow(missing_docs)]
+
+pub struct PollN {
+    and_return: Option>,
+    finish_at: usize,
+    polls: usize,
+}
+
+impl std::future::Future for PollN
+where
+    T: Unpin,
+    E: Unpin,
+{
+    type Output = Result;
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll {
+        let this = self.get_mut();
+
+        this.polls += 1;
+        if this.polls == this.finish_at {
+            let value = this.and_return.take().expect("polled after ready");
+
+            Poll::Ready(value)
+        } else {
+            cx.waker().wake_by_ref();
+            Poll::Pending
+        }
+    }
+}
+
+impl PollN<(), ()> {
+    pub fn new_ok(finish_at: usize) -> Self {
+        Self {
+            and_return: Some(Ok(())),
+            finish_at,
+            polls: 0,
+        }
+    }
+
+    pub fn new_err(finish_at: usize) -> Self {
+        Self {
+            and_return: Some(Err(())),
+            finish_at,
+            polls: 0,
+        }
+    }
+}
+
+pub fn block_on_future(future: F) -> F::Output
+where
+    F: std::future::Future,
+{
+    use tokio_test::task;
+
+    let mut task = task::spawn(future);
+    loop {
+        if let Poll::Ready(v) = task.poll() {
+            break v;
+        }
+    }
+}
diff --git a/tracing/test_static_max_level_features/Cargo.toml b/tracing/test_static_max_level_features/Cargo.toml
index 89edfd506b..44271a8974 100644
--- a/tracing/test_static_max_level_features/Cargo.toml
+++ b/tracing/test_static_max_level_features/Cargo.toml
@@ -19,4 +19,4 @@ features = ["max_level_debug", "release_max_level_info"]
 
 [dev-dependencies]
 tokio-test = "0.2.0"
-tracing-mock = { path = "../../tracing-mock", features = ["tokio-test"] }
+tracing-test = { path = "../../tracing-test" }
diff --git a/tracing/test_static_max_level_features/tests/test.rs b/tracing/test_static_max_level_features/tests/test.rs
index f4c6e12036..71e629cb62 100644
--- a/tracing/test_static_max_level_features/tests/test.rs
+++ b/tracing/test_static_max_level_features/tests/test.rs
@@ -6,7 +6,7 @@ use tracing::{
     debug, error, info, instrument, span, trace, warn, Collect, Event, Id, Level, Metadata,
 };
 use tracing_core::span::Current;
-use tracing_mock::*;
+use tracing_test::block_on_future;
 
 struct State {
     last_level: Mutex>,

From ad2bfaab8781a56d3ecf0b0a896dd178325327a2 Mon Sep 17 00:00:00 2001
From: Hayden Stainsby 
Date: Tue, 14 Nov 2023 10:57:14 +0100
Subject: [PATCH 067/101] mock: document public APIs in `span` module (#2442)

This change adds documentation to the tracing-mock span module and all
the public APIs within it. This includes doctests on all the methods
which serve as examples.

Additionally, the validation on `ExpectedSpan` was improved so that it
validates the level and target during `enter` and `exit` as well as on
`new_span`.

The method `ExpectedSpan::with_field` was renamed to `with_fields`
(plural) to match the same method on `ExpectedEvent` (and because
multiple fields can be passed to it).

A copy-paste typo was also fixed in the documentation for
`ExpectedEvent::with_contextual_parent`.

Refs: #539

Co-authored-by: David Barsky 
---
 tracing-attributes/tests/async_fn.rs          |  16 +-
 tracing-attributes/tests/destructuring.rs     |  12 +-
 tracing-attributes/tests/err.rs               |   2 +-
 tracing-attributes/tests/fields.rs            |  18 +-
 tracing-attributes/tests/instrument.rs        |  14 +-
 tracing-mock/README.md                        |   2 +-
 tracing-mock/src/collector.rs                 |  20 +-
 tracing-mock/src/event.rs                     |   2 +-
 tracing-mock/src/span.rs                      | 551 +++++++++++++++++-
 tracing-mock/src/subscriber.rs                |   4 +-
 tracing-subscriber/tests/env_filter/main.rs   |   4 +-
 .../tests/env_filter/per_subscriber.rs        |   4 +-
 tracing-subscriber/tests/same_len_filters.rs  |   4 +-
 tracing/tests/collector.rs                    |   4 +-
 tracing/tests/span.rs                         |  26 +-
 15 files changed, 601 insertions(+), 82 deletions(-)

diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs
index 7c9e89b941..60d772ebd3 100644
--- a/tracing-attributes/tests/async_fn.rs
+++ b/tracing-attributes/tests/async_fn.rs
@@ -197,8 +197,8 @@ fn async_fn_with_async_trait() {
     let (collector, handle) = collector::mock()
         .new_span(
             span.clone()
-                .with_field(expect::field("self"))
-                .with_field(expect::field("v")),
+                .with_fields(expect::field("self"))
+                .with_fields(expect::field("v")),
         )
         .enter(span.clone())
         .new_span(span3.clone())
@@ -208,7 +208,7 @@ fn async_fn_with_async_trait() {
         .enter(span3.clone())
         .exit(span3.clone())
         .drop_span(span3)
-        .new_span(span2.clone().with_field(expect::field("self")))
+        .new_span(span2.clone().with_fields(expect::field("self")))
         .enter(span2.clone())
         .event(expect::event().with_fields(expect::field("val").with_value(&5u64)))
         .exit(span2.clone())
@@ -258,7 +258,7 @@ fn async_fn_with_async_trait_and_fields_expressions() {
     let span = expect::span().named("call");
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("_v")
                     .with_value(&5usize)
                     .and(expect::field("test").with_value(&tracing::field::debug(10)))
@@ -328,7 +328,7 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
     let span4 = expect::span().named("sync_fun");
     let (collector, handle) = collector::mock()
         /*.new_span(span.clone()
-            .with_field(
+            .with_fields(
                 expect::field("Self").with_value(&"TestImpler")))
         .enter(span.clone())
         .exit(span.clone())
@@ -336,13 +336,13 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
         .new_span(
             span2
                 .clone()
-                .with_field(expect::field("Self").with_value(&std::any::type_name::())),
+                .with_fields(expect::field("Self").with_value(&std::any::type_name::())),
         )
         .enter(span2.clone())
         .new_span(
             span4
                 .clone()
-                .with_field(expect::field("Self").with_value(&std::any::type_name::())),
+                .with_fields(expect::field("Self").with_value(&std::any::type_name::())),
         )
         .enter(span4.clone())
         .exit(span4.clone())
@@ -355,7 +355,7 @@ fn async_fn_with_async_trait_and_fields_expressions_with_generic_parameter() {
         .new_span(
             span3
                 .clone()
-                .with_field(expect::field("Self").with_value(&std::any::type_name::())),
+                .with_fields(expect::field("Self").with_value(&std::any::type_name::())),
         )
         .enter(span3.clone())
         .exit(span3.clone())
diff --git a/tracing-attributes/tests/destructuring.rs b/tracing-attributes/tests/destructuring.rs
index 3d7c88e8b6..6efccba31b 100644
--- a/tracing-attributes/tests/destructuring.rs
+++ b/tracing-attributes/tests/destructuring.rs
@@ -11,7 +11,7 @@ fn destructure_tuples() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("1"))
                     .and(expect::field("arg2").with_value(&format_args!("2")))
@@ -40,7 +40,7 @@ fn destructure_nested_tuples() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("1"))
                     .and(expect::field("arg2").with_value(&format_args!("2")))
@@ -72,7 +72,7 @@ fn destructure_refs() {
     let (collector, handle) = collector::mock()
         .new_span(
             span.clone()
-                .with_field(expect::field("arg1").with_value(&1usize).only()),
+                .with_fields(expect::field("arg1").with_value(&1usize).only()),
         )
         .enter(span.clone())
         .exit(span.clone())
@@ -98,7 +98,7 @@ fn destructure_tuple_structs() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("1"))
                     .and(expect::field("arg2").with_value(&format_args!("2")))
@@ -139,7 +139,7 @@ fn destructure_structs() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("1"))
                     .and(expect::field("arg2").with_value(&format_args!("2")))
@@ -184,7 +184,7 @@ fn destructure_everything() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("1"))
                     .and(expect::field("arg2").with_value(&format_args!("2")))
diff --git a/tracing-attributes/tests/err.rs b/tracing-attributes/tests/err.rs
index f4480c7b0b..3ff9f056ab 100644
--- a/tracing-attributes/tests/err.rs
+++ b/tracing-attributes/tests/err.rs
@@ -160,7 +160,7 @@ fn impl_trait_return_type() {
     let (collector, handle) = collector::mock()
         .new_span(
             span.clone()
-                .with_field(expect::field("x").with_value(&10usize).only()),
+                .with_fields(expect::field("x").with_value(&10usize).only()),
         )
         .enter(span.clone())
         .exit(span.clone())
diff --git a/tracing-attributes/tests/fields.rs b/tracing-attributes/tests/fields.rs
index b7b6fbeb1c..476d172482 100644
--- a/tracing-attributes/tests/fields.rs
+++ b/tracing-attributes/tests/fields.rs
@@ -46,7 +46,7 @@ impl HasField {
 
 #[test]
 fn fields() {
-    let span = expect::span().with_field(
+    let span = expect::span().with_fields(
         expect::field("foo")
             .with_value(&"bar")
             .and(expect::field("dsa").with_value(&true))
@@ -60,7 +60,7 @@ fn fields() {
 
 #[test]
 fn expr_field() {
-    let span = expect::span().with_field(
+    let span = expect::span().with_fields(
         expect::field("s")
             .with_value(&"hello world")
             .and(expect::field("len").with_value(&"hello world".len()))
@@ -73,7 +73,7 @@ fn expr_field() {
 
 #[test]
 fn two_expr_fields() {
-    let span = expect::span().with_field(
+    let span = expect::span().with_fields(
         expect::field("s")
             .with_value(&"hello world")
             .and(expect::field("s.len").with_value(&"hello world".len()))
@@ -87,7 +87,7 @@ fn two_expr_fields() {
 
 #[test]
 fn clashy_expr_field() {
-    let span = expect::span().with_field(
+    let span = expect::span().with_fields(
         // Overriding the `s` field should record `s` as a `Display` value,
         // rather than as a `Debug` value.
         expect::field("s")
@@ -99,7 +99,7 @@ fn clashy_expr_field() {
         fn_clashy_expr_field("hello world");
     });
 
-    let span = expect::span().with_field(expect::field("s").with_value(&"s").only());
+    let span = expect::span().with_fields(expect::field("s").with_value(&"s").only());
     run_test(span, || {
         fn_clashy_expr_field2("hello world");
     });
@@ -108,7 +108,7 @@ fn clashy_expr_field() {
 #[test]
 fn self_expr_field() {
     let span =
-        expect::span().with_field(expect::field("my_field").with_value(&"hello world").only());
+        expect::span().with_fields(expect::field("my_field").with_value(&"hello world").only());
     run_test(span, || {
         let has_field = HasField {
             my_field: "hello world",
@@ -119,7 +119,7 @@ fn self_expr_field() {
 
 #[test]
 fn parameters_with_fields() {
-    let span = expect::span().with_field(
+    let span = expect::span().with_fields(
         expect::field("foo")
             .with_value(&"bar")
             .and(expect::field("param").with_value(&1u32))
@@ -132,7 +132,7 @@ fn parameters_with_fields() {
 
 #[test]
 fn empty_field() {
-    let span = expect::span().with_field(expect::field("foo").with_value(&"bar").only());
+    let span = expect::span().with_fields(expect::field("foo").with_value(&"bar").only());
     run_test(span, || {
         fn_empty_field();
     });
@@ -140,7 +140,7 @@ fn empty_field() {
 
 #[test]
 fn string_field() {
-    let span = expect::span().with_field(expect::field("s").with_value(&"hello world").only());
+    let span = expect::span().with_fields(expect::field("s").with_value(&"hello world").only());
     run_test(span, || {
         fn_string(String::from("hello world"));
     });
diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs
index 4c6b0290c0..0376be4069 100644
--- a/tracing-attributes/tests/instrument.rs
+++ b/tracing-attributes/tests/instrument.rs
@@ -64,7 +64,7 @@ fn fields() {
         .with_target("my_target");
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&2usize)
                     .and(expect::field("arg2").with_value(&false))
@@ -76,7 +76,7 @@ fn fields() {
         .exit(span.clone())
         .drop_span(span)
         .new_span(
-            span2.clone().with_field(
+            span2.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&3usize)
                     .and(expect::field("arg2").with_value(&true))
@@ -117,7 +117,7 @@ fn skip() {
     let (collector, handle) = collector::mock()
         .new_span(
             span.clone()
-                .with_field(expect::field("arg1").with_value(&2usize).only()),
+                .with_fields(expect::field("arg1").with_value(&2usize).only()),
         )
         .enter(span.clone())
         .exit(span.clone())
@@ -125,7 +125,7 @@ fn skip() {
         .new_span(
             span2
                 .clone()
-                .with_field(expect::field("arg1").with_value(&3usize).only()),
+                .with_fields(expect::field("arg1").with_value(&3usize).only()),
         )
         .enter(span2.clone())
         .exit(span2.clone())
@@ -157,7 +157,7 @@ fn generics() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("arg1")
                     .with_value(&format_args!("Foo"))
                     .and(expect::field("arg2").with_value(&format_args!("false"))),
@@ -190,7 +190,7 @@ fn methods() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            span.clone().with_field(
+            span.clone().with_fields(
                 expect::field("self")
                     .with_value(&format_args!("Foo"))
                     .and(expect::field("arg1").with_value(&42usize)),
@@ -222,7 +222,7 @@ fn impl_trait_return_type() {
     let (collector, handle) = collector::mock()
         .new_span(
             span.clone()
-                .with_field(expect::field("x").with_value(&10usize).only()),
+                .with_fields(expect::field("x").with_value(&10usize).only()),
         )
         .enter(span.clone())
         .exit(span.clone())
diff --git a/tracing-mock/README.md b/tracing-mock/README.md
index 0682687f97..52703a14d9 100644
--- a/tracing-mock/README.md
+++ b/tracing-mock/README.md
@@ -121,7 +121,7 @@ let span = expect::span().named("yak_shaving");
 let (collector, handle) = collector::mock()
     .new_span(
         span.clone()
-            .with_field(expect::field("number_of_yaks").with_value(&yak_count).only()),
+            .with_fields(expect::field("number_of_yaks").with_value(&yak_count).only()),
     )
     .enter(span.clone())
     .event(
diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs
index 3298bfb22f..d1f05a24eb 100644
--- a/tracing-mock/src/collector.rs
+++ b/tracing-mock/src/collector.rs
@@ -158,12 +158,18 @@ use tracing::{
     Collect, Event, Metadata,
 };
 
-struct SpanState {
+pub(crate) struct SpanState {
     name: &'static str,
     refs: usize,
     meta: &'static Metadata<'static>,
 }
 
+impl SpanState {
+    pub(crate) fn metadata(&self) -> &'static Metadata<'static> {
+        self.meta
+    }
+}
+
 struct Running) -> bool> {
     spans: Mutex>,
     expected: Arc>>,
@@ -399,7 +405,7 @@ where
     /// let span = expect::span()
     ///     .at_level(tracing::Level::INFO)
     ///     .named("the span we're testing")
-    ///     .with_field(expect::field("testing").with_value(&"yes"));
+    ///     .with_fields(expect::field("testing").with_value(&"yes"));
     /// let (collector, handle) = collector::mock()
     ///     .new_span(span)
     ///     .run_with_handle();
@@ -420,7 +426,7 @@ where
     /// let span = expect::span()
     ///     .at_level(tracing::Level::INFO)
     ///     .named("the span we're testing")
-    ///     .with_field(expect::field("testing").with_value(&"yes"));
+    ///     .with_fields(expect::field("testing").with_value(&"yes"));
     /// let (collector, handle) = collector::mock()
     ///     .new_span(span)
     ///     .run_with_handle();
@@ -1122,9 +1128,7 @@ where
             match self.expected.lock().unwrap().pop_front() {
                 None => {}
                 Some(Expect::Enter(ref expected_span)) => {
-                    if let Some(name) = expected_span.name() {
-                        assert_eq!(name, span.name);
-                    }
+                    expected_span.check(span, &self.name);
                 }
                 Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name)),
             }
@@ -1147,9 +1151,7 @@ where
         match self.expected.lock().unwrap().pop_front() {
             None => {}
             Some(Expect::Exit(ref expected_span)) => {
-                if let Some(name) = expected_span.name() {
-                    assert_eq!(name, span.name);
-                }
+                expected_span.check(span, &self.name);
                 let curr = self.current.lock().unwrap().pop();
                 assert_eq!(
                     Some(id),
diff --git a/tracing-mock/src/event.rs b/tracing-mock/src/event.rs
index 497b9e9aa8..2a732dc69c 100644
--- a/tracing-mock/src/event.rs
+++ b/tracing-mock/src/event.rs
@@ -364,7 +364,7 @@ impl ExpectedEvent {
     ///
     /// # Examples
     ///
-    /// The explicit parent is matched by name:
+    /// The contextual parent is matched by name:
     ///
     /// ```
     /// use tracing::collect::with_default;
diff --git a/tracing-mock/src/span.rs b/tracing-mock/src/span.rs
index 4f61f98038..42ed3a603e 100644
--- a/tracing-mock/src/span.rs
+++ b/tracing-mock/src/span.rs
@@ -1,16 +1,128 @@
+//! Define expectations to match and validate spans.
+//!
+//! The [`ExpectedSpan`] and [`NewSpan`] structs define expectations
+//! for spans to be matched by the mock collector API in the
+//! [`collector`] module.
+//!
+//! Expected spans should be created with [`expect::span`] and a
+//! chain of method calls describing the assertions made about the
+//! span. Expectations about the lifecycle of the span can be set on the [`MockCollector`].
+//!
+//! # Examples
+//!
+//! ```
+//! use tracing_mock::{collector, expect};
+//!
+//! let span = expect::span()
+//!     .named("interesting_span")
+//!     .at_level(tracing::Level::INFO);
+//!
+//! let (collector, handle) = collector::mock()
+//!     .enter(span.clone())
+//!     .exit(span)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!    let span = tracing::info_span!("interesting_span");
+//!     let _guard = span.enter();
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! The following example asserts the name, level, parent, and fields of the span:
+//!
+//! ```
+//! use tracing_mock::{collector, expect};
+//!
+//! let span = expect::span()
+//!     .named("interesting_span")
+//!     .at_level(tracing::Level::INFO);
+//! let new_span = span
+//!     .clone()
+//!     .with_fields(expect::field("field.name").with_value(&"field_value"))
+//!     .with_explicit_parent(Some("parent_span"));
+//!
+//! let (collector, handle) = collector::mock()
+//!     .new_span(expect::span().named("parent_span"))
+//!     .new_span(new_span)
+//!     .enter(span.clone())
+//!     .exit(span)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!     let parent = tracing::info_span!("parent_span");
+//!
+//!     let span = tracing::info_span!(
+//!         parent: parent.id(),
+//!         "interesting_span",
+//!         field.name = "field_value",
+//!     );
+//!     let _guard = span.enter();
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! All expectations must be met for the test to pass. For example,
+//! the following test will fail due to a mismatch in the spans' names:
+//!
+//! ```should_panic
+//! use tracing_mock::{collector, expect};
+//!
+//! let span = expect::span()
+//!     .named("interesting_span")
+//!     .at_level(tracing::Level::INFO);
+//!
+//! let (collector, handle) = collector::mock()
+//!     .enter(span.clone())
+//!     .exit(span)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!    let span = tracing::info_span!("another_span");
+//!    let _guard = span.enter();
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! [`MockCollector`]: struct@crate::collector::MockCollector
+//! [`collector`]: mod@crate::collector
+//! [`expect::span`]: fn@crate::expect::span
 #![allow(missing_docs)]
-use super::{expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent};
+use crate::{
+    collector::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent,
+};
 use std::fmt;
 
 /// A mock span.
 ///
-/// This is intended for use with the mock subscriber API in the
-/// `subscriber` module.
+/// This is intended for use with the mock collector API in the
+/// [`collector`] module.
+///
+/// [`collector`]: mod@crate::collector
 #[derive(Clone, Default, Eq, PartialEq)]
 pub struct ExpectedSpan {
     pub(crate) metadata: ExpectedMetadata,
 }
 
+/// A mock new span.
+///
+/// **Note**: This struct contains expectations that can only be asserted
+/// on when expecting a new span via [`MockCollector::new_span`]. They
+/// cannot be validated on [`MockCollector::enter`],
+/// [`MockCollector::exit`], or any other method on [`MockCollector`]
+/// that takes an `ExpectedSpan`.
+///
+/// For more details on how to use this struct, see the documentation
+/// on the [`collector`] module.
+///
+/// [`collector`]: mod@crate::collector
+/// [`MockCollector`]: struct@crate::collector::MockCollector
+/// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
+/// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
+/// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
 #[derive(Default, Eq, PartialEq)]
 pub struct NewSpan {
     pub(crate) span: ExpectedSpan,
@@ -26,6 +138,47 @@ where
 }
 
 impl ExpectedSpan {
+    /// Sets a name to expect when matching a span.
+    ///
+    /// If an event is recorded with a name that differs from the one provided to this method, the expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span().named("span name");
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::info_span!("span name");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// When the span name is different, the assertion will fail:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span().named("span name");
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::info_span!("a different span name");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn named(self, name: I) -> Self
     where
         I: Into,
@@ -38,6 +191,50 @@ impl ExpectedSpan {
         }
     }
 
+    /// Sets the [`Level`](tracing::Level) to expect when matching a span.
+    ///
+    /// If an span is record with a level that differs from the one provided to this method, the expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .at_level(tracing::Level::INFO);
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::info_span!("span");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// Expecting a span at `INFO` level will fail if the event is
+    /// recorded at any other level:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .at_level(tracing::Level::INFO);
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::warn_span!("a serious span");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn at_level(self, level: tracing::Level) -> Self {
         Self {
             metadata: ExpectedMetadata {
@@ -47,6 +244,50 @@ impl ExpectedSpan {
         }
     }
 
+    /// Sets the target to expect when matching a span.
+    ///
+    /// If an event is recorded with a target that doesn't match the
+    /// provided target, this expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_target("some_target");
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::info_span!(target: "some_target", "span");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// The test will fail if the target is different:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_target("some_target");
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .enter(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let span = tracing::info_span!(target: "a_different_target", "span");
+    ///     let _guard = span.enter();
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn with_target(self, target: I) -> Self
     where
         I: Into,
@@ -59,6 +300,100 @@ impl ExpectedSpan {
         }
     }
 
+    /// Configures this `ExpectedSpan` to expect an explicit parent
+    /// span or to be an explicit root.
+    ///
+    /// **Note**: This method returns a [`NewSpan`] and as such, this
+    /// expectation can only be validated when expecting a new span via
+    /// [`MockCollector::new_span`]. It cannot be validated on
+    /// [`MockCollector::enter`], [`MockCollector::exit`], or any other
+    /// method on [`MockCollector`] that takes an `ExpectedSpan`.
+    ///
+    /// An _explicit_ parent span is one passed to the `span!` macro in the
+    /// `parent:` field.
+    ///
+    /// If `Some("parent_name")` is passed to `with_explicit_parent` then,
+    /// the provided string is the name of the parent span to expect.
+    ///
+    /// To expect that a span is recorded with no parent, `None`
+    /// can be passed to `with_explicit_parent` instead.
+    ///
+    /// If a span is recorded without an explicit parent, or if the
+    /// explicit parent has a different name, this expectation will
+    /// fail.
+    ///
+    /// # Examples
+    ///
+    /// The explicit parent is matched by name:
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_explicit_parent(Some("parent_span"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(expect::span().named("parent_span"))
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let parent = tracing::info_span!("parent_span");
+    ///     tracing::info_span!(parent: parent.id(), "span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// In the following example, the expected span is an explicit root:
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_explicit_parent(None);
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info_span!(parent: None, "span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// In the example below, the expectation fails because the
+    /// span is *contextually*—as opposed to explicitly—within the span
+    /// `parent_span`:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let parent_span = expect::span().named("parent_span");
+    /// let span = expect::span()
+    ///     .with_explicit_parent(Some("parent_span"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(parent_span.clone())
+    ///     .enter(parent_span)
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let parent = tracing::info_span!("parent_span");
+    ///     let _guard = parent.enter();
+    ///     tracing::info_span!("span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// [`MockCollector`]: struct@crate::collector::MockCollector
+    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
+    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
+    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
     pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
         let parent = match parent {
             Some(name) => Parent::Explicit(name.into()),
@@ -71,6 +406,99 @@ impl ExpectedSpan {
         }
     }
 
+    /// Configures this `ExpectedSpan` to expect a
+    /// contextually-determined parent span, or be a contextual
+    /// root.
+    ///
+    /// **Note**: This method returns a [`NewSpan`] and as such, this
+    /// expectation can only be validated when expecting a new span via
+    /// [`MockCollector::new_span`]. It cannot be validated on
+    /// [`MockCollector::enter`], [`MockCollector::exit`], or any other
+    /// method on [`MockCollector`] that takes an `ExpectedSpan`.
+    ///
+    /// The provided string is the name of the parent span to expect.
+    /// To expect that the event is a contextually-determined root, pass
+    /// `None` instead.
+    ///
+    /// To expect a span with an explicit parent span, use
+    /// [`ExpectedSpan::with_explicit_parent`].
+    ///
+    /// If a span is recorded which is not inside a span, has an explicitly
+    /// overridden parent span, or has a differently-named span as its
+    /// parent, this expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// The contextual parent is matched by name:
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let parent_span = expect::span().named("parent_span");
+    /// let span = expect::span()
+    ///     .with_contextual_parent(Some("parent_span"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(parent_span.clone())
+    ///     .enter(parent_span)
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let parent = tracing::info_span!("parent_span");
+    ///     let _guard = parent.enter();
+    ///     tracing::info_span!("span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// In the following example, we expect that the matched span is
+    /// a contextually-determined root:
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_contextual_parent(None);
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info_span!("span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// In the example below, the expectation fails because the
+    /// span is recorded with an explicit parent:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_contextual_parent(Some("parent_span"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(expect::span().named("parent_span"))
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let parent = tracing::info_span!("parent_span");
+    ///     tracing::info_span!(parent: parent.id(), "span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// [`MockCollector`]: struct@crate::collector::MockCollector
+    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
+    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
+    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
     pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
         let parent = match parent {
             Some(name) => Parent::Contextual(name.into()),
@@ -83,27 +511,95 @@ impl ExpectedSpan {
         }
     }
 
-    pub fn name(&self) -> Option<&str> {
+    /// Adds fields to expect when matching a span.
+    ///
+    /// **Note**: This method returns a [`NewSpan`] and as such, this
+    /// expectation can only be validated when expecting a new span via
+    /// [`MockCollector::new_span`]. It cannot be validated on
+    /// [`MockCollector::enter`], [`MockCollector::exit`], or any other
+    /// method on [`MockCollector`] that takes an `ExpectedSpan`.
+    ///
+    /// If a span is recorded with fields that do not match the provided
+    /// [`ExpectedFields`], this expectation will fail.
+    ///
+    /// If the provided field is not present on the recorded span or
+    /// if the value for that field diffs, then the expectation
+    /// will fail.
+    ///
+    /// More information on the available validations is available in
+    /// the [`ExpectedFields`] documentation.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info_span!("span", field.name = "field_value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// A different field value will cause the expectation to fail:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let span = expect::span()
+    ///     .with_fields(expect::field("field.name").with_value(&"field_value"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info_span!("span", field.name = "different_field_value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// [`ExpectedFields`]: struct@crate::field::ExpectedFields
+    /// [`MockCollector`]: struct@crate::collector::MockCollector
+    /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
+    /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
+    /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
+    pub fn with_fields(self, fields: I) -> NewSpan
+    where
+        I: Into,
+    {
+        NewSpan {
+            span: self,
+            fields: fields.into(),
+            ..Default::default()
+        }
+    }
+
+    pub(crate) fn name(&self) -> Option<&str> {
         self.metadata.name.as_ref().map(String::as_ref)
     }
 
-    pub fn level(&self) -> Option {
+    pub(crate) fn level(&self) -> Option {
         self.metadata.level
     }
 
-    pub fn target(&self) -> Option<&str> {
+    pub(crate) fn target(&self) -> Option<&str> {
         self.metadata.target.as_deref()
     }
 
-    pub fn with_field(self, fields: I) -> NewSpan
-    where
-        I: Into,
-    {
-        NewSpan {
-            span: self,
-            fields: fields.into(),
-            ..Default::default()
-        }
+    pub(crate) fn check(&self, actual: &SpanState, collector_name: &str) {
+        let meta = actual.metadata();
+        let name = meta.name();
+        self.metadata
+            .check(meta, format_args!("span `{}`", name), collector_name);
     }
 }
 
@@ -147,6 +643,13 @@ impl From for NewSpan {
 }
 
 impl NewSpan {
+    /// Configures this `ExpectedSpan` to expect an explicit parent
+    /// span or to be an explicit root.
+    ///
+    /// For more information and examples, see the documentation on
+    /// [`ExpectedSpan::with_explicit_parent`].
+    ///
+    /// [`ExpectedSpan::with_explicit_parent`]: fn@crate::span::ExpectedSpan::with_explicit_parent
     pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan {
         let parent = match parent {
             Some(name) => Parent::Explicit(name.into()),
@@ -158,6 +661,14 @@ impl NewSpan {
         }
     }
 
+    /// Configures this `NewSpan` to expect a
+    /// contextually-determined parent span, or to be a contextual
+    /// root.
+    ///
+    /// For more information and examples, see the documentation on
+    /// [`ExpectedSpan::with_contextual_parent`].
+    ///
+    /// [`ExpectedSpan::with_contextual_parent`]: fn@crate::span::ExpectedSpan::with_contextual_parent
     pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan {
         let parent = match parent {
             Some(name) => Parent::Contextual(name.into()),
@@ -169,7 +680,13 @@ impl NewSpan {
         }
     }
 
-    pub fn with_field(self, fields: I) -> NewSpan
+    /// Adds fields to expect when matching a span.
+    ///
+    /// For more information and examples, see the documentation on
+    /// [`ExpectedSpan::with_fields`].
+    ///
+    /// [`ExpectedSpan::with_fields`]: fn@crate::span::ExpectedSpan::with_fields
+    pub fn with_fields(self, fields: I) -> NewSpan
     where
         I: Into,
     {
@@ -179,7 +696,7 @@ impl NewSpan {
         }
     }
 
-    pub fn check(
+    pub(crate) fn check(
         &mut self,
         span: &tracing_core::span::Attributes<'_>,
         get_parent_name: impl FnOnce() -> Option,
diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs
index e923f56863..b0686f74b8 100644
--- a/tracing-mock/src/subscriber.rs
+++ b/tracing-mock/src/subscriber.rs
@@ -432,7 +432,7 @@ impl MockSubscriberBuilder {
     /// let span = expect::span()
     ///     .at_level(tracing::Level::INFO)
     ///     .named("the span we're testing")
-    ///     .with_field(expect::field("testing").with_value(&"yes"));
+    ///     .with_fields(expect::field("testing").with_value(&"yes"));
     /// let (subscriber, handle) = subscriber::mock()
     ///     .new_span(span)
     ///     .run_with_handle();
@@ -456,7 +456,7 @@ impl MockSubscriberBuilder {
     /// let span = expect::span()
     ///     .at_level(tracing::Level::INFO)
     ///     .named("the span we're testing")
-    ///     .with_field(expect::field("testing").with_value(&"yes"));
+    ///     .with_fields(expect::field("testing").with_value(&"yes"));
     /// let (subscriber, handle) = subscriber::mock()
     ///     .new_span(span)
     ///     .run_with_handle();
diff --git a/tracing-subscriber/tests/env_filter/main.rs b/tracing-subscriber/tests/env_filter/main.rs
index 260da39a2d..47c4cabaf8 100644
--- a/tracing-subscriber/tests/env_filter/main.rs
+++ b/tracing-subscriber/tests/env_filter/main.rs
@@ -41,13 +41,13 @@ fn same_name_spans() {
             expect::span()
                 .named("foo")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("bar")),
+                .with_fields(expect::field("bar")),
         )
         .new_span(
             expect::span()
                 .named("foo")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("baz")),
+                .with_fields(expect::field("baz")),
         )
         .only()
         .run_with_handle();
diff --git a/tracing-subscriber/tests/env_filter/per_subscriber.rs b/tracing-subscriber/tests/env_filter/per_subscriber.rs
index f86612aaab..8af97cbffe 100644
--- a/tracing-subscriber/tests/env_filter/per_subscriber.rs
+++ b/tracing-subscriber/tests/env_filter/per_subscriber.rs
@@ -37,13 +37,13 @@ fn same_name_spans() {
             expect::span()
                 .named("foo")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("bar")),
+                .with_fields(expect::field("bar")),
         )
         .new_span(
             expect::span()
                 .named("foo")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("baz")),
+                .with_fields(expect::field("baz")),
         )
         .only()
         .run_with_handle();
diff --git a/tracing-subscriber/tests/same_len_filters.rs b/tracing-subscriber/tests/same_len_filters.rs
index 53f1793b42..d3f39509e2 100644
--- a/tracing-subscriber/tests/same_len_filters.rs
+++ b/tracing-subscriber/tests/same_len_filters.rs
@@ -61,13 +61,13 @@ fn same_num_fields_and_name_len() {
             expect::span()
                 .named("foo")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("bar")),
+                .with_fields(expect::field("bar")),
         )
         .new_span(
             expect::span()
                 .named("baz")
                 .at_level(Level::TRACE)
-                .with_field(expect::field("boz")),
+                .with_fields(expect::field("boz")),
         )
         .only()
         .run_with_handle();
diff --git a/tracing/tests/collector.rs b/tracing/tests/collector.rs
index d5cccc245e..48b1aa1e53 100644
--- a/tracing/tests/collector.rs
+++ b/tracing/tests/collector.rs
@@ -64,7 +64,7 @@ fn event_macros_dont_infinite_loop() {
 fn boxed_collector() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&display("hello from my span"))
                     .only(),
@@ -97,7 +97,7 @@ fn arced_collector() {
 
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&display("hello from my span"))
                     .only(),
diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs
index 62a9fe6145..e2c4c92396 100644
--- a/tracing/tests/span.rs
+++ b/tracing/tests/span.rs
@@ -342,7 +342,7 @@ fn entered_api() {
 fn moved_field() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&display("hello from my span"))
                     .only(),
@@ -373,7 +373,7 @@ fn dotted_field_name() {
         .new_span(
             expect::span()
                 .named("foo")
-                .with_field(expect::field("fields.bar").with_value(&true).only()),
+                .with_fields(expect::field("fields.bar").with_value(&true).only()),
         )
         .only()
         .run_with_handle();
@@ -389,7 +389,7 @@ fn dotted_field_name() {
 fn borrowed_field() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&display("hello from my span"))
                     .only(),
@@ -432,7 +432,7 @@ fn move_field_out_of_struct() {
     };
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("x")
                     .with_value(&debug(3.234))
                     .and(expect::field("y").with_value(&debug(-1.223)))
@@ -442,7 +442,7 @@ fn move_field_out_of_struct() {
         .new_span(
             expect::span()
                 .named("bar")
-                .with_field(expect::field("position").with_value(&debug(&pos)).only()),
+                .with_fields(expect::field("position").with_value(&debug(&pos)).only()),
         )
         .run_with_handle();
 
@@ -465,7 +465,7 @@ fn move_field_out_of_struct() {
 fn float_values() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("x")
                     .with_value(&3.234)
                     .and(expect::field("y").with_value(&-1.223))
@@ -492,7 +492,7 @@ fn add_field_after_new_span() {
         .new_span(
             span::mock()
                 .named("foo")
-                .with_field(expect::field("bar").with_value(&5)
+                .with_fields(expect::field("bar").with_value(&5)
                 .and(expect::field("baz").with_value).only()),
         )
         .record(
@@ -549,7 +549,7 @@ fn add_fields_only_after_new_span() {
 fn record_new_value_for_field() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&5)
                     .and(expect::field("baz").with_value(&false))
@@ -580,7 +580,7 @@ fn record_new_value_for_field() {
 fn record_new_values_for_fields() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("foo").with_field(
+            expect::span().named("foo").with_fields(
                 expect::field("bar")
                     .with_value(&4)
                     .and(expect::field("baz").with_value(&false))
@@ -781,7 +781,7 @@ fn contextual_child() {
 fn display_shorthand() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("my_span").with_field(
+            expect::span().named("my_span").with_fields(
                 expect::field("my_field")
                     .with_value(&display("hello world"))
                     .only(),
@@ -801,7 +801,7 @@ fn display_shorthand() {
 fn debug_shorthand() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("my_span").with_field(
+            expect::span().named("my_span").with_fields(
                 expect::field("my_field")
                     .with_value(&debug("hello world"))
                     .only(),
@@ -821,7 +821,7 @@ fn debug_shorthand() {
 fn both_shorthands() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("my_span").with_field(
+            expect::span().named("my_span").with_fields(
                 expect::field("display_field")
                     .with_value(&display("hello world"))
                     .and(expect::field("debug_field").with_value(&debug("hello world")))
@@ -842,7 +842,7 @@ fn both_shorthands() {
 fn constant_field_name() {
     let (collector, handle) = collector::mock()
         .new_span(
-            expect::span().named("my_span").with_field(
+            expect::span().named("my_span").with_fields(
                 expect::field("foo")
                     .with_value(&"bar")
                     .and(expect::field("constant string").with_value(&"also works"))

From 29c494d66e487968e7cbc772ed0b101fe071abd3 Mon Sep 17 00:00:00 2001
From: Hayden Stainsby 
Date: Tue, 14 Nov 2023 12:29:28 +0100
Subject: [PATCH 068/101] mock: document public APIs in the `field` module
 (#2443)

This change adds documentation to the tracing-mock `field` module and
all the public APIs within it. This includes doctests on all the methods
which serve as examples.

Additionally, the `field::msg` function (which constructs a field with
name "message" and the provided value) was moved to `expect::message`.
This is part of a unification of all expectation constructors inside the
`expect` module.

Refs: #539

Co-authored-by: David Barsky 
---
 tracing-mock/README.md         |  10 +-
 tracing-mock/src/collector.rs  |   8 +-
 tracing-mock/src/event.rs      |   2 +-
 tracing-mock/src/expect.rs     |   7 +
 tracing-mock/src/field.rs      | 376 +++++++++++++++++++++++++++++++--
 tracing-mock/src/subscriber.rs |  16 +-
 tracing/tests/event.rs         |   4 +-
 7 files changed, 390 insertions(+), 33 deletions(-)

diff --git a/tracing-mock/README.md b/tracing-mock/README.md
index 52703a14d9..d27bb959d0 100644
--- a/tracing-mock/README.md
+++ b/tracing-mock/README.md
@@ -71,14 +71,14 @@ Below is an example that checks that an event contains a message:
 
 ```rust
 use tracing::collect::with_default;
-use tracing_mock::{collector, expect, field};
+use tracing_mock::{collector, expect};
 
 fn yak_shaving() {
     tracing::info!("preparing to shave yaks");
 }
 
 let (collector, handle) = collector::mock()
-    .event(expect::event().with_fields(field::msg("preparing to shave yaks")))
+    .event(expect::event().with_fields(expect::message("preparing to shave yaks")))
     .only()
     .run_with_handle();
 
@@ -102,7 +102,7 @@ Below is a slightly more complex example. `tracing-mock` asserts that, in order:
 
 ```rust
 use tracing::collect::with_default;
-use tracing_mock::{collector, expect, field};
+use tracing_mock::{collector, expect};
 
 #[tracing::instrument]
 fn yak_shaving(number_of_yaks: u32) {
@@ -128,7 +128,7 @@ let (collector, handle) = collector::mock()
         expect::event().with_fields(
             expect::field("number_of_yaks")
                 .with_value(&yak_count)
-                .and(field::msg("preparing to shave yaks"))
+                .and(expect::message("preparing to shave yaks"))
                 .only(),
         ),
     )
@@ -136,7 +136,7 @@ let (collector, handle) = collector::mock()
         expect::event().with_fields(
             expect::field("all_yaks_shaved")
                 .with_value(&true)
-                .and(field::msg("yak shaving completed."))
+                .and(expect::message("yak shaving completed."))
                 .only(),
         ),
     )
diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs
index d1f05a24eb..a91cdd332d 100644
--- a/tracing-mock/src/collector.rs
+++ b/tracing-mock/src/collector.rs
@@ -12,7 +12,7 @@
 //!
 //! let (collector, handle) = collector::mock()
 //!     // Expect a single event with a specified message
-//!     .event(expect::event().with_fields(field::msg("droids")))
+//!     .event(expect::event().with_fields(expect::message("droids")))
 //!     .only()
 //!     .run_with_handle();
 //!
@@ -40,7 +40,7 @@
 //!     // Enter a matching span
 //!     .enter(span.clone())
 //!     // Record an event with message "collect parting message"
-//!     .event(expect::event().with_fields(field::msg("collect parting message")))
+//!     .event(expect::event().with_fields(expect::message("collect parting message")))
 //!     // Record a value for the field `parting` on a matching span
 //!     .record(span.clone(), expect::field("parting").with_value(&"goodbye world!"))
 //!     // Exit a matching span
@@ -81,7 +81,7 @@
 //!     .named("my_span");
 //! let (collector, handle) = collector::mock()
 //!     .enter(span.clone())
-//!     .event(expect::event().with_fields(field::msg("collect parting message")))
+//!     .event(expect::event().with_fields(expect::message("collect parting message")))
 //!     .record(span.clone(), expect::field("parting").with_value(&"goodbye world!"))
 //!     .exit(span)
 //!     .only()
@@ -221,7 +221,7 @@ pub struct MockHandle(Arc>>, String);
 ///     // Enter a matching span
 ///     .enter(span.clone())
 ///     // Record an event with message "collect parting message"
-///     .event(expect::event().with_fields(field::msg("collect parting message")))
+///     .event(expect::event().with_fields(expect::message("collect parting message")))
 ///     // Record a value for the field `parting` on a matching span
 ///     .record(span.clone(), expect::field("parting").with_value(&"goodbye world!"))
 ///     // Exit a matching span
diff --git a/tracing-mock/src/event.rs b/tracing-mock/src/event.rs
index 2a732dc69c..840867d019 100644
--- a/tracing-mock/src/event.rs
+++ b/tracing-mock/src/event.rs
@@ -48,7 +48,7 @@ pub struct ExpectedEvent {
 }
 
 pub fn msg(message: impl fmt::Display) -> ExpectedEvent {
-    expect::event().with_fields(field::msg(message))
+    expect::event().with_fields(expect::message(message))
 }
 
 impl ExpectedEvent {
diff --git a/tracing-mock/src/expect.rs b/tracing-mock/src/expect.rs
index 044f134580..353bc52f5f 100644
--- a/tracing-mock/src/expect.rs
+++ b/tracing-mock/src/expect.rs
@@ -38,6 +38,13 @@ where
     }
 }
 
+pub fn message(message: impl fmt::Display) -> ExpectedField {
+    ExpectedField {
+        name: "message".to_string(),
+        value: ExpectedValue::Debug(message.to_string()),
+    }
+}
+
 pub fn span() -> ExpectedSpan {
     ExpectedSpan {
         ..Default::default()
diff --git a/tracing-mock/src/field.rs b/tracing-mock/src/field.rs
index fa956c7946..a374461375 100644
--- a/tracing-mock/src/field.rs
+++ b/tracing-mock/src/field.rs
@@ -1,3 +1,86 @@
+//! Define expectations to validate fields on events and spans.
+//!
+//! The [`ExpectedField`] struct define expected values for fields in
+//! order to match events and spans via the mock collector API in the
+//! [`collector`] module.
+//!
+//! Expected fields should be created with [`expect::field`] and a
+//! chain of method calls to specify the field value and additional
+//! fields as necessary.
+//!
+//! # Examples
+//!
+//! The simplest case is to expect that an event has a field with a
+//! specific name, without any expectation about the value:
+//!
+//! ```
+//! use tracing_mock::{collector, expect};
+//!
+//! let event = expect::event()
+//!     .with_fields(expect::field("field_name"));
+//!
+//! let (collector, handle) = collector::mock()
+//!     .event(event)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!     tracing::info!(field_name = "value");
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! It is possible to expect multiple fields and specify the value for
+//! each of them:
+//!
+//! ```
+//! use tracing_mock::{collector, expect};
+//!
+//! let event = expect::event().with_fields(
+//!     expect::field("string_field")
+//!         .with_value(&"field_value")
+//!         .and(expect::field("integer_field").with_value(&54_i64))
+//!         .and(expect::field("bool_field").with_value(&true)),
+//! );
+//!
+//! let (collector, handle) = collector::mock()
+//!     .event(event)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!     tracing::info!(
+//!         string_field = "field_value",
+//!         integer_field = 54_i64,
+//!         bool_field = true,
+//!     );
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! If an expected field is not present, or if the value of the field
+//! is different, the test will fail. In this example, the value is
+//! different:
+//!
+//! ```should_panic
+//! use tracing_mock::{collector, expect};
+//!
+//! let event = expect::event()
+//!     .with_fields(expect::field("field_name").with_value(&"value"));
+//!
+//! let (collector, handle) = collector::mock()
+//!     .event(event)
+//!     .run_with_handle();
+//!
+//! tracing::collect::with_default(collector, || {
+//!     tracing::info!(field_name = "different value");
+//! });
+//!
+//! handle.assert_finished();
+//! ```
+//!
+//! [`collector`]: mod@crate::collector
+//! [`expect::field`]: fn@crate::expect::field
 use tracing::{
     callsite,
     callsite::Callsite,
@@ -7,12 +90,24 @@ use tracing::{
 
 use std::{collections::HashMap, fmt};
 
+/// An expectation for multiple fields.
+///
+/// For a detailed description and examples, see the documentation for
+/// the methods and the [`field`] module.
+///
+/// [`field`]: mod@crate::field
 #[derive(Default, Debug, Eq, PartialEq)]
 pub struct ExpectedFields {
     fields: HashMap,
     only: bool,
 }
 
+/// An expected field.
+///
+/// For a detailed description and examples, see the documentation for
+/// the methods and the [`field`] module.
+///
+/// [`field`]: mod@crate::field
 #[derive(Debug)]
 pub struct ExpectedField {
     pub(super) name: String,
@@ -20,7 +115,7 @@ pub struct ExpectedField {
 }
 
 #[derive(Debug)]
-pub enum ExpectedValue {
+pub(crate) enum ExpectedValue {
     F64(f64),
     I64(i64),
     U64(u64),
@@ -55,15 +150,48 @@ impl PartialEq for ExpectedValue {
     }
 }
 
-pub fn msg(message: impl fmt::Display) -> ExpectedField {
-    ExpectedField {
-        name: "message".to_string(),
-        value: ExpectedValue::Debug(message.to_string()),
-    }
-}
-
 impl ExpectedField {
-    /// Expect a field with the given name and value.
+    /// Sets the value to expect when matching this field.
+    ///
+    /// If the recorded value for this field diffs, the expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event()
+    ///     .with_fields(expect::field("field_name").with_value(&"value"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(field_name = "value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// A different value will cause the test to fail:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event()
+    ///     .with_fields(expect::field("field_name").with_value(&"value"));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(field_name = "different value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn with_value(self, value: &dyn Value) -> Self {
         Self {
             value: ExpectedValue::from(value),
@@ -71,6 +199,58 @@ impl ExpectedField {
         }
     }
 
+    /// Adds an additional [`ExpectedField`] to be matched.
+    ///
+    /// Any fields introduced by `.and` must also match. If any fields
+    /// are not present, or if the value for any field is different,
+    /// then the expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42)),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(
+    ///         field = "value",
+    ///         another_field = 42,
+    ///     );
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// If the second field is not present, the test will fail:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42)),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(field = "value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn and(self, other: ExpectedField) -> ExpectedFields {
         ExpectedFields {
             fields: HashMap::new(),
@@ -80,6 +260,47 @@ impl ExpectedField {
         .and(other)
     }
 
+    /// Indicates that no fields other than those specified should be
+    /// expected.
+    ///
+    /// If additional fields are present on the recorded event or span,
+    /// the expectation will fail.
+    ///
+    /// # Examples
+    ///
+    /// Check that only a single field is recorded.
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event()
+    ///     .with_fields(expect::field("field").with_value(&"value").only());
+    ///
+    /// let (collector, handle) = collector::mock().event(event).run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(field = "value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// The following example fails because a second field is recorded.
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event()
+    ///     .with_fields(expect::field("field").with_value(&"value").only());
+    ///
+    /// let (collector, handle) = collector::mock().event(event).run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(field = "value", another_field = 42,);
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn only(self) -> ExpectedFields {
         ExpectedFields {
             fields: HashMap::new(),
@@ -100,12 +321,137 @@ impl From for ExpectedFields {
 }
 
 impl ExpectedFields {
+    /// Adds an additional [`ExpectedField`] to be matched.
+    ///
+    /// _All_ fields must match for the expectation to pass. If any of
+    /// them are not present, if any of the values differs, the
+    /// expectation will fail.
+    ///
+    /// This method performs the same function as
+    /// [`ExpectedField::and`], but applies in the case where there are
+    /// already multiple fields expected.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42))
+    ///         .and(expect::field("a_third_field").with_value(&true)),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(
+    ///         field = "value",
+    ///         another_field = 42,
+    ///         a_third_field = true,
+    ///     );
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// If any of the expected fields are not present on the recorded
+    /// event, the test will fail:
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42))
+    ///         .and(expect::field("a_third_field").with_value(&true)),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(
+    ///         field = "value",
+    ///         a_third_field = true,
+    ///     );
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// [`ExpectedField::and`]: fn@crate::field::ExpectedField::and
     pub fn and(mut self, field: ExpectedField) -> Self {
         self.fields.insert(field.name, field.value);
         self
     }
 
-    /// Indicates that no fields other than those specified should be expected.
+    /// Asserts that no fields other than those specified should be
+    /// expected.
+    ///
+    /// This method performs the same function as
+    /// [`ExpectedField::only`], but applies in the case where there are
+    /// multiple fields expected.
+    ///
+    /// # Examples
+    ///
+    /// Check that only two fields are recorded on the event.
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42))
+    ///         .only(),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(
+    ///         field = "value",
+    ///         another_field = 42,
+    ///     );
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// The following example fails because a third field is recorded.
+    ///
+    /// ```should_panic
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let event = expect::event().with_fields(
+    ///     expect::field("field")
+    ///         .with_value(&"value")
+    ///         .and(expect::field("another_field").with_value(&42))
+    ///         .only(),
+    /// );
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     tracing::info!(
+    ///         field = "value",
+    ///         another_field = 42,
+    ///         a_third_field = true,
+    ///     );
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
     pub fn only(self) -> Self {
         Self { only: true, ..self }
     }
@@ -132,7 +478,11 @@ impl ExpectedFields {
         }
     }
 
-    pub fn checker<'a>(&'a mut self, ctx: &'a str, collector_name: &'a str) -> CheckVisitor<'a> {
+    pub(crate) fn checker<'a>(
+        &'a mut self,
+        ctx: &'a str,
+        collector_name: &'a str,
+    ) -> CheckVisitor<'a> {
         CheckVisitor {
             expect: self,
             ctx,
@@ -140,7 +490,7 @@ impl ExpectedFields {
         }
     }
 
-    pub fn is_empty(&self) -> bool {
+    pub(crate) fn is_empty(&self) -> bool {
         self.fields.is_empty()
     }
 }
@@ -159,7 +509,7 @@ impl fmt::Display for ExpectedValue {
     }
 }
 
-pub struct CheckVisitor<'a> {
+pub(crate) struct CheckVisitor<'a> {
     expect: &'a mut ExpectedFields,
     ctx: &'a str,
     collector_name: &'a str,
diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs
index b0686f74b8..6a8c536d55 100644
--- a/tracing-mock/src/subscriber.rs
+++ b/tracing-mock/src/subscriber.rs
@@ -7,12 +7,12 @@
 //! validated as the code under test is run.
 //!
 //! ```
-//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_mock::{expect, subscriber};
 //! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
 //!
 //! let (subscriber, handle) = subscriber::mock()
 //!     // Expect a single event with a specified message
-//!     .event(expect::event().with_fields(field::msg("droids")))
+//!     .event(expect::event().with_fields(expect::message("droids")))
 //!     .run_with_handle();
 //!
 //! // Use `set_default` to apply the `MockSubscriber` until the end
@@ -33,7 +33,7 @@
 //! their respective fields:
 //!
 //! ```
-//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_mock::{expect, subscriber};
 //! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
 //!
 //! let span = expect::span()
@@ -42,7 +42,7 @@
 //!     // Enter a matching span
 //!     .enter(span.clone())
 //!     // Record an event with message "collect parting message"
-//!     .event(expect::event().with_fields(field::msg("say hello")))
+//!     .event(expect::event().with_fields(expect::message("say hello")))
 //!     // Exit a matching span
 //!     .exit(span)
 //!     // Expect no further messages to be recorded
@@ -75,7 +75,7 @@
 //! span before recording an event, the test will fail:
 //!
 //! ```should_panic
-//! use tracing_mock::{expect, field, subscriber};
+//! use tracing_mock::{expect, subscriber};
 //! use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
 //!
 //! let span = expect::span()
@@ -84,7 +84,7 @@
 //!     // Enter a matching span
 //!     .enter(span.clone())
 //!     // Record an event with message "collect parting message"
-//!     .event(expect::event().with_fields(field::msg("say hello")))
+//!     .event(expect::event().with_fields(expect::message("say hello")))
 //!     // Exit a matching span
 //!     .exit(span)
 //!     // Expect no further messages to be recorded
@@ -145,7 +145,7 @@ use std::{
 /// # Examples
 ///
 /// ```
-/// use tracing_mock::{expect, field, subscriber};
+/// use tracing_mock::{expect, subscriber};
 /// use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt, Subscribe};
 ///
 /// let span = expect::span()
@@ -154,7 +154,7 @@ use std::{
 ///     // Enter a matching span
 ///     .enter(span.clone())
 ///     // Record an event with message "collect parting message"
-///     .event(expect::event().with_fields(field::msg("say hello")))
+///     .event(expect::event().with_fields(expect::message("say hello")))
 ///     // Exit a matching span
 ///     .exit(span)
 ///     // Expect no further messages to be recorded
diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs
index 55594ea913..a370b700cd 100644
--- a/tracing/tests/event.rs
+++ b/tracing/tests/event.rs
@@ -85,7 +85,7 @@ fn message_without_delims() {
                     .and(
                         expect::field("question").with_value(&"life, the universe, and everything"),
                     )
-                    .and(field::msg(format_args!(
+                    .and(expect::message(format_args!(
                         "hello from my event! tricky? {:?}!",
                         true
                     )))
@@ -114,7 +114,7 @@ fn string_message_without_delims() {
                     .and(
                         expect::field("question").with_value(&"life, the universe, and everything"),
                     )
-                    .and(field::msg(format_args!("hello from my event")))
+                    .and(expect::message(format_args!("hello from my event")))
                     .only(),
             ),
         )

From 8ba80ae82371638aa749436e4f284b65adf9ff90 Mon Sep 17 00:00:00 2001
From: Sleep_AllDay <37238439+SpeedReach@users.noreply.github.com>
Date: Wed, 15 Nov 2023 02:08:35 +0800
Subject: [PATCH 069/101] subscriber: fix flaky reload tests

fixes https://github.com/tokio-rs/tracing/actions/runs/6785393202/job/18443641813

cargo test runs tests in the same file in parallel by default, causing race condition,
this can be proven by running
`cargo test --test reload -- --test-threads=1` => successes
`cargo test --test reload -- --test-threads=2` => flaky
multiple times

This fix runs only the two tests in serial.
We could seperate the tests in different files, but they share the same testing dependencies, so I left them in the same file.
---
 tracing-subscriber/tests/reload.rs | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/tracing-subscriber/tests/reload.rs b/tracing-subscriber/tests/reload.rs
index 381a307c50..58b75c01fb 100644
--- a/tracing-subscriber/tests/reload.rs
+++ b/tracing-subscriber/tests/reload.rs
@@ -47,7 +47,15 @@ impl tracing_subscriber::Subscribe for NopSubscriber {
     }
 }
 
+/// Running these two tests in parallel will cause flaky failures, since they are both modifying the MAX_LEVEL value.
+/// "cargo test -- --test-threads=1 fixes it, but it runs all tests in serial.
+/// The only way to run tests in serial in a single file is this way.
 #[test]
+fn run_all_reload_test() {
+    reload_handle();
+    reload_filter();
+}
+
 fn reload_handle() {
     static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
     static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0);
@@ -104,7 +112,6 @@ fn reload_handle() {
     })
 }
 
-#[test]
 fn reload_filter() {
     static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0);
     static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0);

From bac25085fcea68ca30b8e19b8981fbd689f53735 Mon Sep 17 00:00:00 2001
From: Hayden Stainsby 
Date: Sun, 26 Nov 2023 16:34:17 +0100
Subject: [PATCH 070/101] chore: fixes for clippy changes in Rust 1.74 (#2814)

With the release of Rust 1.74, there are some new or modified clippy
lints that need adaption in the code.

The main change was the removal of the `private_in_public`.
https://rust-lang.github.io/rfcs/2145-type-privacy.html

Then two more changes were required, in one case to adhere a lint and
the other to allow it. When talking about what an "application" needs to
do when setting up `tracing-error`, it makes sense to include `fn
main()` in the doctest, even though the lint recommends against it.
---
 examples/examples/map-traced-error.rs | 2 +-
 tracing-appender/src/lib.rs           | 3 ++-
 tracing-attributes/src/lib.rs         | 3 ++-
 tracing-core/src/lib.rs               | 3 ++-
 tracing-error/src/lib.rs              | 4 +++-
 tracing-flame/src/lib.rs              | 3 ++-
 tracing-futures/src/lib.rs            | 3 ++-
 tracing-log/src/lib.rs                | 3 ++-
 tracing-serde/src/lib.rs              | 3 ++-
 tracing-subscriber/src/lib.rs         | 3 ++-
 tracing-tower/src/lib.rs              | 3 ++-
 tracing/src/lib.rs                    | 3 ++-
 12 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/examples/examples/map-traced-error.rs b/examples/examples/map-traced-error.rs
index 25bf779355..9911e762ef 100644
--- a/examples/examples/map-traced-error.rs
+++ b/examples/examples/map-traced-error.rs
@@ -44,7 +44,7 @@ fn do_something() -> Result<(), TracedError> {
 
 #[tracing::instrument]
 fn do_the_real_stuff() -> Result<(), TracedError> {
-    Err(InnerError).map_err(TracedError::from)
+    Err(TracedError::from(InnerError))
 }
 
 #[derive(Debug)]
diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs
index 03ce53fec0..d6e2af9e95 100644
--- a/tracing-appender/src/lib.rs
+++ b/tracing-appender/src/lib.rs
@@ -153,7 +153,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-attributes/src/lib.rs b/tracing-attributes/src/lib.rs
index 857e0798b6..809305ca4e 100644
--- a/tracing-attributes/src/lib.rs
+++ b/tracing-attributes/src/lib.rs
@@ -70,7 +70,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused_allocation,
     unused_comparisons,
diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs
index 7aafb01151..eafdba9120 100644
--- a/tracing-core/src/lib.rs
+++ b/tracing-core/src/lib.rs
@@ -152,7 +152,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs
index 19f1d6e65e..c658f4e8c6 100644
--- a/tracing-error/src/lib.rs
+++ b/tracing-error/src/lib.rs
@@ -183,6 +183,7 @@
     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/"
 )]
+#![allow(clippy::needless_doctest_main)]
 #![warn(
     missing_debug_implementations,
     missing_docs,
@@ -196,7 +197,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-flame/src/lib.rs b/tracing-flame/src/lib.rs
index 36d2f9c844..5ad84b582c 100644
--- a/tracing-flame/src/lib.rs
+++ b/tracing-flame/src/lib.rs
@@ -124,7 +124,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-futures/src/lib.rs b/tracing-futures/src/lib.rs
index 0b1c810c60..dfa99b046c 100644
--- a/tracing-futures/src/lib.rs
+++ b/tracing-futures/src/lib.rs
@@ -89,7 +89,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-log/src/lib.rs b/tracing-log/src/lib.rs
index aff6d42fe1..dbdf8e9549 100644
--- a/tracing-log/src/lib.rs
+++ b/tracing-log/src/lib.rs
@@ -115,7 +115,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs
index 67535b8514..97b78d55c0 100644
--- a/tracing-serde/src/lib.rs
+++ b/tracing-serde/src/lib.rs
@@ -159,7 +159,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index c64a40eedf..4b1ce72d82 100644
--- a/tracing-subscriber/src/lib.rs
+++ b/tracing-subscriber/src/lib.rs
@@ -156,7 +156,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing-tower/src/lib.rs b/tracing-tower/src/lib.rs
index 41ec704970..4f06044d29 100644
--- a/tracing-tower/src/lib.rs
+++ b/tracing-tower/src/lib.rs
@@ -17,7 +17,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index a2fcc25a31..1467c907f2 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -948,7 +948,8 @@
     overflowing_literals,
     path_statements,
     patterns_in_fns_without_body,
-    private_in_public,
+    private_interfaces,
+    private_bounds,
     unconditional_recursion,
     unused,
     unused_allocation,

From 00ca2dc116d11b450cdc9c6306aaf59f6b7ed34a Mon Sep 17 00:00:00 2001
From: Alex Saveau 
Date: Sat, 6 Jan 2024 11:28:11 -0800
Subject: [PATCH 071/101] examples: suppress false positive clippy lint (#2846)

Signed-off-by: Alex Saveau 
---
 examples/examples/sloggish/sloggish_collector.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/examples/examples/sloggish/sloggish_collector.rs b/examples/examples/sloggish/sloggish_collector.rs
index fd93425f13..0cc71ee0fc 100644
--- a/examples/examples/sloggish/sloggish_collector.rs
+++ b/examples/examples/sloggish/sloggish_collector.rs
@@ -230,6 +230,7 @@ impl Collect for SloggishCollector {
             self.print_indent(&mut stderr, indent).unwrap();
             stack.push(span_id.clone());
             if let Some(data) = data {
+                #[allow(clippy::map_identity)] // TODO remove in Rust 1.77
                 self.print_kvs(&mut stderr, data.kvs.iter().map(|(k, v)| (k, v)), "")
                     .unwrap();
             }

From 07b490067c0e2af61f48a3d2afb85a20ab70ba95 Mon Sep 17 00:00:00 2001
From: Taiki Endo 
Date: Sun, 7 Jan 2024 06:10:05 +0900
Subject: [PATCH 072/101] chore(ci): update actions/checkout action to v4
 (#2845)

---
 .github/workflows/CI.yml      | 18 +++++++++---------
 .github/workflows/audit.yml   |  2 +-
 .github/workflows/release.yml |  2 +-
 3 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 7ec7579c09..1f1ee1b63f 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -35,7 +35,7 @@ jobs:
     name: cargo check
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
     - name: Check
       run: cargo check --all --tests --benches
@@ -46,7 +46,7 @@ jobs:
     needs: check
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
       with:
         components: rustfmt
@@ -58,7 +58,7 @@ jobs:
     runs-on: ubuntu-latest
     needs: check
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
       with:
         components: clippy
@@ -88,7 +88,7 @@ jobs:
         - tracing
         - tracing-subscriber
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
     - name: install cargo-hack
       uses: taiki-e/install-action@cargo-hack
@@ -146,7 +146,7 @@ jobs:
         - 1.63.0
         - stable
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: install Rust nightly
       uses: dtolnay/rust-toolchain@nightly
     - name: "install Rust ${{ matrix.toolchain }}"
@@ -210,7 +210,7 @@ jobs:
       fail-fast: false
     runs-on: ${{ matrix.os }}
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - name: "install Rust ${{ matrix.rust }}"
       uses: dtolnay/rust-toolchain@master
       with:
@@ -252,7 +252,7 @@ jobs:
         - tracing-tower
       fail-fast: false
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
       with:
         target: wasm32-unknown-unknown
@@ -268,7 +268,7 @@ jobs:
         subcrate:
         - tracing
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
       with:
         target: wasm32-unknown-unknown
@@ -283,7 +283,7 @@ jobs:
     needs: check
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3
+    - uses: actions/checkout@v4
     - uses: dtolnay/rust-toolchain@stable
     - name: "Test log support"
       run: cargo test
diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml
index f4d89ead1f..ccd30acee6 100644
--- a/.github/workflows/audit.yml
+++ b/.github/workflows/audit.yml
@@ -29,7 +29,7 @@ jobs:
   security_audit:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - uses: actions-rs/audit-check@v1
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 77a52ca806..c175cfe762 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -13,7 +13,7 @@ jobs:
     if: github.repository_owner == 'tokio-rs'
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
       - uses: taiki-e/create-gh-release-action@v1
         with:
           prefix: tracing(-[a-z]+)?

From d4ca988252211b12e3beccfa6b029064ede1d6dc Mon Sep 17 00:00:00 2001
From: Gabriel Goller 
Date: Thu, 11 Jan 2024 21:28:41 +0100
Subject: [PATCH 073/101] journald: make level mappings configurable (#2824)

This allows to manually map tracing levels to journald levels.

It seems that @little-dude, who started the original PR, doesn't have
time to finish this, so I picked it up.  Reapplied the changes to the
newest master branch and fixed the latest comments/issues.

This will also fix/close:

Closes #2649
Closes #2661
Closes #2347 (the original pr)
---
 tracing-journald/src/lib.rs       | 177 +++++++++++++++++++++++++++---
 tracing-journald/tests/journal.rs |  50 ++++++++-
 2 files changed, 210 insertions(+), 17 deletions(-)

diff --git a/tracing-journald/src/lib.rs b/tracing-journald/src/lib.rs
index 02b12c3e82..112ee7c2d0 100644
--- a/tracing-journald/src/lib.rs
+++ b/tracing-journald/src/lib.rs
@@ -62,7 +62,7 @@ mod socket;
 /// names by translating `.`s into `_`s, stripping leading `_`s and non-ascii-alphanumeric
 /// characters other than `_`, and upcasing.
 ///
-/// Levels are mapped losslessly to journald `PRIORITY` values as follows:
+/// By default, levels are mapped losslessly to journald `PRIORITY` values as follows:
 ///
 /// - `ERROR` => Error (3)
 /// - `WARN` => Warning (4)
@@ -70,6 +70,8 @@ mod socket;
 /// - `DEBUG` => Informational (6)
 /// - `TRACE` => Debug (7)
 ///
+/// These mappings can be changed with [`Subscriber::with_priority_mappings`].
+///
 /// The standard journald `CODE_LINE` and `CODE_FILE` fields are automatically emitted. A `TARGET`
 /// field is emitted containing the event's target.
 ///
@@ -86,6 +88,7 @@ pub struct Subscriber {
     field_prefix: Option,
     syslog_identifier: String,
     additional_fields: Vec,
+    priority_mappings: PriorityMappings,
 }
 
 #[cfg(unix)]
@@ -111,6 +114,7 @@ impl Subscriber {
                     // If we fail to get the name of the current executable fall back to an empty string.
                     .unwrap_or_default(),
                 additional_fields: Vec::new(),
+                priority_mappings: PriorityMappings::new(),
             };
             // Check that we can talk to journald, by sending empty payload which journald discards.
             // However if the socket didn't exist or if none listened we'd get an error here.
@@ -131,6 +135,41 @@ impl Subscriber {
         self
     }
 
+    /// Sets how [`tracing_core::Level`]s are mapped to [journald priorities](Priority).
+    ///
+    /// # Examples
+    ///
+    /// ```rust
+    /// use tracing_journald::{Priority, PriorityMappings};
+    /// use tracing_subscriber::prelude::*;
+    /// use tracing::error;
+    ///
+    /// let registry = tracing_subscriber::registry();
+    /// match tracing_journald::subscriber() {
+    ///     Ok(subscriber) => {
+    ///         registry.with(
+    ///             subscriber
+    ///                 // We can tweak the mappings between the trace level and
+    ///                 // the journal priorities.
+    ///                 .with_priority_mappings(PriorityMappings {
+    ///                     info: Priority::Informational,
+    ///                     ..PriorityMappings::new()
+    ///                 }),
+    ///         );
+    ///     }
+    ///     // journald is typically available on Linux systems, but nowhere else. Portable software
+    ///     // should handle its absence gracefully.
+    ///     Err(e) => {
+    ///         registry.init();
+    ///         error!("couldn't connect to journald: {}", e);
+    ///     }
+    /// }
+    /// ```
+    pub fn with_priority_mappings(mut self, mappings: PriorityMappings) -> Self {
+        self.priority_mappings = mappings;
+        self
+    }
+
     /// Sets the syslog identifier for this logger.
     ///
     /// The syslog identifier comes from the classic syslog interface (`openlog()`
@@ -234,6 +273,20 @@ impl Subscriber {
         memfd::seal_fully(mem.as_raw_fd())?;
         socket::send_one_fd_to(&self.socket, mem.as_raw_fd(), JOURNALD_PATH)
     }
+
+    fn put_priority(&self, buf: &mut Vec, meta: &Metadata) {
+        put_field_wellformed(
+            buf,
+            "PRIORITY",
+            &[match *meta.level() {
+                Level::ERROR => self.priority_mappings.error as u8,
+                Level::WARN => self.priority_mappings.warn as u8,
+                Level::INFO => self.priority_mappings.info as u8,
+                Level::DEBUG => self.priority_mappings.debug as u8,
+                Level::TRACE => self.priority_mappings.trace as u8,
+            }],
+        );
+    }
 }
 
 /// Construct a journald subscriber
@@ -288,7 +341,7 @@ where
         }
 
         // Record event fields
-        put_priority(&mut buf, event.metadata());
+        self.put_priority(&mut buf, event.metadata());
         put_metadata(&mut buf, event.metadata(), None);
         put_field_length_encoded(&mut buf, "SYSLOG_IDENTIFIER", |buf| {
             write!(buf, "{}", self.syslog_identifier).unwrap()
@@ -376,18 +429,114 @@ impl Visit for EventVisitor<'_> {
     }
 }
 
-fn put_priority(buf: &mut Vec, meta: &Metadata) {
-    put_field_wellformed(
-        buf,
-        "PRIORITY",
-        match *meta.level() {
-            Level::ERROR => b"3",
-            Level::WARN => b"4",
-            Level::INFO => b"5",
-            Level::DEBUG => b"6",
-            Level::TRACE => b"7",
-        },
-    );
+/// A priority (called "severity code" by syslog) is used to mark the
+/// importance of a message.
+///
+/// Descriptions and examples are taken from the [Arch Linux wiki].
+/// Priorities are also documented in the
+/// [section 6.2.1 of the Syslog protocol RFC][syslog].
+///
+/// [Arch Linux wiki]: https://wiki.archlinux.org/title/Systemd/Journal#Priority_level
+/// [syslog]: https://www.rfc-editor.org/rfc/rfc5424#section-6.2.1
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+#[repr(u8)]
+pub enum Priority {
+    /// System is unusable.
+    ///
+    /// Examples:
+    ///
+    /// - severe Kernel BUG
+    /// - systemd dumped core
+    ///
+    /// This level should not be used by applications.
+    Emergency = b'0',
+    /// Should be corrected immediately.
+    ///
+    /// Examples:
+    ///
+    /// - Vital subsystem goes out of work, data loss:
+    /// - `kernel: BUG: unable to handle kernel paging request at ffffc90403238ffc`
+    Alert = b'1',
+    /// Critical conditions
+    ///
+    /// Examples:
+    ///
+    /// - Crashe, coredumps
+    /// - `systemd-coredump[25319]: Process 25310 (plugin-container) of user 1000 dumped core`
+    Critical = b'2',
+    /// Error conditions
+    ///
+    /// Examples:
+    ///
+    /// - Not severe error reported
+    /// - `kernel: usb 1-3: 3:1: cannot get freq at ep 0x84, systemd[1]: Failed unmounting /var`
+    /// - `libvirtd[1720]: internal error: Failed to initialize a valid firewall backend`
+    Error = b'3',
+    /// May indicate that an error will occur if action is not taken.
+    ///
+    /// Examples:
+    ///
+    /// - a non-root file system has only 1GB free
+    /// - `org.freedesktop. Notifications[1860]: (process:5999): Gtk-WARNING **: Locale not supported by C library. Using the fallback 'C' locale`
+    Warning = b'4',
+    /// Events that are unusual, but not error conditions.
+    ///
+    /// Examples:
+    ///
+    /// - `systemd[1]: var.mount: Directory /var to mount over is not empty, mounting anyway`
+    /// - `gcr-prompter[4997]: Gtk: GtkDialog mapped without a transient parent. This is discouraged`
+    Notice = b'5',
+    /// Normal operational messages that require no action.
+    ///
+    /// Example: `lvm[585]: 7 logical volume(s) in volume group "archvg" now active`
+    Informational = b'6',
+    /// Information useful to developers for debugging the
+    /// application.
+    ///
+    /// Example: `kdeinit5[1900]: powerdevil: Scheduling inhibition from ":1.14" "firefox" with cookie 13 and reason "screen"`
+    Debug = b'7',
+}
+
+/// Mappings from tracing [`Level`]s to journald [priorities].
+///
+/// [priorities]: Priority
+#[derive(Debug, Clone)]
+pub struct PriorityMappings {
+    /// Priority mapped to the `ERROR` level
+    pub error: Priority,
+    /// Priority mapped to the `WARN` level
+    pub warn: Priority,
+    /// Priority mapped to the `INFO` level
+    pub info: Priority,
+    /// Priority mapped to the `DEBUG` level
+    pub debug: Priority,
+    /// Priority mapped to the `TRACE` level
+    pub trace: Priority,
+}
+
+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)
+    pub fn new() -> PriorityMappings {
+        Self {
+            error: Priority::Error,
+            warn: Priority::Warning,
+            info: Priority::Notice,
+            debug: Priority::Informational,
+            trace: Priority::Debug,
+        }
+    }
+}
+
+impl Default for PriorityMappings {
+    fn default() -> Self {
+        Self::new()
+    }
 }
 
 fn put_metadata(buf: &mut Vec, meta: &Metadata, prefix: Option<&str>) {
diff --git a/tracing-journald/tests/journal.rs b/tracing-journald/tests/journal.rs
index 24a7fc7611..df86475fe3 100644
--- a/tracing-journald/tests/journal.rs
+++ b/tracing-journald/tests/journal.rs
@@ -5,8 +5,8 @@ use std::process::Command;
 use std::time::Duration;
 
 use serde::Deserialize;
-use tracing::{debug, error, info, info_span, warn};
-use tracing_journald::Subscriber;
+use tracing::{debug, error, info, info_span, trace, warn};
+use tracing_journald::{Priority, PriorityMappings, Subscriber};
 use tracing_subscriber::subscribe::CollectExt;
 use tracing_subscriber::Registry;
 
@@ -16,7 +16,16 @@ fn journalctl_version() -> std::io::Result {
 }
 
 fn with_journald(f: impl FnOnce()) {
-    with_journald_subscriber(Subscriber::new().unwrap().with_field_prefix(None), f)
+    with_journald_subscriber(
+        Subscriber::new()
+            .unwrap()
+            .with_field_prefix(None)
+            .with_priority_mappings(PriorityMappings {
+                trace: Priority::Informational,
+                ..PriorityMappings::new()
+            }),
+        f,
+    )
 }
 
 fn with_journald_subscriber(subscriber: Subscriber, f: impl FnOnce()) {
@@ -167,6 +176,41 @@ fn simple_message() {
     });
 }
 
+#[test]
+fn custom_priorities() {
+    fn check_message(level: &str, priority: &str) {
+        let entry = retry_read_one_line_from_journal(&format!("custom_priority.{}", level));
+        assert_eq!(entry["MESSAGE"], format!("hello {}", level).as_str());
+        assert_eq!(entry["PRIORITY"], priority);
+    }
+
+    let priorities = PriorityMappings {
+        error: Priority::Critical,
+        warn: Priority::Error,
+        info: Priority::Warning,
+        debug: Priority::Notice,
+        trace: Priority::Informational,
+    };
+    let subscriber = Subscriber::new()
+        .unwrap()
+        .with_field_prefix(None)
+        .with_priority_mappings(priorities);
+    let test = || {
+        trace!(test.name = "custom_priority.trace", "hello trace");
+        check_message("trace", "6");
+        debug!(test.name = "custom_priority.debug", "hello debug");
+        check_message("debug", "5");
+        info!(test.name = "custom_priority.info", "hello info");
+        check_message("info", "4");
+        warn!(test.name = "custom_priority.warn", "hello warn");
+        check_message("warn", "3");
+        error!(test.name = "custom_priority.error", "hello error");
+        check_message("error", "2");
+    };
+
+    with_journald_subscriber(subscriber, test);
+}
+
 #[test]
 fn multiline_message() {
     with_journald(|| {

From 335380a6b17e733899b95e0524ad03581861cbc2 Mon Sep 17 00:00:00 2001
From: John Vandenberg 
Date: Tue, 16 Jan 2024 03:43:42 +0800
Subject: [PATCH 074/101] chore: Fix spelling (#2854)

---
 examples/examples/fmt/yak_shave.rs                   |  2 +-
 tracing-core/src/metadata.rs                         |  2 +-
 tracing-error/CHANGELOG.md                           |  2 +-
 tracing-error/src/error.rs                           |  2 +-
 tracing-mock/src/subscriber.rs                       |  2 +-
 tracing-subscriber/CHANGELOG.md                      | 12 ++++++------
 tracing-subscriber/src/filter/env/mod.rs             |  2 +-
 tracing-subscriber/src/filter/filter_fn.rs           |  2 +-
 tracing-subscriber/src/filter/mod.rs                 |  2 +-
 .../src/filter/subscriber_filters/mod.rs             |  2 +-
 tracing-subscriber/src/fmt/format/mod.rs             |  2 +-
 tracing-subscriber/src/fmt/time/datetime.rs          |  2 +-
 tracing-subscriber/src/subscribe/mod.rs              | 10 +++++-----
 tracing/CHANGELOG.md                                 |  2 +-
 tracing/README.md                                    |  4 ++--
 tracing/src/lib.rs                                   |  2 +-
 tracing/src/macros.rs                                |  2 +-
 tracing/tests/enabled.rs                             |  2 +-
 18 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/examples/examples/fmt/yak_shave.rs b/examples/examples/fmt/yak_shave.rs
index 6e60a9fd8b..6639f8ddd4 100644
--- a/examples/examples/fmt/yak_shave.rs
+++ b/examples/examples/fmt/yak_shave.rs
@@ -5,7 +5,7 @@ use tracing::{debug, error, info, span, trace, warn, Level};
 
 // the `#[tracing::instrument]` attribute creates and enters a span
 // every time the instrumented function is called. The span is named after
-// the function or method. Paramaters passed to the function are recorded as fields.
+// the function or method. Parameters passed to the function are recorded as fields.
 #[tracing::instrument]
 pub fn shave(yak: usize) -> Result<(), Box> {
     // this creates an event at the TRACE log level with two fields:
diff --git a/tracing-core/src/metadata.rs b/tracing-core/src/metadata.rs
index 76d84e6d1e..3e8cb9761a 100644
--- a/tracing-core/src/metadata.rs
+++ b/tracing-core/src/metadata.rs
@@ -378,7 +378,7 @@ impl Kind {
     pub const SPAN: Kind = Kind(Self::SPAN_BIT);
 
     /// `enabled!` callsite. [`Collect`][`crate::collect::Collect`]s can assume
-    /// this `Kind` means they will never recieve a
+    /// this `Kind` means they will never receive a
     /// full event with this [`Metadata`].
     pub const HINT: Kind = Kind(Self::HINT_BIT);
 
diff --git a/tracing-error/CHANGELOG.md b/tracing-error/CHANGELOG.md
index a1b780aa2f..54aa87e8c3 100644
--- a/tracing-error/CHANGELOG.md
+++ b/tracing-error/CHANGELOG.md
@@ -5,7 +5,7 @@
 - **TracedError**: `TracedError`, an error type wrapper that annotates an error
   with the current span.
 - **SpanTrace**:`SpanTrace::status` method and `SpanTraceStatus` type for
-  determing whether a `SpanTrace` was successfully captured (#614)
+  determining whether a `SpanTrace` was successfully captured (#614)
 
 ### Changed
 
diff --git a/tracing-error/src/error.rs b/tracing-error/src/error.rs
index 51bd599ee1..cdc2864cf3 100644
--- a/tracing-error/src/error.rs
+++ b/tracing-error/src/error.rs
@@ -74,7 +74,7 @@ where
         // erased `ErrorImpl` back to its original type, which is needed in order to forward our
         // error/display/debug impls to the internal error type from the type erased error type.
         //
-        // The repr(C) is necessary to ensure that the struct is layed out in the order we
+        // The repr(C) is necessary to ensure that the struct is laid out in the order we
         // specified it, so that we can safely access the vtable and spantrace fields through a type
         // erased pointer to the original object.
         let vtable = &ErrorVTable {
diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs
index 6a8c536d55..f9560065ff 100644
--- a/tracing-mock/src/subscriber.rs
+++ b/tracing-mock/src/subscriber.rs
@@ -1,5 +1,5 @@
 //! An implementation of the [`Subscribe`] trait which validates that
-//! the `tracing` data it recieves matches  the expected output for a test.
+//! the `tracing` data it receives matches the expected output for a test.
 //!
 //!
 //! The [`MockSubscriber`] is the central component in these tools. The
diff --git a/tracing-subscriber/CHANGELOG.md b/tracing-subscriber/CHANGELOG.md
index 132260184d..e362d87b75 100644
--- a/tracing-subscriber/CHANGELOG.md
+++ b/tracing-subscriber/CHANGELOG.md
@@ -57,7 +57,7 @@ Thanks to @bdonlan and @jeromegn for contributing to this release!
 ### Changed
 
 - **filter**: `LevelFilter` is now a re-export of the
-  `tracing_core::LevelFilter` type, it can now be used interchangably with the
+  `tracing_core::LevelFilter` type, it can now be used interchangeably with the
   versions in `tracing` and `tracing-core` (#853)
 - **filter**: Significant performance improvements when comparing `LevelFilter`s
   and `Level`s (#853)
@@ -260,7 +260,7 @@ tuning in this release!
 
 - **fmt**: Fixed empty `{}` printed after spans with no fields (f079f2d)
 - **fmt**: Fixed inconsistent formatting when ANSI colors are disabled (506a482)
-- **fmt**: Fixed mis-aligned levels when ANSI colors are disabled (eba1adb)
+- **fmt**: Fixed misaligned levels when ANSI colors are disabled (eba1adb)
 - Fixed warnings on nightly Rust compilers (#558)
 
 # 0.2.0-alpha.5 (January 31, 2020)
@@ -347,9 +347,9 @@ tuning in this release!
   changes in subsequent alpha. (#420, #425)
 - **BREAKING**: Removed `Filter`. Use `EnvFilter` instead (#434)
 
-### Contributers
+### Contributors
 
-Thanks to all the contributers to this release!
+Thanks to all the contributors to this release!
 
 - @pimeys for #377 and #415
 
@@ -375,9 +375,9 @@ Thanks to all the contributers to this release!
   order to initialize the global logger. Only `tracing-log` needs to be
   specified now (#400).
 
-### Contributers
+### Contributors
 
-Thanks to all the contributers to this release!
+Thanks to all the contributors to this release!
 
 - @emschwartz for #385, #387, #400 and #401
 - @bIgBV for #388
diff --git a/tracing-subscriber/src/filter/env/mod.rs b/tracing-subscriber/src/filter/env/mod.rs
index 93f44640d0..4819e7936b 100644
--- a/tracing-subscriber/src/filter/env/mod.rs
+++ b/tracing-subscriber/src/filter/env/mod.rs
@@ -476,7 +476,7 @@ impl EnvFilter {
         let level = metadata.level();
 
         // is it possible for a dynamic filter directive to enable this event?
-        // if not, we can avoid the thread loca'l access + iterating over the
+        // if not, we can avoid the thread local access + iterating over the
         // spans in the current scope.
         if self.has_dynamics && self.dynamics.max_level >= *level {
             if metadata.is_span() {
diff --git a/tracing-subscriber/src/filter/filter_fn.rs b/tracing-subscriber/src/filter/filter_fn.rs
index eda7cef47c..b426208b25 100644
--- a/tracing-subscriber/src/filter/filter_fn.rs
+++ b/tracing-subscriber/src/filter/filter_fn.rs
@@ -295,7 +295,7 @@ where
         metadata: &'static Metadata<'static>,
     ) -> Interest {
         // Because `self.enabled` takes a `Metadata` only (and no `Context`
-        // parameter), we can reasonably assume its results are cachable, and
+        // parameter), we can reasonably assume its results are cacheable, and
         // just return `Interest::always`/`Interest::never`.
         if (self.enabled)(metadata) {
             debug_assert!(
diff --git a/tracing-subscriber/src/filter/mod.rs b/tracing-subscriber/src/filter/mod.rs
index b50e5b2628..bb0b5af5f1 100644
--- a/tracing-subscriber/src/filter/mod.rs
+++ b/tracing-subscriber/src/filter/mod.rs
@@ -42,7 +42,7 @@ feature! {
     pub use self::directive::ParseError;
 }
 
-/// Stub implementations of the per-subscriber-fitler detection functions for
+/// Stub implementations of the per-subscriber-filter detection functions for
 /// when the `registry` feature is disabled.
 #[cfg(not(all(feature = "registry", feature = "std")))]
 mod has_psf_stubs {
diff --git a/tracing-subscriber/src/filter/subscriber_filters/mod.rs b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
index d519aa4d7c..7d98ee9d08 100644
--- a/tracing-subscriber/src/filter/subscriber_filters/mod.rs
+++ b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
@@ -717,7 +717,7 @@ where
     //
     // it would be cool if there was some wild rust reflection way of checking
     // if a trait impl has the default impl of a trait method or not, but that's
-    // almsot certainly impossible...right?
+    // almost certainly impossible...right?
 
     fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest {
         let interest = self.filter.callsite_enabled(metadata);
diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs
index d4c619b3fb..e19d689486 100644
--- a/tracing-subscriber/src/fmt/format/mod.rs
+++ b/tracing-subscriber/src/fmt/format/mod.rs
@@ -1748,7 +1748,7 @@ pub(super) mod test {
             "^fake time tracing_subscriber::fmt::format::test: {}:[0-9]+: hello\n$",
             current_path()
                 // if we're on Windows, the path might contain backslashes, which
-                // have to be escpaed before compiling the regex.
+                // have to be escaped before compiling the regex.
                 .replace('\\', "\\\\")
         ))
         .unwrap();
diff --git a/tracing-subscriber/src/fmt/time/datetime.rs b/tracing-subscriber/src/fmt/time/datetime.rs
index 5313316870..a39d2ef19c 100644
--- a/tracing-subscriber/src/fmt/time/datetime.rs
+++ b/tracing-subscriber/src/fmt/time/datetime.rs
@@ -387,7 +387,7 @@ mod tests {
         case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0);
         case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0);
 
-        // Skipping these tests on windows as std::time::SysteTime range is low
+        // Skipping these tests on windows as std::time::SystemTime range is low
         // on Windows compared with that of Unix which can cause the following
         // high date value tests to panic
         #[cfg(not(target_os = "windows"))]
diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs
index 61a2e4b080..7d6c9d8d7f 100644
--- a/tracing-subscriber/src/subscribe/mod.rs
+++ b/tracing-subscriber/src/subscribe/mod.rs
@@ -430,7 +430,7 @@
 //!   callsite). See [`Collect::register_callsite`] and
 //!   [`tracing_core::callsite`] for a summary of how this behaves.
 //! - [`enabled`], once per emitted event (roughly: once per time that `event!`
-//!   or `span!` is *executed*), and only if `register_callsite` regesters an
+//!   or `span!` is *executed*), and only if `register_callsite` registers an
 //!   [`Interest::sometimes`]. This is the main customization point to globally
 //!   filter events based on their [`Metadata`]. If an event can be disabled
 //!   based only on [`Metadata`], it should be, as this allows the construction
@@ -768,7 +768,7 @@ where
     /// [`Collect`] has been set as the default, both the subscriber and
     /// [`Collect`] are passed to this method _mutably_. This gives the
     /// subscribe the opportunity to set any of its own fields with values
-    /// recieved by method calls on the [`Collect`].
+    /// received by method calls on the [`Collect`].
     ///
     /// For example, [`Filtered`] subscribers implement `on_subscribe` to call the
     /// [`Collect`]'s [`register_filter`] method, and store the returned
@@ -900,7 +900,7 @@ where
     ///
     /// **Note**: This method determines whether an event is globally enabled,
     /// *not* whether the individual subscriber will be notified about the
-    /// event. This is intended to be used by subscibers that implement
+    /// event. This is intended to be used by subscribers that implement
     /// filtering for the entire stack. Subscribers which do not wish to be
     /// notified about certain events but do not wish to globally disable them
     /// should ignore those events in their [on_event][Self::on_event].
@@ -1287,7 +1287,7 @@ pub trait Filter {
     /// 
     /// Note: If a Filter will perform
     /// dynamic filtering that depends on the current context in which
-    /// a span or event was observered (e.g. only enabling an event when it
+    /// a span or event was observed (e.g. only enabling an event when it
     /// occurs within a particular span), it must return
     /// Interest::sometimes() from this method. If it returns
     /// Interest::always() or Interest::never(), the
@@ -1306,7 +1306,7 @@ pub trait Filter {
     /// other hand, when a `Filter` returns [`Interest::always()`][always] or
     /// [`Interest::never()`][never] for a callsite, _other_ [`Subscribe`]s may have
     /// differing interests in that callsite. If this is the case, the callsite
-    /// will recieve [`Interest::sometimes()`][sometimes], and the [`enabled`]
+    /// will receive [`Interest::sometimes()`][sometimes], and the [`enabled`]
     /// method will still be called for that callsite when it records a span or
     /// event.
     ///
diff --git a/tracing/CHANGELOG.md b/tracing/CHANGELOG.md
index 0a6c648e5c..b0af15f7b7 100644
--- a/tracing/CHANGELOG.md
+++ b/tracing/CHANGELOG.md
@@ -78,7 +78,7 @@ this release!
   filtering, improving performance when a span or event is disabled by a
   `static_max_level_XXX` feature flag (#868) 
 - `LevelFilter` is now a re-export of the `tracing_core::LevelFilter` type, it
-  can now be used interchangably with the versions in `tracing-core` and
+  can now be used interchangeably with the versions in `tracing-core` and
   `tracing-subscriber` (#853)
 - Significant performance improvements when comparing `LevelFilter`s and
   `Level`s (#853)
diff --git a/tracing/README.md b/tracing/README.md
index f5852b818d..73f5e75f08 100644
--- a/tracing/README.md
+++ b/tracing/README.md
@@ -145,7 +145,7 @@ use tracing::{debug, error, info, span, warn, Level};
 
 // the `#[tracing::instrument]` attribute creates and enters a span
 // every time the instrumented function is called. The span is named after
-// the function or method. Paramaters passed to the function are recorded as fields.
+// the function or method. Parameters passed to the function are recorded as fields.
 #[tracing::instrument]
 pub fn shave(yak: usize) -> Result<(), Box> {
     // this creates an event at the DEBUG level with two fields:
@@ -186,7 +186,7 @@ pub fn shave_all(yaks: usize) -> usize {
 
         if let Err(ref error) = res {
             // Like spans, events can also use the field initialization shorthand.
-            // In this instance, `yak` is the field being initalized.
+            // In this instance, `yak` is the field being initialized.
             error!(yak, error = error.as_ref(), "failed to shave yak!");
         } else {
             yaks_shaved += 1;
diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs
index 1467c907f2..de08ec408f 100644
--- a/tracing/src/lib.rs
+++ b/tracing/src/lib.rs
@@ -173,7 +173,7 @@
 //! For functions which don't have built-in tracing support and can't have
 //! the `#[instrument]` attribute applied (such as from an external crate),
 //! the [`Span` struct][`Span`] has a [`in_scope()` method][`in_scope`]
-//! which can be used to easily wrap synchonous code in a span.
+//! which can be used to easily wrap synchronous code in a span.
 //!
 //! For example:
 //! ```rust
diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 3756186ff7..75ead0bcb7 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -3044,7 +3044,7 @@ macro_rules! fieldset {
         $crate::fieldset!(@ { $($out),*, $k } $($rest)*)
     };
 
-    // Remainder is unparseable, but exists --- must be format args!
+    // Remainder is unparsable, but exists --- must be format args!
     (@ { $(,)* $($out:expr),* } $($rest:tt)+) => {
         $crate::fieldset!(@ { "message", $($out),*, })
     };
diff --git a/tracing/tests/enabled.rs b/tracing/tests/enabled.rs
index 9c82309056..860d55639f 100644
--- a/tracing/tests/enabled.rs
+++ b/tracing/tests/enabled.rs
@@ -46,7 +46,7 @@ fn span_and_event() {
 
     let _guard = tracing::collect::set_default(collector);
 
-    // Ensure that the `_event` and `_span` alternatives work corretly
+    // Ensure that the `_event` and `_span` alternatives work correctly
     assert!(!tracing::event_enabled!(Level::TRACE));
     assert!(tracing::event_enabled!(Level::DEBUG));
     assert!(tracing::span_enabled!(Level::TRACE));

From aa50d0e61373bb4af1ba567963e396dd2a54eb22 Mon Sep 17 00:00:00 2001
From: Min Deng 
Date: Sun, 21 Jan 2024 02:40:06 +0800
Subject: [PATCH 075/101] docs: fix typo "synchonous" => "synchronous" (#2850)

## Motivation

Fix typo "synchonous" => "synchronous" in the crate level documentation.

From b461897c78a63faa8c88304b6276122564e43aaa Mon Sep 17 00:00:00 2001
From: Cijo Thomas 
Date: Wed, 24 Jan 2024 20:29:56 -0800
Subject: [PATCH 076/101] docs: Update README.md with correct link to
 tracing-etw (#2861)

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 62c2baf646..7bc9b7fcda 100644
--- a/README.md
+++ b/README.md
@@ -438,7 +438,7 @@ please let us know!)
 [Tracy]: https://github.com/wolfpld/tracy
 [`tracing-elastic-apm`]: https://crates.io/crates/tracing-elastic-apm
 [Elastic APM]: https://www.elastic.co/apm
-[`tracing-etw`]: https://github.com/microsoft/tracing-etw
+[`tracing-etw`]: https://github.com/microsoft/rust_win_etw/tree/main/win_etw_tracing
 [ETW]: https://docs.microsoft.com/en-us/windows/win32/etw/about-event-tracing
 [`sentry-tracing`]: https://crates.io/crates/sentry-tracing
 [Sentry]: https://sentry.io/welcome/

From baeba47cdaac9ed32d5ef3f6f1d7b0cc71ffdbdf Mon Sep 17 00:00:00 2001
From: Alex Saveau 
Date: Thu, 25 Jan 2024 09:21:29 -0800
Subject: [PATCH 077/101] use const `thread_local`s when possible (#2838)

This results in a substantial performance improvement,
and is compatible with our MSRV.

Signed-off-by: Alex Saveau 
Co-authored-by: Eliza Weisman 
---
 .../examples/sloggish/sloggish_collector.rs   |  2 +-
 tracing-core/src/dispatch.rs                  |  8 ++--
 .../src/filter/subscriber_filters/mod.rs      | 38 +++++++++++++------
 tracing-subscriber/src/fmt/fmt_subscriber.rs  |  2 +-
 tracing-subscriber/src/registry/sharded.rs    |  8 ++--
 5 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/examples/examples/sloggish/sloggish_collector.rs b/examples/examples/sloggish/sloggish_collector.rs
index 0cc71ee0fc..32bce14880 100644
--- a/examples/examples/sloggish/sloggish_collector.rs
+++ b/examples/examples/sloggish/sloggish_collector.rs
@@ -27,7 +27,7 @@ pub struct CurrentSpanPerThread {
 impl CurrentSpanPerThread {
     pub fn new() -> Self {
         thread_local! {
-            static CURRENT: RefCell> = RefCell::new(vec![]);
+            static CURRENT: RefCell> = const { RefCell::new(Vec::new()) };
         };
         Self { current: &CURRENT }
     }
diff --git a/tracing-core/src/dispatch.rs b/tracing-core/src/dispatch.rs
index 0945efab8c..ab3ccd4ba8 100644
--- a/tracing-core/src/dispatch.rs
+++ b/tracing-core/src/dispatch.rs
@@ -205,9 +205,11 @@ enum Kind {
 
 #[cfg(feature = "std")]
 thread_local! {
-    static CURRENT_STATE: State = State {
-        default: RefCell::new(None),
-        can_enter: Cell::new(true),
+    static CURRENT_STATE: State = const {
+        State {
+            default: RefCell::new(None),
+            can_enter: Cell::new(true),
+        }
     };
 }
 
diff --git a/tracing-subscriber/src/filter/subscriber_filters/mod.rs b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
index 7d98ee9d08..f0449484c9 100644
--- a/tracing-subscriber/src/filter/subscriber_filters/mod.rs
+++ b/tracing-subscriber/src/filter/subscriber_filters/mod.rs
@@ -99,11 +99,17 @@ pub struct FilterId(u64);
 ///
 /// [`Registry`]: crate::Registry
 /// [`Filter`]: crate::subscribe::Filter
-#[derive(Default, Copy, Clone, Eq, PartialEq)]
+#[derive(Copy, Clone, Eq, PartialEq)]
 pub(crate) struct FilterMap {
     bits: u64,
 }
 
+impl FilterMap {
+    pub(crate) const fn new() -> Self {
+        Self { bits: 0 }
+    }
+}
+
 /// The current state of `enabled` calls to per-subscriber filters on this
 /// thread.
 ///
@@ -145,7 +151,7 @@ pub(crate) struct FilterState {
 
 /// Extra counters added to `FilterState` used only to make debug assertions.
 #[cfg(debug_assertions)]
-#[derive(Debug, Default)]
+#[derive(Debug)]
 struct DebugCounters {
     /// How many per-subscriber filters have participated in the current `enabled`
     /// call?
@@ -156,8 +162,18 @@ struct DebugCounters {
     in_interest_pass: Cell,
 }
 
+#[cfg(debug_assertions)]
+impl DebugCounters {
+    const fn new() -> Self {
+        Self {
+            in_filter_pass: Cell::new(0),
+            in_interest_pass: Cell::new(0),
+        }
+    }
+}
+
 thread_local! {
-    pub(crate) static FILTERING: FilterState = FilterState::new();
+    pub(crate) static FILTERING: FilterState = const { FilterState::new() };
 }
 
 /// Extension trait adding [combinators] for combining [`Filter`].
@@ -1072,13 +1088,13 @@ impl fmt::Binary for FilterMap {
 // === impl FilterState ===
 
 impl FilterState {
-    fn new() -> Self {
+    const fn new() -> Self {
         Self {
-            enabled: Cell::new(FilterMap::default()),
+            enabled: Cell::new(FilterMap::new()),
             interest: RefCell::new(None),
 
             #[cfg(debug_assertions)]
-            counters: DebugCounters::default(),
+            counters: DebugCounters::new(),
         }
     }
 
@@ -1087,7 +1103,7 @@ impl FilterState {
         {
             let in_current_pass = self.counters.in_filter_pass.get();
             if in_current_pass == 0 {
-                debug_assert_eq!(self.enabled.get(), FilterMap::default());
+                debug_assert_eq!(self.enabled.get(), FilterMap::new());
             }
             self.counters.in_filter_pass.set(in_current_pass + 1);
             debug_assert_eq!(
@@ -1132,7 +1148,7 @@ impl FilterState {
                 #[cfg(debug_assertions)]
                 {
                     if this.counters.in_filter_pass.get() == 0 {
-                        debug_assert_eq!(this.enabled.get(), FilterMap::default());
+                        debug_assert_eq!(this.enabled.get(), FilterMap::new());
                     }
 
                     // Nothing enabled this event, we won't tick back down the
@@ -1169,7 +1185,7 @@ impl FilterState {
         {
             let in_current_pass = self.counters.in_filter_pass.get();
             if in_current_pass <= 1 {
-                debug_assert_eq!(self.enabled.get(), FilterMap::default());
+                debug_assert_eq!(self.enabled.get(), FilterMap::new());
             }
             self.counters
                 .in_filter_pass
@@ -1199,7 +1215,7 @@ impl FilterState {
         // a panic and the thread-local has been torn down, that's fine, just
         // ignore it ratehr than panicking.
         let _ = FILTERING.try_with(|filtering| {
-            filtering.enabled.set(FilterMap::default());
+            filtering.enabled.set(FilterMap::new());
 
             #[cfg(debug_assertions)]
             filtering.counters.in_filter_pass.set(0);
@@ -1225,7 +1241,7 @@ impl FilterState {
         let map = self.enabled.get();
         #[cfg(debug_assertions)]
         if self.counters.in_filter_pass.get() == 0 {
-            debug_assert_eq!(map, FilterMap::default());
+            debug_assert_eq!(map, FilterMap::new());
         }
 
         map
diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs
index 64f9dfc1bb..2dd539e6a5 100644
--- a/tracing-subscriber/src/fmt/fmt_subscriber.rs
+++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs
@@ -944,7 +944,7 @@ where
 
     fn on_event(&self, event: &Event<'_>, ctx: Context<'_, C>) {
         thread_local! {
-            static BUF: RefCell = RefCell::new(String::new());
+            static BUF: RefCell = const { RefCell::new(String::new()) };
         }
 
         BUF.with(|buf| {
diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs
index bd7587cd19..a6c06391fa 100644
--- a/tracing-subscriber/src/registry/sharded.rs
+++ b/tracing-subscriber/src/registry/sharded.rs
@@ -214,7 +214,7 @@ thread_local! {
     /// track how many subscribers have processed the close.
     /// For additional details, see [`CloseGuard`].
     ///
-    static CLOSE_COUNT: Cell = Cell::new(0);
+    static CLOSE_COUNT: Cell = const { Cell::new(0) };
 }
 
 impl Collect for Registry {
@@ -255,7 +255,7 @@ impl Collect for Registry {
                 data.filter_map = crate::filter::FILTERING.with(|filtering| filtering.filter_map());
                 #[cfg(debug_assertions)]
                 {
-                    if data.filter_map != FilterMap::default() {
+                    if data.filter_map != FilterMap::new() {
                         debug_assert!(self.has_per_subscriber_filters());
                     }
                 }
@@ -477,7 +477,7 @@ impl Default for DataInner {
         };
 
         Self {
-            filter_map: FilterMap::default(),
+            filter_map: FilterMap::new(),
             metadata: &NULL_METADATA,
             parent: None,
             ref_count: AtomicUsize::new(0),
@@ -522,7 +522,7 @@ impl Clear for DataInner {
             })
             .clear();
 
-        self.filter_map = FilterMap::default();
+        self.filter_map = FilterMap::new();
     }
 }
 

From 045b22ecd90318447b7973dc31baa118f812ec89 Mon Sep 17 00:00:00 2001
From: Tobias Bieniek 
Date: Thu, 15 Feb 2024 22:54:04 +0100
Subject: [PATCH 078/101] macros: Fix missing field prefixes (#2878)

## Motivation

In the simple macro cases with e.g. only "name" or only "target" these
prefixes exist, but for the more complicated cases like "name + target"
the prefixes were not piped through and silently ignored. This led to
the compiler complaining about `Value` not being implemented despite the
usage of the `?` or `%` prefixes:

Bildschirmfoto 2024-02-14 um 12 38 00


## Solution

This change adds the missing prefixes to the `$($k)` tokens, like they
already exist for the simple cases mentioned above.
---
 tracing/src/macros.rs   | 80 ++++++++++++++++++++---------------------
 tracing/tests/macros.rs | 75 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 115 insertions(+), 40 deletions(-)

diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 75ead0bcb7..ca841275a3 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -1307,10 +1307,10 @@ macro_rules! trace {
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1324,10 +1324,10 @@ macro_rules! trace {
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
@@ -1341,10 +1341,10 @@ macro_rules! trace {
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1358,10 +1358,10 @@ macro_rules! trace {
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1584,10 +1584,10 @@ macro_rules! debug {
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1601,10 +1601,10 @@ macro_rules! debug {
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1618,10 +1618,10 @@ macro_rules! debug {
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1635,10 +1635,10 @@ macro_rules! debug {
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1872,10 +1872,10 @@ macro_rules! info {
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1889,10 +1889,10 @@ macro_rules! info {
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
@@ -1906,10 +1906,10 @@ macro_rules! info {
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1923,10 +1923,10 @@ macro_rules! info {
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -2153,10 +2153,10 @@ macro_rules! warn {
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2170,10 +2170,10 @@ macro_rules! warn {
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
@@ -2187,10 +2187,10 @@ macro_rules! warn {
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2204,10 +2204,10 @@ macro_rules! warn {
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2430,10 +2430,10 @@ macro_rules! error {
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2447,10 +2447,10 @@ macro_rules! error {
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
@@ -2464,10 +2464,10 @@ macro_rules! error {
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2481,10 +2481,10 @@ macro_rules! error {
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index b6e568f4a6..e9966a5464 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -10,6 +10,16 @@ use tracing::{
     span, span_enabled, trace, trace_span, warn, warn_span, Level,
 };
 
+/// A type that implements `Display` and `Debug`, but not `Value`.
+#[derive(Debug)]
+struct DisplayDebug;
+
+impl ::std::fmt::Display for DisplayDebug {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        ::std::write!(f, "Foo")
+    }
+}
+
 // Tests that macros work across various invocation syntax.
 //
 // These are quite repetitive, and _could_ be generated by a macro. However,
@@ -581,6 +591,19 @@ fn trace() {
     trace!(target: "foo_events", ?foo, true, "message");
     trace!(target: "foo_events", %foo, true, "message");
     trace!(target: "foo_events", foo, true, "message");
+    let foo = DisplayDebug;
+    trace!(?foo);
+    trace!(%foo);
+    trace!(name: "foo", ?foo);
+    trace!(name: "foo", %foo);
+    trace!(name: "foo", ?foo, true, "message");
+    trace!(name: "foo", %foo, true, "message");
+    trace!(target: "foo_events", ?foo);
+    trace!(target: "foo_events", %foo);
+    trace!(target: "foo_events", ?foo, true, "message");
+    trace!(target: "foo_events", %foo, true, "message");
+    trace!(name: "foo", target: "foo_events", ?foo, true, "message");
+    trace!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -625,6 +648,19 @@ fn debug() {
     debug!(target: "foo_events", ?foo, true, "message");
     debug!(target: "foo_events", %foo, true, "message");
     debug!(target: "foo_events", foo, true, "message");
+    let foo = DisplayDebug;
+    debug!(?foo);
+    debug!(%foo);
+    debug!(name: "foo", ?foo);
+    debug!(name: "foo", %foo);
+    debug!(name: "foo", ?foo, true, "message");
+    debug!(name: "foo", %foo, true, "message");
+    debug!(target: "foo_events", ?foo);
+    debug!(target: "foo_events", %foo);
+    debug!(target: "foo_events", ?foo, true, "message");
+    debug!(target: "foo_events", %foo, true, "message");
+    debug!(name: "foo", target: "foo_events", ?foo, true, "message");
+    debug!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -669,6 +705,19 @@ fn info() {
     info!(target: "foo_events", ?foo, true, "message");
     info!(target: "foo_events", %foo, true, "message");
     info!(target: "foo_events", foo, true, "message");
+    let foo = DisplayDebug;
+    info!(?foo);
+    info!(%foo);
+    info!(name: "foo", ?foo);
+    info!(name: "foo", %foo);
+    info!(name: "foo", ?foo, true, "message");
+    info!(name: "foo", %foo, true, "message");
+    info!(target: "foo_events", ?foo);
+    info!(target: "foo_events", %foo);
+    info!(target: "foo_events", ?foo, true, "message");
+    info!(target: "foo_events", %foo, true, "message");
+    info!(name: "foo", target: "foo_events", ?foo, true, "message");
+    info!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -713,6 +762,19 @@ fn warn() {
     warn!(target: "foo_events", ?foo, true, "message");
     warn!(target: "foo_events", %foo, true, "message");
     warn!(target: "foo_events", foo, true, "message");
+    let foo = DisplayDebug;
+    warn!(?foo);
+    warn!(%foo);
+    warn!(name: "foo", ?foo);
+    warn!(name: "foo", %foo);
+    warn!(name: "foo", ?foo, true, "message");
+    warn!(name: "foo", %foo, true, "message");
+    warn!(target: "foo_events", ?foo);
+    warn!(target: "foo_events", %foo);
+    warn!(target: "foo_events", ?foo, true, "message");
+    warn!(target: "foo_events", %foo, true, "message");
+    warn!(name: "foo", target: "foo_events", ?foo, true, "message");
+    warn!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
@@ -757,6 +819,19 @@ fn error() {
     error!(target: "foo_events", ?foo, true, "message");
     error!(target: "foo_events", %foo, true, "message");
     error!(target: "foo_events", foo, true, "message");
+    let foo = DisplayDebug;
+    error!(?foo);
+    error!(%foo);
+    error!(name: "foo", ?foo);
+    error!(name: "foo", %foo);
+    error!(name: "foo", ?foo, true, "message");
+    error!(name: "foo", %foo, true, "message");
+    error!(target: "foo_events", ?foo);
+    error!(target: "foo_events", %foo);
+    error!(target: "foo_events", ?foo, true, "message");
+    error!(target: "foo_events", %foo, true, "message");
+    error!(name: "foo", target: "foo_events", ?foo, true, "message");
+    error!(name: "foo", target: "foo_events", %foo, true, "message");
 }
 
 #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]

From 0e3577f6f3995b92accee21e0737c25ef0f1953c Mon Sep 17 00:00:00 2001
From: Tobias Bieniek 
Date: Fri, 16 Feb 2024 20:48:38 +0100
Subject: [PATCH 079/101] macros: Fix non-simple macro usage without message
 (#2879)

If something like `warn!(?foo)` and `warn!(name: "foo", ?foo)` works, then `warn!(name: "foo", target: "foo_events", ?foo)` should arguably also work. Before this change the more complicated variants of the macros however required a message argument, due to the usage of the `+` specifier in the macro definitions. This commit changes the `+` (1 or more) to `*` (0 or more), which matches how the `field` tokens are used in the simpler variants of the macro.
---
 tracing/src/macros.rs   | 240 ++++++++++++++++++++--------------------
 tracing/tests/macros.rs |  15 +++
 2 files changed, 135 insertions(+), 120 deletions(-)

diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index ca841275a3..9f6bd7670d 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -1303,14 +1303,14 @@ macro_rules! trace {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1320,14 +1320,14 @@ macro_rules! trace {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::TRACE, {}, $($arg)+)
@@ -1337,14 +1337,14 @@ macro_rules! trace {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1354,14 +1354,14 @@ macro_rules! trace {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::TRACE, {}, $($arg)+)
@@ -1580,14 +1580,14 @@ macro_rules! debug {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1597,14 +1597,14 @@ macro_rules! debug {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1614,14 +1614,14 @@ macro_rules! debug {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1631,14 +1631,14 @@ macro_rules! debug {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::DEBUG, {}, $($arg)+)
@@ -1868,14 +1868,14 @@ macro_rules! info {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1885,14 +1885,14 @@ macro_rules! info {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::INFO, {}, $($arg)+)
@@ -1902,14 +1902,14 @@ macro_rules! info {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -1919,14 +1919,14 @@ macro_rules! info {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::INFO, {}, $($arg)+)
@@ -2149,14 +2149,14 @@ macro_rules! warn {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2166,14 +2166,14 @@ macro_rules! warn {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::WARN, {}, $($arg)+)
@@ -2183,14 +2183,14 @@ macro_rules! warn {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2200,14 +2200,14 @@ macro_rules! warn {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::WARN, {}, $($arg)+)
@@ -2426,14 +2426,14 @@ macro_rules! error {
     (name: $name:expr, target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2443,14 +2443,14 @@ macro_rules! error {
     (name: $name:expr, target: $target:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)+ })
+    (name: $name:expr, target: $target:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, target: $target, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, target: $target:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, target: $target, $crate::Level::ERROR, {}, $($arg)+)
@@ -2460,14 +2460,14 @@ macro_rules! error {
     (target: $target:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
+    (target: $target:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (target: $target:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(target: $target, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
@@ -2477,14 +2477,14 @@ macro_rules! error {
     (name: $name:expr, parent: $parent:expr, { $($field:tt)* }, $($arg:tt)* ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($field)* }, $($arg)*)
     );
-    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, $($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { $($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, ?$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { ?$($k).+ $($field)* })
     );
-    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)+ ) => (
-        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)+ })
+    (name: $name:expr, parent: $parent:expr, %$($k:ident).+ $($field:tt)* ) => (
+        $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, { %$($k).+ $($field)* })
     );
     (name: $name:expr, parent: $parent:expr, $($arg:tt)+ ) => (
         $crate::event!(name: $name, parent: $parent, $crate::Level::ERROR, {}, $($arg)+)
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index e9966a5464..8d58bf6107 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -591,6 +591,9 @@ fn trace() {
     trace!(target: "foo_events", ?foo, true, "message");
     trace!(target: "foo_events", %foo, true, "message");
     trace!(target: "foo_events", foo, true, "message");
+    trace!(name: "foo", target: "foo_events", ?foo);
+    trace!(name: "foo", target: "foo_events", %foo);
+    trace!(name: "foo", target: "foo_events", foo);
     let foo = DisplayDebug;
     trace!(?foo);
     trace!(%foo);
@@ -648,6 +651,9 @@ fn debug() {
     debug!(target: "foo_events", ?foo, true, "message");
     debug!(target: "foo_events", %foo, true, "message");
     debug!(target: "foo_events", foo, true, "message");
+    debug!(name: "foo", target: "foo_events", ?foo);
+    debug!(name: "foo", target: "foo_events", %foo);
+    debug!(name: "foo", target: "foo_events", foo);
     let foo = DisplayDebug;
     debug!(?foo);
     debug!(%foo);
@@ -705,6 +711,9 @@ fn info() {
     info!(target: "foo_events", ?foo, true, "message");
     info!(target: "foo_events", %foo, true, "message");
     info!(target: "foo_events", foo, true, "message");
+    info!(name: "foo", target: "foo_events", ?foo);
+    info!(name: "foo", target: "foo_events", %foo);
+    info!(name: "foo", target: "foo_events", foo);
     let foo = DisplayDebug;
     info!(?foo);
     info!(%foo);
@@ -762,6 +771,9 @@ fn warn() {
     warn!(target: "foo_events", ?foo, true, "message");
     warn!(target: "foo_events", %foo, true, "message");
     warn!(target: "foo_events", foo, true, "message");
+    warn!(name: "foo", target: "foo_events", ?foo);
+    warn!(name: "foo", target: "foo_events", %foo);
+    warn!(name: "foo", target: "foo_events", foo);
     let foo = DisplayDebug;
     warn!(?foo);
     warn!(%foo);
@@ -819,6 +831,9 @@ fn error() {
     error!(target: "foo_events", ?foo, true, "message");
     error!(target: "foo_events", %foo, true, "message");
     error!(target: "foo_events", foo, true, "message");
+    error!(name: "foo", target: "foo_events", ?foo);
+    error!(name: "foo", target: "foo_events", %foo);
+    error!(name: "foo", target: "foo_events", foo);
     let foo = DisplayDebug;
     error!(?foo);
     error!(%foo);

From 908cc432a5994f6e17c8f36e13c217dc40085704 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Ml=C3=A1dek?= 
Date: Fri, 1 Mar 2024 21:59:00 +0100
Subject: [PATCH 080/101] tracing: fix event macros with constant field names
 in the first position (#2883)

## Motivation

Const argumetns in `level!` macros do not work when in the first
position.

This also seems to have fixed #2748 where literals for fields names like
`info!("foo" = 2)` could not be used outside the `event!` macro.


Fixes #2837
Fixes #2738

## Solution

Previsously, `level!($(args:tt))` was forwarded to `event!(target: ...,
level: ..., { $(args:tt) })` but the added curly braces seem to have
prevented the `event` macro from correctly understanding the arguments
and it tried to pass them to `format!`.

With this change there may have some performance impact when expanding
the macros in most cases where the braces could have been added as it
will take one more step.

These are the two relevant `event!` blocks I believe, the new tests used
to expand to the first one (with empty fields), now they expand to the
latter:
```
    (target: $target:expr, $lvl:expr, { $($fields:tt)* }, $($arg:tt)+ ) => (
        $crate::event!(
            target: $target,
            $lvl,
            { message = $crate::__macro_support::format_args!($($arg)+), $($fields)* }
        )
    );
    (target: $target:expr, $lvl:expr, $($arg:tt)+ ) => (
        $crate::event!(target: $target, $lvl, { $($arg)+ })
    );
```
---
 tracing/src/macros.rs   |  5 ----
 tracing/tests/event.rs  | 54 +++++++++++++++++++++++++++++++++++++++++
 tracing/tests/macros.rs | 32 +++++++++++++++++++++++-
 3 files changed, 85 insertions(+), 6 deletions(-)

diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs
index 9f6bd7670d..5de45cb75b 100644
--- a/tracing/src/macros.rs
+++ b/tracing/src/macros.rs
@@ -1545,7 +1545,6 @@ macro_rules! trace {
         $crate::event!(
             target: module_path!(),
             $crate::Level::TRACE,
-            {},
             $($arg)+
         )
     );
@@ -1822,7 +1821,6 @@ macro_rules! debug {
         $crate::event!(
             target: module_path!(),
             $crate::Level::DEBUG,
-            {},
             $($arg)+
         )
     );
@@ -2110,7 +2108,6 @@ macro_rules! info {
         $crate::event!(
             target: module_path!(),
             $crate::Level::INFO,
-            {},
             $($arg)+
         )
     );
@@ -2391,7 +2388,6 @@ macro_rules! warn {
         $crate::event!(
             target: module_path!(),
             $crate::Level::WARN,
-            {},
             $($arg)+
         )
     );
@@ -2668,7 +2664,6 @@ macro_rules! error {
         $crate::event!(
             target: module_path!(),
             $crate::Level::ERROR,
-            {},
             $($arg)+
         )
     );
diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs
index a370b700cd..0af0602e96 100644
--- a/tracing/tests/event.rs
+++ b/tracing/tests/event.rs
@@ -416,6 +416,12 @@ fn constant_field_name() {
         )
     };
     let (collector, handle) = collector::mock()
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
+        .event(expect_event())
         .event(expect_event())
         .event(expect_event())
         .only()
@@ -439,6 +445,54 @@ fn constant_field_name() {
             },
             "quux"
         );
+        tracing::info!(
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "quux"
+        );
+        tracing::info!(
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "quux"
+        );
+        tracing::event!(
+            Level::INFO,
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "{}",
+            "quux"
+        );
+        tracing::event!(
+            Level::INFO,
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "{}",
+            "quux"
+        );
+        tracing::info!(
+            { std::convert::identity(FOO) } = "bar",
+            { "constant string" } = "also works",
+            foo.bar = "baz",
+            "{}",
+            "quux"
+        );
+        tracing::info!(
+            {
+                { std::convert::identity(FOO) } = "bar",
+                { "constant string" } = "also works",
+                foo.bar = "baz",
+            },
+            "{}",
+            "quux"
+        );
     });
 
     handle.assert_finished();
diff --git a/tracing/tests/macros.rs b/tracing/tests/macros.rs
index 8d58bf6107..a072389e24 100644
--- a/tracing/tests/macros.rs
+++ b/tracing/tests/macros.rs
@@ -555,12 +555,15 @@ fn trace() {
     trace!(foo = ?3, bar.baz = %2, quux = false);
     trace!(foo = 3, bar.baz = 2, quux = false);
     trace!(foo = 3, bar.baz = 3,);
+    trace!("foo" = 3, bar.baz = 3,);
+    trace!(foo = 3, "bar.baz" = 3,);
     trace!("foo");
     trace!("foo: {}", 3);
     trace!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     trace!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     trace!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     trace!({ foo = 3, bar.baz = 80 }, "quux");
+    trace!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     trace!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     trace!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     trace!({ foo = 2, bar.baz = 78 }, "quux");
@@ -579,6 +582,9 @@ fn trace() {
     trace!(?foo);
     trace!(%foo);
     trace!(foo);
+    trace!("foo" = ?foo);
+    trace!("foo" = %foo);
+    trace!("foo" = foo);
     trace!(name: "foo", ?foo);
     trace!(name: "foo", %foo);
     trace!(name: "foo", foo);
@@ -615,12 +621,15 @@ fn debug() {
     debug!(foo = ?3, bar.baz = %2, quux = false);
     debug!(foo = 3, bar.baz = 2, quux = false);
     debug!(foo = 3, bar.baz = 3,);
+    debug!("foo" = 3, bar.baz = 3,);
+    debug!(foo = 3, "bar.baz" = 3,);
     debug!("foo");
     debug!("foo: {}", 3);
     debug!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     debug!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     debug!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     debug!({ foo = 3, bar.baz = 80 }, "quux");
+    debug!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     debug!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     debug!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     debug!({ foo = 2, bar.baz = 78 }, "quux");
@@ -639,6 +648,9 @@ fn debug() {
     debug!(?foo);
     debug!(%foo);
     debug!(foo);
+    debug!("foo" = ?foo);
+    debug!("foo" = %foo);
+    debug!("foo" = foo);
     debug!(name: "foo", ?foo);
     debug!(name: "foo", %foo);
     debug!(name: "foo", foo);
@@ -675,12 +687,15 @@ fn info() {
     info!(foo = ?3, bar.baz = %2, quux = false);
     info!(foo = 3, bar.baz = 2, quux = false);
     info!(foo = 3, bar.baz = 3,);
+    info!("foo" = 3, bar.baz = 3,);
+    info!(foo = 3, "bar.baz" = 3,);
     info!("foo");
     info!("foo: {}", 3);
     info!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     info!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     info!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     info!({ foo = 3, bar.baz = 80 }, "quux");
+    info!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     info!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     info!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     info!({ foo = 2, bar.baz = 78 }, "quux");
@@ -699,6 +714,9 @@ fn info() {
     info!(?foo);
     info!(%foo);
     info!(foo);
+    info!("foo" = ?foo);
+    info!("foo" = %foo);
+    info!("foo" = foo);
     info!(name: "foo", ?foo);
     info!(name: "foo", %foo);
     info!(name: "foo", foo);
@@ -735,12 +753,15 @@ fn warn() {
     warn!(foo = ?3, bar.baz = %2, quux = false);
     warn!(foo = 3, bar.baz = 2, quux = false);
     warn!(foo = 3, bar.baz = 3,);
+    warn!("foo" = 3, bar.baz = 3,);
+    warn!(foo = 3, "bar.baz" = 3,);
     warn!("foo");
     warn!("foo: {}", 3);
     warn!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     warn!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     warn!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     warn!({ foo = 3, bar.baz = 80 }, "quux");
+    warn!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     warn!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     warn!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
     warn!({ foo = 2, bar.baz = 78 }, "quux");
@@ -759,6 +780,9 @@ fn warn() {
     warn!(?foo);
     warn!(%foo);
     warn!(foo);
+    warn!("foo" = ?foo);
+    warn!("foo" = %foo);
+    warn!("foo" = foo);
     warn!(name: "foo", ?foo);
     warn!(name: "foo", %foo);
     warn!(name: "foo", foo);
@@ -795,15 +819,18 @@ fn error() {
     error!(foo = ?3, bar.baz = %2, quux = false);
     error!(foo = 3, bar.baz = 2, quux = false);
     error!(foo = 3, bar.baz = 3,);
+    error!("foo" = 3, bar.baz = 3,);
+    error!(foo = 3, "bar.baz" = 3,);
     error!("foo");
     error!("foo: {}", 3);
     error!(foo = ?3, bar.baz = %2, quux = false, "hello world {:?}", 42);
     error!(foo = 3, bar.baz = 2, quux = false, "hello world {:?}", 42);
     error!(foo = 3, bar.baz = 3, "hello world {:?}", 42,);
     error!({ foo = 3, bar.baz = 80 }, "quux");
+    error!({ "foo" = 3, "bar.baz" = 80 }, "quux");
     error!({ foo = 2, bar.baz = 79 }, "quux {:?}", true);
     error!({ foo = 2, bar.baz = 79 }, "quux {:?}, {quux}", true, quux = false);
-    error!({ foo = 2, bar.baz = 78, }, "quux");
+    error!({ foo = 2, bar.baz = 78 }, "quux");
     error!({ foo = ?2, bar.baz = %78 }, "quux");
     error!(name: "foo", foo = 3, bar.baz = 2, quux = false);
     error!(name: "foo", target: "foo_events", foo = 3, bar.baz = 2, quux = false);
@@ -819,6 +846,9 @@ fn error() {
     error!(?foo);
     error!(%foo);
     error!(foo);
+    error!("foo" = ?foo);
+    error!("foo" = %foo);
+    error!("foo" = foo);
     error!(name: "foo", ?foo);
     error!(name: "foo", %foo);
     error!(name: "foo", foo);

From 9adfa7bebddc534aedc23db74bbc0d4de36cbb2a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Dawid=20Ci=C4=99=C5=BCarkiewicz?= 
Date: Thu, 2 May 2024 04:25:51 -0700
Subject: [PATCH 081/101] chore: new clippy/rust lints (#2948)

## Motivation

clippy is failing with recent Rust toolchain up to 1.77.2.

## Solution

Give clippy what it wants.
---
 tracing-attributes/src/expand.rs           | 1 +
 tracing-attributes/tests/instrument.rs     | 1 +
 tracing-core/src/dispatch.rs               | 1 +
 tracing-core/src/field.rs                  | 2 ++
 tracing-futures/tests/std_future.rs        | 1 +
 tracing-subscriber/src/registry/sharded.rs | 1 +
 tracing/tests/instrument.rs                | 1 +
 7 files changed, 8 insertions(+)

diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs
index a92ef58aee..b52cb12aba 100644
--- a/tracing-attributes/src/expand.rs
+++ b/tracing-attributes/src/expand.rs
@@ -795,6 +795,7 @@ impl<'a> VisitMut for IdentAndTypesRenamer<'a> {
 }
 
 // A visitor struct that replace an async block by its patched version
+#[allow(dead_code)]
 struct AsyncTraitBlockReplacer<'a> {
     block: &'a Block,
     patched_block: Block,
diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs
index 0376be4069..957567dcf9 100644
--- a/tracing-attributes/tests/instrument.rs
+++ b/tracing-attributes/tests/instrument.rs
@@ -100,6 +100,7 @@ fn fields() {
 
 #[test]
 fn skip() {
+    #[allow(dead_code)]
     struct UnDebug(pub u32);
 
     #[instrument(target = "my_target", level = "debug", skip(_arg2, _arg3))]
diff --git a/tracing-core/src/dispatch.rs b/tracing-core/src/dispatch.rs
index ab3ccd4ba8..53263b494d 100644
--- a/tracing-core/src/dispatch.rs
+++ b/tracing-core/src/dispatch.rs
@@ -510,6 +510,7 @@ pub(crate) fn get_global() -> &'static Dispatch {
     unsafe {
         // This is safe given the invariant that setting the global dispatcher
         // also sets `GLOBAL_INIT` to `INITIALIZED`.
+        #[allow(static_mut_refs)]
         &GLOBAL_DISPATCH
     }
 }
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index d7a01bb814..d9ba1707cd 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -966,6 +966,7 @@ mod test {
     use crate::metadata::{Kind, Level, Metadata};
 
     // Make sure TEST_CALLSITE_* have non-zero size, so they can't be located at the same address.
+    #[allow(dead_code)]
     struct TestCallsite1(u8);
     static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1(0);
     static TEST_META_1: Metadata<'static> = metadata! {
@@ -987,6 +988,7 @@ mod test {
         }
     }
 
+    #[allow(dead_code)]
     struct TestCallsite2(u8);
     static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2(0);
     static TEST_META_2: Metadata<'static> = metadata! {
diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs
index d550896524..d8ec669175 100644
--- a/tracing-futures/tests/std_future.rs
+++ b/tracing-futures/tests/std_future.rs
@@ -55,6 +55,7 @@ fn span_on_drop() {
         }
     }
 
+    #[allow(dead_code)]
     struct Fut(Option);
 
     impl Future for Fut {
diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs
index a6c06391fa..c915109fe5 100644
--- a/tracing-subscriber/src/registry/sharded.rs
+++ b/tracing-subscriber/src/registry/sharded.rs
@@ -592,6 +592,7 @@ mod tests {
         closed: Vec<(&'static str, Weak<()>)>,
     }
 
+    #[allow(dead_code)]
     struct SetRemoved(Arc<()>);
 
     impl Subscribe for CloseSubscriber
diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs
index 9cb4dfbb3f..6e48de6bfc 100644
--- a/tracing/tests/instrument.rs
+++ b/tracing/tests/instrument.rs
@@ -21,6 +21,7 @@ fn span_on_drop() {
         }
     }
 
+    #[allow(dead_code)]
     struct Fut(Option);
 
     impl Future for Fut {

From 2c5a1f011e81d2cf404c8fe4d07de1969db4632f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Ml=C3=A1dek?= 
Date: Fri, 3 May 2024 19:04:03 +0200
Subject: [PATCH 082/101] chore: fix clippy lints (#2961)

## Motivation

New lints were added to 1.78 which breaks CI.

## Solution

New lints are fixed. I also fixed lints present in the beta channel for 1.79.
---
 tracing-appender/Cargo.toml                 |  2 +-
 tracing-core/src/field.rs                   |  4 ++--
 tracing-subscriber/Cargo.toml               |  2 +-
 tracing-subscriber/src/filter/env/field.rs  |  4 ++--
 tracing-subscriber/src/fmt/time/datetime.rs | 13 ++++++-------
 tracing-subscriber/src/lib.rs               |  4 ++++
 tracing-subscriber/src/registry/sharded.rs  |  6 +-----
 tracing/Cargo.toml                          |  4 ++--
 tracing/src/span.rs                         | 14 ++++++++------
 9 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/tracing-appender/Cargo.toml b/tracing-appender/Cargo.toml
index 28870f18f3..e8f21ff3a8 100644
--- a/tracing-appender/Cargo.toml
+++ b/tracing-appender/Cargo.toml
@@ -33,7 +33,7 @@ default-features = false
 features = ["fmt", "std"]
 
 [dev-dependencies]
-criterion = { version = "0.3.6", default_features = false }
+criterion = { version = "0.3.6", default-features = false }
 tracing = { path = "../tracing", version = "0.2" }
 time = { version = "0.3.2", default-features = false, features = ["formatting", "parsing"] }
 tempfile = "3.3.0"
diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index d9ba1707cd..663a98b49a 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -713,9 +713,9 @@ impl FieldSet {
     /// Returns the [`Field`] named `name`, or `None` if no such field exists.
     ///
     /// [`Field`]: super::Field
-    pub fn field(&self, name: &Q) -> Option
+    pub fn field(&self, name: &Q) -> Option
     where
-        Q: Borrow,
+        Q: Borrow + ?Sized,
     {
         let name = &name.borrow();
         self.names.iter().position(|f| f == name).map(|i| Field {
diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml
index 524bf4b482..92efbff841 100644
--- a/tracing-subscriber/Cargo.toml
+++ b/tracing-subscriber/Cargo.toml
@@ -70,7 +70,7 @@ tracing = { path = "../tracing", version = "0.2" }
 tracing-mock = { path = "../tracing-mock", features = ["tracing-subscriber"] }
 log = "0.4.17"
 tracing-log = { path = "../tracing-log", version = "0.2" }
-criterion = { version = "0.3.6", default_features = false }
+criterion = { version = "0.3.6", default-features = false }
 regex = { version = "1.6.0", default-features = false, features = ["std"] }
 tracing-futures = { path = "../tracing-futures", version = "0.3", default-features = false, features = ["std-future", "std"] }
 tokio = { version = "1.20.0", features = ["rt", "macros"] }
diff --git a/tracing-subscriber/src/filter/env/field.rs b/tracing-subscriber/src/filter/env/field.rs
index 211d2bded9..d6d97afe3b 100644
--- a/tracing-subscriber/src/filter/env/field.rs
+++ b/tracing-subscriber/src/filter/env/field.rs
@@ -267,7 +267,7 @@ impl fmt::Display for ValueMatch {
         match self {
             ValueMatch::Bool(ref inner) => fmt::Display::fmt(inner, f),
             ValueMatch::F64(ref inner) => fmt::Display::fmt(inner, f),
-            ValueMatch::NaN => fmt::Display::fmt(&std::f64::NAN, f),
+            ValueMatch::NaN => fmt::Display::fmt(&f64::NAN, f),
             ValueMatch::I64(ref inner) => fmt::Display::fmt(inner, f),
             ValueMatch::U64(ref inner) => fmt::Display::fmt(inner, f),
             ValueMatch::Debug(ref inner) => fmt::Display::fmt(inner, f),
@@ -507,7 +507,7 @@ impl<'a> Visit for MatchVisitor<'a> {
                 matched.store(true, Release);
             }
             Some((ValueMatch::F64(ref e), ref matched))
-                if (value - *e).abs() < std::f64::EPSILON =>
+                if (value - *e).abs() < f64::EPSILON =>
             {
                 matched.store(true, Release);
             }
diff --git a/tracing-subscriber/src/fmt/time/datetime.rs b/tracing-subscriber/src/fmt/time/datetime.rs
index a39d2ef19c..e7f58f9890 100644
--- a/tracing-subscriber/src/fmt/time/datetime.rs
+++ b/tracing-subscriber/src/fmt/time/datetime.rs
@@ -247,12 +247,12 @@ impl From for DateTime {
     fn from(timestamp: std::time::SystemTime) -> DateTime {
         let (t, nanos) = match timestamp.duration_since(std::time::UNIX_EPOCH) {
             Ok(duration) => {
-                debug_assert!(duration.as_secs() <= std::i64::MAX as u64);
+                debug_assert!(duration.as_secs() <= i64::MAX as u64);
                 (duration.as_secs() as i64, duration.subsec_nanos())
             }
             Err(error) => {
                 let duration = error.duration();
-                debug_assert!(duration.as_secs() <= std::i64::MAX as u64);
+                debug_assert!(duration.as_secs() <= i64::MAX as u64);
                 let (secs, nanos) = (duration.as_secs() as i64, duration.subsec_nanos());
                 if nanos == 0 {
                     (-secs, 0)
@@ -332,7 +332,6 @@ impl From for DateTime {
 
 #[cfg(test)]
 mod tests {
-    use std::i32;
     use std::time::{Duration, UNIX_EPOCH};
 
     use super::*;
@@ -382,8 +381,8 @@ mod tests {
             1,
         );
 
-        case("2038-01-19T03:14:07.000000Z", std::i32::MAX as i64, 0);
-        case("2038-01-19T03:14:08.000000Z", std::i32::MAX as i64 + 1, 0);
+        case("2038-01-19T03:14:07.000000Z", i32::MAX as i64, 0);
+        case("2038-01-19T03:14:08.000000Z", i32::MAX as i64 + 1, 0);
         case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0);
         case("1901-12-13T20:45:51.000000Z", i32::MIN as i64 - 1, 0);
 
@@ -392,8 +391,8 @@ mod tests {
         // high date value tests to panic
         #[cfg(not(target_os = "windows"))]
         {
-            case("+292277026596-12-04T15:30:07.000000Z", std::i64::MAX, 0);
-            case("+292277026596-12-04T15:30:06.000000Z", std::i64::MAX - 1, 0);
+            case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0);
+            case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0);
             case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0);
         }
 
diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs
index 4b1ce72d82..8f390e19e6 100644
--- a/tracing-subscriber/src/lib.rs
+++ b/tracing-subscriber/src/lib.rs
@@ -210,7 +210,11 @@ feature! {
     #![all(feature = "registry", feature = "std")]
     pub use registry::Registry;
 
+    /// Creates a default [`Registry`], a [`Collect`](tracing_core::Collect)
+    /// implementation which tracks per-span data and exposes it to
+    /// [`Subscribe`]s.
     ///
+    /// For more information see [`Registry`].
     pub fn registry() -> Registry {
         Registry::default()
     }
diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs
index c915109fe5..0ebcdf9a58 100644
--- a/tracing-subscriber/src/registry/sharded.rs
+++ b/tracing-subscriber/src/registry/sharded.rs
@@ -348,7 +348,7 @@ impl Collect for Registry {
 
         let refs = span.ref_count.fetch_sub(1, Ordering::Release);
         if !std::thread::panicking() {
-            assert!(refs < std::usize::MAX, "reference count overflow!");
+            assert!(refs < usize::MAX, "reference count overflow!");
         }
         if refs > 1 {
             return false;
@@ -541,10 +541,6 @@ mod tests {
         Collect,
     };
 
-    #[derive(Debug)]
-    struct DoesNothing;
-    impl Subscribe for DoesNothing {}
-
     struct AssertionSubscriber;
     impl Subscribe for AssertionSubscriber
     where
diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml
index f01783c293..a653458fab 100644
--- a/tracing/Cargo.toml
+++ b/tracing/Cargo.toml
@@ -37,8 +37,8 @@ tracing-attributes = { path = "../tracing-attributes", version = "0.2", optional
 pin-project-lite = "0.2.9"
 
 [dev-dependencies]
-criterion = { version = "0.3.6", default_features = false }
-futures = { version = "0.3.21", default_features = false }
+criterion = { version = "0.3.6", default-features = false }
+futures = { version = "0.3.21", default-features = false }
 log = "0.4.17"
 tracing-mock = { path = "../tracing-mock" }
 
diff --git a/tracing/src/span.rs b/tracing/src/span.rs
index ed58997b5a..fe69683171 100644
--- a/tracing/src/span.rs
+++ b/tracing/src/span.rs
@@ -1105,9 +1105,9 @@ impl Span {
 
     /// Returns a [`Field`](super::field::Field) for the field with the
     /// given `name`, if one exists,
-    pub fn field(&self, field: &Q) -> Option
+    pub fn field(&self, field: &Q) -> Option
     where
-        Q: field::AsField,
+        Q: field::AsField + ?Sized,
     {
         self.metadata().and_then(|meta| field.as_field(meta))
     }
@@ -1115,9 +1115,9 @@ impl Span {
     /// Returns true if this `Span` has a field for the given
     /// [`Field`](super::field::Field) or field name.
     #[inline]
-    pub fn has_field(&self, field: &Q) -> bool
+    pub fn has_field(&self, field: &Q) -> bool
     where
-        Q: field::AsField,
+        Q: field::AsField + ?Sized,
     {
         self.field(field).is_some()
     }
@@ -1193,9 +1193,9 @@ impl Span {
     ///
     /// [`field::Empty`]: super::field::Empty
     /// [`Metadata`]: super::Metadata
-    pub fn record(&self, field: &Q, value: V) -> &Self
+    pub fn record(&self, field: &Q, value: V) -> &Self
     where
-        Q: field::AsField,
+        Q: field::AsField + ?Sized,
         V: field::Value,
     {
         if let Some(meta) = self.meta {
@@ -1594,9 +1594,11 @@ unsafe impl Sync for PhantomNotSend {}
 mod test {
     use super::*;
 
+    #[allow(dead_code)]
     trait AssertSend: Send {}
     impl AssertSend for Span {}
 
+    #[allow(dead_code)]
     trait AssertSync: Sync {}
     impl AssertSync for Span {}
     impl AssertSync for Entered<'_> {}

From 690a9a68098aa1eb86d1c5c4aad085ef12245bca Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?David=20Ml=C3=A1dek?= 
Date: Fri, 3 May 2024 19:17:38 +0200
Subject: [PATCH 083/101] tracing: allow `&[u8]` to be recorded as event/span
 field (#2954)

## Motivation

Users may want to pass data to `Subscribe`s which is not valid UTF-8. Currently, it would have to be encoded into `&str` to be passed as a field value.

## Solution

This branch adds a `record_bytes` method to `Visit`. It has an implementation falling back to `record_debug` so it is not be a breaking change.

`JsonVisitor` got an overridden implementation as it should not use `Debug` output and encode it as a string, but rather store the bytes as an array.

`PrettyVisitor` go an overridden implementation to make the output more pretty.

Co-authored-by: Eliza Weisman 
---
 tracing-core/src/field.rs                 | 54 ++++++++++++++++++++++-
 tracing-subscriber/src/fmt/format/json.rs | 15 ++++++-
 2 files changed, 66 insertions(+), 3 deletions(-)

diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs
index 663a98b49a..fbaa50dec4 100644
--- a/tracing-core/src/field.rs
+++ b/tracing-core/src/field.rs
@@ -38,7 +38,7 @@
 use crate::callsite;
 use core::{
     borrow::Borrow,
-    fmt,
+    fmt::{self, Write},
     hash::{Hash, Hasher},
     num,
     ops::Range,
@@ -224,6 +224,11 @@ pub trait Visit {
         self.record_debug(field, &value)
     }
 
+    /// Visit a byte slice.
+    fn record_bytes(&mut self, field: &Field, value: &[u8]) {
+        self.record_debug(field, &HexBytes(value))
+    }
+
     /// Records a type implementing `Error`.
     ///
     /// 
@@ -283,6 +288,26 @@ where DebugValue(t) } +struct HexBytes<'a>(&'a [u8]); + +impl<'a> fmt::Debug for HexBytes<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_char('[')?; + + let mut bytes = self.0.iter(); + + if let Some(byte) = bytes.next() { + f.write_fmt(format_args!("{byte:02x}"))?; + } + + for byte in bytes { + f.write_fmt(format_args!(" {byte:02x}"))?; + } + + f.write_char(']') + } +} + // ===== impl Visit ===== impl<'a, 'b> Visit for fmt::DebugStruct<'a, 'b> { @@ -443,6 +468,14 @@ impl Value for str { } } +impl crate::sealed::Sealed for [u8] {} + +impl Value for [u8] { + fn record(&self, key: &Field, visitor: &mut dyn Visit) { + visitor.record_bytes(key, self) + } +} + #[cfg(feature = "std")] impl crate::sealed::Sealed for dyn std::error::Error + 'static {} @@ -1131,4 +1164,23 @@ mod test { }); assert_eq!(result, format!("{}", err)); } + + #[test] + fn record_bytes() { + let fields = TEST_META_1.fields(); + let first = &b"abc"[..]; + let second: &[u8] = &[192, 255, 238]; + let values = &[ + (&fields.field("foo").unwrap(), Some(&first as &dyn Value)), + (&fields.field("bar").unwrap(), Some(&" " as &dyn Value)), + (&fields.field("baz").unwrap(), Some(&second as &dyn Value)), + ]; + let valueset = fields.value_set(values); + let mut result = String::new(); + valueset.record(&mut |_: &Field, value: &dyn fmt::Debug| { + use core::fmt::Write; + write!(&mut result, "{:?}", value).unwrap(); + }); + assert_eq!(result, format!("{}", r#"[61 62 63]" "[c0 ff ee]"#)); + } } diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index f4e61fb123..1f045d93b5 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -488,6 +488,11 @@ impl<'a> field::Visit for JsonVisitor<'a> { .insert(field.name(), serde_json::Value::from(value)); } + fn record_bytes(&mut self, field: &Field, value: &[u8]) { + self.values + .insert(field.name(), serde_json::Value::from(value)); + } + fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { match field.name() { // Skip fields that are actually log metadata that have already been handled @@ -528,13 +533,19 @@ mod test { #[test] fn json() { let expected = - "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; + "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3,\"slice\":[97,98,99]}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let collector = collector() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, collector, || { - let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); + let span = tracing::span!( + tracing::Level::INFO, + "json_span", + answer = 42, + number = 3, + slice = &b"abc"[..] + ); let _guard = span.enter(); tracing::info!("some json test"); }); From d61b03979222f7e8205df2ba4e6679211ea50591 Mon Sep 17 00:00:00 2001 From: Jonas Platte <158304798+svix-jplatte@users.noreply.github.com> Date: Tue, 21 May 2024 16:58:12 +0200 Subject: [PATCH 084/101] chore: fix warnings about unexpected `cfg(feature)` values (#2968) * futures: Remove executor::futures_preview module The cfg for it was impossible to enable as of https://github.com/tokio-rs/tracing/pull/441. * subscriber: Fix cfgs for nu-ansi-term Previously, enabling this optional dependency did not do anything. * Unconditionally ignore flaky tests No need to use a custom cfg to run off-by-default tests. * tower: Delete unused code It might be possible to make it work with tower_make instead, but that crate looks to be unmaintained (no longer present in the tower repo which is specified as the repository URL) so it should likely just be removed as a dependency. --- .../src/executor/futures_preview.rs | 121 ------------------ tracing-futures/src/executor/mod.rs | 5 - tracing-subscriber/src/filter/env/builder.rs | 14 +- tracing-subscriber/tests/field_filter.rs | 4 +- tracing-tower/src/lib.rs | 25 ---- tracing-tower/src/service_span.rs | 4 - 6 files changed, 9 insertions(+), 164 deletions(-) delete mode 100644 tracing-futures/src/executor/futures_preview.rs diff --git a/tracing-futures/src/executor/futures_preview.rs b/tracing-futures/src/executor/futures_preview.rs deleted file mode 100644 index fec9e3fadb..0000000000 --- a/tracing-futures/src/executor/futures_preview.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{Instrument, Instrumented, WithDispatch}; -use core::future::Future; -use futures_core_preview::{ - future::FutureObj, - task::{LocalSpawn, Spawn, SpawnError}, -}; - -impl Spawn for Instrumented -where - T: Spawn, -{ - /// Spawns a future that will be run to completion. - /// - /// # Errors - /// - /// The executor may be unable to spawn tasks. Spawn errors should - /// represent relatively rare scenarios, such as the executor - /// having been shut down so that it is no longer able to accept - /// tasks. - fn spawn_obj(&mut self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { - let future = future.instrument(self.span.clone()); - self.inner.spawn_obj(Box::pin(future)) - } - - /// Determines whether the executor is able to spawn new tasks. - /// - /// This method will return `Ok` when the executor is *likely* - /// (but not guaranteed) to accept a subsequent spawn attempt. - /// Likewise, an `Err` return means that `spawn` is likely, but - /// not guaranteed, to yield an error. - #[inline] - fn status(&self) -> Result<(), SpawnError> { - self.inner.status() - } -} - -impl Spawn for WithDispatch -where - T: Spawn, -{ - /// Spawns a future that will be run to completion. - /// - /// # Errors - /// - /// The executor may be unable to spawn tasks. Spawn errors should - /// represent relatively rare scenarios, such as the executor - /// having been shut down so that it is no longer able to accept - /// tasks. - fn spawn_obj(&mut self, future: FutureObj<'static, ()>) -> Result<(), SpawnError> { - self.inner.spawn_obj(Box::pin(self.with_dispatch(future))) - } - - /// Determines whether the executor is able to spawn new tasks. - /// - /// This method will return `Ok` when the executor is *likely* - /// (but not guaranteed) to accept a subsequent spawn attempt. - /// Likewise, an `Err` return means that `spawn` is likely, but - /// not guaranteed, to yield an error. - #[inline] - fn status(&self) -> Result<(), SpawnError> { - self.inner.status() - } -} - -impl LocalSpawn for Instrumented -where - T: LocalSpawn, -{ - /// Spawns a future that will be run to completion. - /// - /// # Errors - /// - /// The executor may be unable to spawn tasks. Spawn errors should - /// represent relatively rare scenarios, such as the executor - /// having been shut down so that it is no longer able to accept - /// tasks. - fn spawn_local_obj(&mut self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { - let future = future.instrument(self.span.clone()); - self.inner.spawn_local_obj(Box::pin(future)) - } - - /// Determines whether the executor is able to spawn new tasks. - /// - /// This method will return `Ok` when the executor is *likely* - /// (but not guaranteed) to accept a subsequent spawn attempt. - /// Likewise, an `Err` return means that `spawn` is likely, but - /// not guaranteed, to yield an error. - #[inline] - fn status_local(&self) -> Result<(), SpawnError> { - self.inner.status_local() - } -} - -impl LocalSpawn for WithDispatch -where - T: Spawn, -{ - /// Spawns a future that will be run to completion. - /// - /// # Errors - /// - /// The executor may be unable to spawn tasks. Spawn errors should - /// represent relatively rare scenarios, such as the executor - /// having been shut down so that it is no longer able to accept - /// tasks. - fn spawn_local_obj(&mut self, future: LocalFutureObj<'static, ()>) -> Result<(), SpawnError> { - self.inner - .spawn_local_obj(Box::pin(self.with_dispatch(future))) - } - - /// Determines whether the executor is able to spawn new tasks. - /// - /// This method will return `Ok` when the executor is *likely* - /// (but not guaranteed) to accept a subsequent spawn attempt. - /// Likewise, an `Err` return means that `spawn` is likely, but - /// not guaranteed, to yield an error. - #[inline] - fn status_local(&self) -> Result<(), SpawnError> { - self.inner.status_local() - } -} diff --git a/tracing-futures/src/executor/mod.rs b/tracing-futures/src/executor/mod.rs index ced3b5a460..86bad9379c 100644 --- a/tracing-futures/src/executor/mod.rs +++ b/tracing-futures/src/executor/mod.rs @@ -1,11 +1,6 @@ #[cfg(feature = "futures-01")] mod futures_01; -#[cfg(feature = "futures_preview")] -mod futures_preview; -#[cfg(feature = "futures_preview")] -pub use self::futures_preview::*; - #[cfg(feature = "futures-03")] mod futures_03; #[cfg(feature = "futures-03")] diff --git a/tracing-subscriber/src/filter/env/builder.rs b/tracing-subscriber/src/filter/env/builder.rs index ebb7b45e72..8d126af2de 100644 --- a/tracing-subscriber/src/filter/env/builder.rs +++ b/tracing-subscriber/src/filter/env/builder.rs @@ -210,15 +210,15 @@ impl Builder { } if !disabled.is_empty() { - #[cfg(feature = "nu_ansi_term")] + #[cfg(feature = "nu-ansi-term")] use nu_ansi_term::{Color, Style}; // NOTE: We can't use a configured `MakeWriter` because the EnvFilter // has no knowledge of any underlying subscriber or collector, which // may or may not use a `MakeWriter`. let warn = |msg: &str| { - #[cfg(not(feature = "nu_ansi_term"))] + #[cfg(not(feature = "nu-ansi-term"))] let msg = format!("warning: {}", msg); - #[cfg(feature = "nu_ansi_term")] + #[cfg(feature = "nu-ansi-term")] let msg = { let bold = Style::new().bold(); let mut warning = Color::Yellow.paint("warning"); @@ -228,9 +228,9 @@ impl Builder { eprintln!("{}", msg); }; let ctx_prefixed = |prefix: &str, msg: &str| { - #[cfg(not(feature = "nu_ansi_term"))] + #[cfg(not(feature = "nu-ansi-term"))] let msg = format!("{} {}", prefix, msg); - #[cfg(feature = "nu_ansi_term")] + #[cfg(feature = "nu-ansi-term")] let msg = { let mut equal = Color::Fixed(21).paint("="); // dark blue equal.style_ref_mut().is_bold = true; @@ -241,9 +241,9 @@ impl Builder { let ctx_help = |msg| ctx_prefixed("help:", msg); let ctx_note = |msg| ctx_prefixed("note:", msg); let ctx = |msg: &str| { - #[cfg(not(feature = "nu_ansi_term"))] + #[cfg(not(feature = "nu-ansi-term"))] let msg = format!("note: {}", msg); - #[cfg(feature = "nu_ansi_term")] + #[cfg(feature = "nu-ansi-term")] let msg = { let mut pipe = Color::Fixed(21).paint("|"); pipe.style_ref_mut().is_bold = true; diff --git a/tracing-subscriber/tests/field_filter.rs b/tracing-subscriber/tests/field_filter.rs index 7aaa767184..17b6a75b4b 100644 --- a/tracing-subscriber/tests/field_filter.rs +++ b/tracing-subscriber/tests/field_filter.rs @@ -5,7 +5,7 @@ use tracing_mock::*; use tracing_subscriber::{filter::EnvFilter, prelude::*}; #[test] -#[cfg_attr(not(flaky_tests), ignore)] +#[ignore] // flaky, use `cargo test -- --ignored` or `--include-ignored` to run fn field_filter_events() { let filter: EnvFilter = "[{thing}]=debug".parse().expect("filter should parse"); let (subscriber, finished) = collector::mock() @@ -35,7 +35,7 @@ fn field_filter_events() { } #[test] -#[cfg_attr(not(flaky_tests), ignore)] +#[ignore] // flaky, use `cargo test -- --ignored` or `--include-ignored` to run fn field_filter_spans() { let filter: EnvFilter = "[{enabled=true}]=debug" .parse() diff --git a/tracing-tower/src/lib.rs b/tracing-tower/src/lib.rs index 4f06044d29..cd8449065b 100644 --- a/tracing-tower/src/lib.rs +++ b/tracing-tower/src/lib.rs @@ -71,33 +71,8 @@ where } } -#[cfg(feature = "tower-util")] -#[cfg_attr(docsrs, doc(cfg(feature = "tower-util")))] -pub trait InstrumentMake -where - Self: tower_util::MakeService + Sized, -{ - fn with_traced_service(self, get_span: G) -> service_span::MakeService - where - G: GetSpan, - { - service_span::MakeService::new(self, get_span) - } - - fn with_traced_requests(self, get_span: G) -> request_span::MakeService - where - G: GetSpan + Clone, - { - request_span::MakeService::new(self, get_span) - } -} - impl InstrumentableService for S where S: Service + Sized {} -#[cfg(feature = "tower-util")] -#[cfg_attr(docsrs, doc(cfg(feature = "tower-util")))] -impl InstrumentMake for M where M: tower_util::MakeService {} - pub trait GetSpan: crate::sealed::Sealed { fn span_for(&self, target: &T) -> tracing::Span; } diff --git a/tracing-tower/src/service_span.rs b/tracing-tower/src/service_span.rs index 95d74db998..464cd25355 100644 --- a/tracing-tower/src/service_span.rs +++ b/tracing-tower/src/service_span.rs @@ -16,10 +16,6 @@ pub struct Service { #[cfg_attr(docsrs, doc(cfg(feature = "tower-layer")))] pub use self::layer::*; -#[cfg(feature = "tower-util")] -#[cfg_attr(docsrs, doc(cfg(feature = "tower-util")))] -pub use self::make::MakeService; - #[cfg(feature = "tower-layer")] #[cfg_attr(docsrs, doc(cfg(feature = "tower-layer")))] mod layer { From e7aeee818f65265b834871b01b101e0d4b123fbc Mon Sep 17 00:00:00 2001 From: Logan Praneis Date: Sat, 25 May 2024 12:53:42 -0500 Subject: [PATCH 085/101] subscriber: add `set_span_events` to `fmt::Subscriber` (#2962) --- tracing-subscriber/src/fmt/fmt_subscriber.rs | 69 ++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index 2dd539e6a5..e3949b3361 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -240,6 +240,27 @@ impl Subscriber { self.is_ansi = ansi; } + /// Modifies how synthesized events are emitted at points in the [span + /// lifecycle][lifecycle]. + /// + /// See [`Self::with_span_events`] for documentation on the [`FmtSpan`] + /// + /// This method is primarily expected to be used with the + /// [`reload::Handle::modify`](crate::reload::Handle::modify) method + /// + /// Note that using this method modifies the span configuration instantly and does not take into + /// account any current spans. If the previous configuration was set to capture + /// `FmtSpan::ALL`, for example, using this method to change to `FmtSpan::NONE` will cause an + /// exit event for currently entered events not to be formatted + /// + /// [lifecycle]: mod@tracing::span#the-span-lifecycle + pub fn set_span_events(&mut self, kind: FmtSpan) { + self.fmt_span = format::FmtSpanConfig { + kind, + fmt_timing: self.fmt_span.fmt_timing, + } + } + /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in /// unit tests. /// @@ -1590,4 +1611,52 @@ mod test { // dropping `_saved_no_color` will restore the previous value of // `NO_COLOR`. } + + // Validates that span event configuration can be modified with a reload handle + #[test] + fn modify_span_events() { + let make_writer = MockMakeWriter::default(); + + let inner_subscriber = fmt::Subscriber::default() + .with_writer(make_writer.clone()) + .with_level(false) + .with_ansi(false) + .with_timer(MockTime) + .with_span_events(FmtSpan::ACTIVE); + + let (reloadable_subscriber, reload_handle) = + crate::reload::Subscriber::new(inner_subscriber); + let reload = reloadable_subscriber.with_collector(Registry::default()); + + with_default(reload, || { + { + let span1 = tracing::info_span!("span1", x = 42); + let _e = span1.enter(); + } + + let _ = reload_handle.modify(|s| s.set_span_events(FmtSpan::NONE)); + + // this span should not be logged at all! + { + let span2 = tracing::info_span!("span2", x = 100); + let _e = span2.enter(); + } + + { + let span3 = tracing::info_span!("span3", x = 42); + let _e = span3.enter(); + + // The span config was modified after span3 was already entered. + // We should only see an exit + let _ = reload_handle.modify(|s| s.set_span_events(FmtSpan::ACTIVE)); + } + }); + let actual = sanitize_timings(make_writer.get_string()); + assert_eq!( + "fake time span1{x=42}: tracing_subscriber::fmt::fmt_subscriber::test: enter\n\ + fake time span1{x=42}: tracing_subscriber::fmt::fmt_subscriber::test: exit\n\ + fake time span3{x=42}: tracing_subscriber::fmt::fmt_subscriber::test: exit\n", + actual.as_str() + ); + } } From 6d319a09e98f589da8e370326c3401fd30538622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dawid=20Ci=C4=99=C5=BCarkiewicz?= Date: Sat, 25 May 2024 11:08:34 -0700 Subject: [PATCH 086/101] tracing-attributes: support const values for `target` and `name` (#2941) Fixes #2960 Co-authored-by: Matthijs Brobbel Co-authored-by: Hayden Stainsby Co-authored-by: Eliza Weisman --- tracing-attributes/src/attr.rs | 32 ++++++++++-- tracing-attributes/tests/instrument.rs | 72 ++++++++++++++++++++++++++ 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/tracing-attributes/src/attr.rs b/tracing-attributes/src/attr.rs index 902ca1450b..4684326d62 100644 --- a/tracing-attributes/src/attr.rs +++ b/tracing-attributes/src/attr.rs @@ -17,8 +17,8 @@ pub(crate) struct EventArgs { #[derive(Clone, Default, Debug)] pub(crate) struct InstrumentArgs { level: Option, - pub(crate) name: Option, - target: Option, + pub(crate) name: Option, + target: Option, pub(crate) parent: Option, pub(crate) follows_from: Option, pub(crate) skips: HashSet, @@ -86,6 +86,8 @@ impl Parse for InstrumentArgs { // XXX: apparently we support names as either named args with an // sign, _or_ as unnamed string literals. That's weird, but // changing it is apparently breaking. + // This also means that when using idents for name, it must be via + // a named arg, i.e. `#[instrument(name = SOME_IDENT)]`. if args.name.is_some() { return Err(input.error("expected only a single `name` argument")); } @@ -198,8 +200,32 @@ impl Parse for EventArgs { } } +#[derive(Debug, Clone)] +pub(super) enum LitStrOrIdent { + LitStr(LitStr), + Ident(Ident), +} + +impl ToTokens for LitStrOrIdent { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + LitStrOrIdent::LitStr(target) => target.to_tokens(tokens), + LitStrOrIdent::Ident(ident) => ident.to_tokens(tokens), + } + } +} + +impl Parse for LitStrOrIdent { + fn parse(input: ParseStream<'_>) -> syn::Result { + input + .parse::() + .map(LitStrOrIdent::LitStr) + .or_else(|_| input.parse::().map(LitStrOrIdent::Ident)) + } +} + struct StrArg { - value: LitStr, + value: LitStrOrIdent, _p: std::marker::PhantomData, } diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index 957567dcf9..722919fad5 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -239,3 +239,75 @@ fn impl_trait_return_type() { handle.assert_finished(); } + +#[test] +fn name_ident() { + const MY_NAME: &str = "my_name"; + #[instrument(name = MY_NAME)] + fn name() {} + + let span_name = expect::span().named(MY_NAME); + + let (collector, handle) = collector::mock() + .new_span(span_name.clone()) + .enter(span_name.clone()) + .exit(span_name.clone()) + .drop_span(span_name) + .only() + .run_with_handle(); + + with_default(collector, || { + name(); + }); + + handle.assert_finished(); +} + +#[test] +fn target_ident() { + const MY_TARGET: &str = "my_target"; + + #[instrument(target = MY_TARGET)] + fn target() {} + + let span_target = expect::span().named("target").with_target(MY_TARGET); + + let (collector, handle) = collector::mock() + .new_span(span_target.clone()) + .enter(span_target.clone()) + .exit(span_target.clone()) + .drop_span(span_target) + .only() + .run_with_handle(); + + with_default(collector, || { + target(); + }); + + handle.assert_finished(); +} + +#[test] +fn target_name_ident() { + const MY_NAME: &str = "my_name"; + const MY_TARGET: &str = "my_target"; + + #[instrument(target = MY_TARGET, name = MY_NAME)] + fn name_target() {} + + let span_name_target = expect::span().named(MY_NAME).with_target(MY_TARGET); + + let (collector, handle) = collector::mock() + .new_span(span_name_target.clone()) + .enter(span_name_target.clone()) + .exit(span_name_target.clone()) + .drop_span(span_name_target) + .only() + .run_with_handle(); + + with_default(collector, || { + name_target(); + }); + + handle.assert_finished(); +} From 382ee01dc1cf428c071cad1e3f2e6c6427e70f87 Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Sat, 25 May 2024 22:54:05 +0200 Subject: [PATCH 087/101] docs: remove non-existing method in comments (#2894) In the documentation of the layer context span_scope method, the note contained a reference to a `scope()` method, which was removed some time ago. Also fixed a phrasing error above. Fixes: #2890 --- tracing-subscriber/src/subscribe/context.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tracing-subscriber/src/subscribe/context.rs b/tracing-subscriber/src/subscribe/context.rs index 183e1ceccb..d322d5dffa 100644 --- a/tracing-subscriber/src/subscribe/context.rs +++ b/tracing-subscriber/src/subscribe/context.rs @@ -315,15 +315,14 @@ where /// Returns an iterator over the [stored data] for all the spans in the /// current context, starting with the specified span and ending with the - /// root of the trace tree and ending with the current span. + /// root of the trace tree. /// ///
///
ⓘNote
///
///
From ba387ddc03f17eb91e70efe58ae007326ecb9cd4 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Fri, 21 Jun 2024 11:11:29 +0200 Subject: [PATCH 088/101] mock: add `ExpectedId` to link span expectations (#3007) ## Motivation It currently isn't possible to differentiate spans with the same name, target, and level when setting expectations on `enter`, `exit`, and `drop_span`. This is not an issue for `tracing-mock`'s original (and still primary) use case, which is to test `tracing` itself. However, when testing the tracing instrumentation in library or application code, this can be a limitation. For example, when testing the instrumentation in tokio (tokio-rs/tokio#6112), it isn't possible to set an expectation on which task span is entered first, because the name, target, and level of those spans are always identical - in fact, the spans have the same metadata and only the field values are different. ## Solution To make differentiating different spans possible, `ExpectId` has been introduced. It is an opaque struct which represents a `span::Id` and can be used to match spans from a `new_span` expectation (where a `NewSpan` is accepted and all fields and values can be expected) through to subsequent `enter`, `exit`, and `drop_span` expectations. An `ExpectedId` is passed to an `ExpectedSpan` which then needs to be expected with `MockCollector::new_span`. A clone of the `ExpectedId` (or a clone of the `ExpectedSpan` with the `ExpectedId` already on it) will then match the ID assigned to the span to the other span lifecycle expectations. The `ExpectedId` uses an `Arc` which has the ID for the new span assigned to it, and then its clones will be matched against that same ID. In future changes it will also be possible to use this `ExpectedId` to match parent spans, currently a parent is only matched by name. --- tracing-mock/src/collector.rs | 9 ++ tracing-mock/src/expect.rs | 21 +++- tracing-mock/src/span.rs | 194 +++++++++++++++++++++++++++++++++- 3 files changed, 222 insertions(+), 2 deletions(-) diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs index a91cdd332d..97a0148825 100644 --- a/tracing-mock/src/collector.rs +++ b/tracing-mock/src/collector.rs @@ -159,12 +159,17 @@ use tracing::{ }; pub(crate) struct SpanState { + id: u64, name: &'static str, refs: usize, meta: &'static Metadata<'static>, } impl SpanState { + pub(crate) fn id(&self) -> u64 { + self.id + } + pub(crate) fn metadata(&self) -> &'static Metadata<'static> { self.meta } @@ -1100,6 +1105,9 @@ where let mut spans = self.spans.lock().unwrap(); if was_expected { if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() { + if let Some(expected_id) = &expected.span.id { + expected_id.set(id.into_u64()).unwrap(); + } let get_parent_name = || { let stack = self.current.lock().unwrap(); span.parent() @@ -1113,6 +1121,7 @@ where spans.insert( id.clone(), SpanState { + id: id.into_u64(), name: meta.name(), refs: 1, meta, diff --git a/tracing-mock/src/expect.rs b/tracing-mock/src/expect.rs index 353bc52f5f..beb66d36f9 100644 --- a/tracing-mock/src/expect.rs +++ b/tracing-mock/src/expect.rs @@ -3,7 +3,7 @@ use std::fmt; use crate::{ event::ExpectedEvent, field::{ExpectedField, ExpectedFields, ExpectedValue}, - span::{ExpectedSpan, NewSpan}, + span::{ExpectedId, ExpectedSpan, NewSpan}, }; #[derive(Debug, Eq, PartialEq)] @@ -51,6 +51,25 @@ pub fn span() -> ExpectedSpan { } } +/// Returns a new, unset `ExpectedId`. +/// +/// The `ExpectedId` needs to be attached to a [`NewSpan`] or an +/// [`ExpectedSpan`] passed to [`MockCollector::new_span`] to +/// ensure that it gets set. When the a clone of the same +/// `ExpectedSpan` is attached to an [`ExpectedSpan`] and passed to +/// any other method on [`MockCollector`] that accepts it, it will +/// ensure that it is exactly the same span used across those +/// distinct expectations. +/// +/// For more details on how to use this struct, see the documentation +/// on [`ExpectedSpan::with_id`]. +/// +/// [`MockCollector`]: struct@crate::collector::MockCollector +/// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span +pub fn id() -> ExpectedId { + ExpectedId::new_unset() +} + impl Expect { pub(crate) fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { let name = name.as_ref(); diff --git a/tracing-mock/src/span.rs b/tracing-mock/src/span.rs index 42ed3a603e..b666cd7108 100644 --- a/tracing-mock/src/span.rs +++ b/tracing-mock/src/span.rs @@ -94,7 +94,13 @@ use crate::{ collector::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent, }; -use std::fmt; +use std::{ + error, fmt, + sync::{ + atomic::{AtomicU64, Ordering}, + Arc, + }, +}; /// A mock span. /// @@ -104,6 +110,7 @@ use std::fmt; /// [`collector`]: mod@crate::collector #[derive(Clone, Default, Eq, PartialEq)] pub struct ExpectedSpan { + pub(crate) id: Option, pub(crate) metadata: ExpectedMetadata, } @@ -137,6 +144,24 @@ where expect::span().named(name) } +/// A mock span ID. +/// +/// This ID makes it possible to link together calls to different +/// [`MockCollector`] span methods that take an [`ExpectedSpan`] in +/// addition to those that take a [`NewSpan`]. +/// +/// Use [`expect::id`] to construct a new, unset `ExpectedId`. +/// +/// For more details on how to use this struct, see the documentation +/// on [`ExpectedSpan::with_id`]. +/// +/// [`expect::id`]: fn@crate::expect::id +/// [`MockCollector`]: struct@crate::collector::MockCollector +#[derive(Clone, Default)] +pub struct ExpectedId { + inner: Arc, +} + impl ExpectedSpan { /// Sets a name to expect when matching a span. /// @@ -188,6 +213,100 @@ impl ExpectedSpan { name: Some(name.into()), ..self.metadata }, + ..self + } + } + + /// Sets the `ID` to expect when matching a span. + /// + /// The [`ExpectedId`] can be used to differentiate spans that are + /// otherwise identical. An [`ExpectedId`] needs to be attached to + /// an `ExpectedSpan` or [`NewSpan`] which is passed to + /// [`MockCollector::new_span`]. The same [`ExpectedId`] can then + /// be used to match the exact same span when passed to + /// [`MockCollector::enter`], [`MockCollector::exit`], and + /// [`MockCollector::drop_span`]. + /// + /// This is especially useful when `tracing-mock` is being used to + /// test the traces being generated within your own crate, in which + /// case you may need to distinguish between spans which have + /// identical metadata but different field values, which can + /// otherwise only be checked in [`MockCollector::new_span`]. + /// + /// # Examples + /// + /// Here we expect that the span that is created first is entered + /// second: + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// let id1 = expect::id(); + /// let span1 = expect::span().named("span").with_id(id1.clone()); + /// let id2 = expect::id(); + /// let span2 = expect::span().named("span").with_id(id2.clone()); + /// + /// let (collector, handle) = collector::mock() + /// .new_span(span1.clone()) + /// .new_span(span2.clone()) + /// .enter(span2) + /// .enter(span1) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// fn create_span() -> tracing::Span { + /// tracing::info_span!("span") + /// } + /// + /// let span1 = create_span(); + /// let span2 = create_span(); + /// + /// let _guard2 = span2.enter(); + /// let _guard1 = span1.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// If the order that the spans are entered changes, the test will + /// fail: + /// + /// ```should_panic + /// use tracing_mock::{collector, expect}; + /// let id1 = expect::id(); + /// let span1 = expect::span().named("span").with_id(id1.clone()); + /// let id2 = expect::id(); + /// let span2 = expect::span().named("span").with_id(id2.clone()); + /// + /// let (collector, handle) = collector::mock() + /// .new_span(span1.clone()) + /// .new_span(span2.clone()) + /// .enter(span2) + /// .enter(span1) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// fn create_span() -> tracing::Span { + /// tracing::info_span!("span") + /// } + /// + /// let span1 = create_span(); + /// let span2 = create_span(); + /// + /// let _guard1 = span1.enter(); + /// let _guard2 = span2.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span + /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter + /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit + /// [`MockCollector::drop_span`]: fn@crate::collector::MockCollector::drop_span + pub fn with_id(self, id: ExpectedId) -> Self { + Self { + id: Some(id), + ..self } } @@ -241,6 +360,7 @@ impl ExpectedSpan { level: Some(level), ..self.metadata }, + ..self } } @@ -297,6 +417,7 @@ impl ExpectedSpan { target: Some(target.into()), ..self.metadata }, + ..self } } @@ -598,6 +719,11 @@ impl ExpectedSpan { pub(crate) fn check(&self, actual: &SpanState, collector_name: &str) { let meta = actual.metadata(); let name = meta.name(); + + if let Some(expected_id) = &self.id { + expected_id.check(actual.id(), format_args!("span `{}`", name), collector_name); + } + self.metadata .check(meta, format_args!("span `{}`", name), collector_name); } @@ -760,3 +886,69 @@ impl fmt::Debug for NewSpan { s.finish() } } + +impl PartialEq for ExpectedId { + fn eq(&self, other: &Self) -> bool { + self.inner.load(Ordering::Relaxed) == other.inner.load(Ordering::Relaxed) + } +} + +impl Eq for ExpectedId {} + +impl ExpectedId { + const UNSET: u64 = 0; + + pub(crate) fn new_unset() -> Self { + Self { + inner: Arc::new(AtomicU64::from(Self::UNSET)), + } + } + + pub(crate) fn set(&self, span_id: u64) -> Result<(), SetActualSpanIdError> { + self.inner + .compare_exchange(Self::UNSET, span_id, Ordering::Relaxed, Ordering::Relaxed) + .map_err(|current| SetActualSpanIdError { + previous_span_id: current, + new_span_id: span_id, + })?; + Ok(()) + } + + pub(crate) fn check(&self, actual: u64, ctx: fmt::Arguments<'_>, collector_name: &str) { + let id = self.inner.load(Ordering::Relaxed); + + assert!( + id != Self::UNSET, + "\n[{}] expected {} to have expected ID set, but it hasn't been, \ + perhaps this `ExpectedId` wasn't used in a call to `MockCollector::new_span()`?", + collector_name, + ctx, + ); + + assert_eq!( + id, actual, + "\n[{}] expected {} to have ID `{}`, but it has `{}` instead", + collector_name, ctx, id, actual, + ); + } +} + +#[derive(Debug)] +pub(crate) struct SetActualSpanIdError { + previous_span_id: u64, + new_span_id: u64, +} + +impl fmt::Display for SetActualSpanIdError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "Could not set `ExpecedId` to {new}, \ + it had already been set to {previous}", + new = self.new_span_id, + previous = self.previous_span_id + ) + } +} + +impl error::Error for SetActualSpanIdError {} From 44861cad7a821f08b3c13aba14bb8ddf562b7053 Mon Sep 17 00:00:00 2001 From: Jonas Platte <158304798+svix-jplatte@users.noreply.github.com> Date: Fri, 5 Jul 2024 14:05:33 +0200 Subject: [PATCH 089/101] macros: allow field path segments to be keywords (#2925) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Motivation Currently, a keyword like `type` fails compilation as (a path segment of) a field name, for no clear reason. Trying to use `r#type` instead leads to the `r#` being part of the field name, which is unhelpful¹. ## Solution Don't require the field path to match a `macro_rules!` `expr`, use repeated `tt` instead. I can't tell why this was ever required: The internal stringify macro was introduced in https://github.com/tokio-rs/tracing/commit/55091c92edb537bfc126e32f1f24acd614ad9fe0#diff-315c02cd05738da173861537577d159833f70f79cfda8cd7cf1a0d7a28ace31b with an `expr` matcher without any explanation, and no tests are failing from making it match upstream's `stringify!` input format. Special thanks to whoever implemented the unstable `macro-backtrace` feature in rustc, otherwise this would have been nigh impossible to track down! ¹ this can likely be fixed too by some sort of "unraw" macro that turns `r#foo` into `foo`, but that's a separate change not made in this PR --- tracing-attributes/tests/fields.rs | 13 +++++++++++++ tracing/src/macros.rs | 4 ++-- tracing/tests/event.rs | 12 ++++++++++++ tracing/tests/span.rs | 18 ++++++++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/tracing-attributes/tests/fields.rs b/tracing-attributes/tests/fields.rs index 476d172482..29521dd5af 100644 --- a/tracing-attributes/tests/fields.rs +++ b/tracing-attributes/tests/fields.rs @@ -34,6 +34,9 @@ fn fn_string(s: String) { let _ = s; } +#[instrument(fields(keywords.impl.type.fn = _arg), skip(_arg))] +fn fn_keyword_ident_in_field(_arg: &str) {} + #[derive(Debug)] struct HasField { my_field: &'static str, @@ -146,6 +149,16 @@ fn string_field() { }); } +#[test] +fn keyword_ident_in_field_name() { + let span = expect::span().with_fields( + expect::field("keywords.impl.type.fn") + .with_value(&"test") + .only(), + ); + run_test(span, || fn_keyword_ident_in_field("test")); +} + fn run_test T, T>(span: NewSpan, fun: F) { let (collector, handle) = collector::mock() .new_span(span) diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 5de45cb75b..a4c6b15718 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -3069,8 +3069,8 @@ macro_rules! level_to_log { #[doc(hidden)] #[macro_export] macro_rules! __tracing_stringify { - ($s:expr) => { - stringify!($s) + ($($t:tt)*) => { + stringify!($($t)*) }; } diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs index 0af0602e96..48a6af774a 100644 --- a/tracing/tests/event.rs +++ b/tracing/tests/event.rs @@ -497,3 +497,15 @@ fn constant_field_name() { handle.assert_finished(); } + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn keyword_ident_in_field_name() { + let (collector, handle) = collector::mock() + .event(expect::event().with_fields(expect::field("crate").with_value(&"tracing"))) + .only() + .run_with_handle(); + + with_default(collector, || error!(crate = "tracing", "message")); + handle.assert_finished(); +} diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index e2c4c92396..9a17cd04d3 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -7,6 +7,7 @@ use std::thread; use tracing::{ collect::with_default, + error_span, field::{debug, display}, Level, Span, }; @@ -866,3 +867,20 @@ fn constant_field_name() { handle.assert_finished(); } + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[test] +fn keyword_ident_in_field_name_span_macro() { + #[derive(Debug)] + struct Foo; + + let (collector, handle) = collector::mock() + .new_span(expect::span().with_fields(expect::field("self").with_value(&debug(Foo)).only())) + .only() + .run_with_handle(); + + with_default(collector, || { + error_span!("span", self = ?Foo); + }); + handle.assert_finished(); +} From 28b3ce7187628dff02cdccd040122dd24bf78d09 Mon Sep 17 00:00:00 2001 From: David Barsky Date: Mon, 22 Jul 2024 11:49:58 -0400 Subject: [PATCH 090/101] chore: fix clippy lints (#3031) 1.80 had a bunch of indentation-related Clippy lints; fixing them. --- tracing-appender/src/lib.rs | 2 +- tracing-appender/src/rolling.rs | 6 +++--- tracing-error/src/lib.rs | 6 +++--- tracing-subscriber/src/subscribe/mod.rs | 1 + tracing/src/macros.rs | 14 +++++++------- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tracing-appender/src/lib.rs b/tracing-appender/src/lib.rs index d6e2af9e95..617751d8c6 100644 --- a/tracing-appender/src/lib.rs +++ b/tracing-appender/src/lib.rs @@ -24,7 +24,7 @@ //! - Using a [`RollingFileAppender`] 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`] and [`RollingFileAppender`] to allow writes to a log file -//! without blocking. +//! without blocking. //! //! ## File Appender //! diff --git a/tracing-appender/src/rolling.rs b/tracing-appender/src/rolling.rs index d82646933a..3c9a61a47f 100644 --- a/tracing-appender/src/rolling.rs +++ b/tracing-appender/src/rolling.rs @@ -10,11 +10,11 @@ //! The following helpers are available for creating a rolling file appender. //! //! - [`Rotation::minutely()`][minutely]: A new log file in the format of `some_directory/log_file_name_prefix.yyyy-MM-dd-HH-mm` -//! will be created minutely (once per minute) +//! will be created minutely (once per minute) //! - [`Rotation::hourly()`][hourly]: A new log file in the format of `some_directory/log_file_name_prefix.yyyy-MM-dd-HH` -//! will be created hourly +//! will be created hourly //! - [`Rotation::daily()`][daily]: A new log file in the format of `some_directory/log_file_name_prefix.yyyy-MM-dd` -//! will be created daily +//! will be created daily //! - [`Rotation::never()`][never()]: This will result in log file located at `some_directory/log_file_name` //! //! diff --git a/tracing-error/src/lib.rs b/tracing-error/src/lib.rs index c658f4e8c6..ced227afd3 100644 --- a/tracing-error/src/lib.rs +++ b/tracing-error/src/lib.rs @@ -26,10 +26,10 @@ //! //! - `traced-error` - Enables the [`TracedError`] type and related Traits //! - [`InstrumentResult`] and [`InstrumentError`] extension traits, which -//! provide an [`in_current_span()`] method for bundling errors with a -//! [`SpanTrace`]. +//! provide an [`in_current_span()`] method for bundling errors with a +//! [`SpanTrace`]. //! - [`ExtractSpanTrace`] extension trait, for extracting `SpanTrace`s from -//! behind `dyn Error` trait objects. +//! behind `dyn Error` trait objects. //! //! ## Usage //! diff --git a/tracing-subscriber/src/subscribe/mod.rs b/tracing-subscriber/src/subscribe/mod.rs index 7d6c9d8d7f..ad420cd744 100644 --- a/tracing-subscriber/src/subscribe/mod.rs +++ b/tracing-subscriber/src/subscribe/mod.rs @@ -573,6 +573,7 @@ //! the [`INFO`] [level] and above. //! - A third subscriber, `subscriber_c`, which should receive spans and events at //! the [`DEBUG`] [level] as well. +//! //! The subscribers and filters would be composed thusly: //! //! ``` diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index a4c6b15718..5c688e918c 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -1115,15 +1115,15 @@ macro_rules! span_enabled { /// in false positives or false negatives include: /// /// - If a collector is using a filter which may enable a span or event based -/// on field names, but `enabled!` is invoked without listing field names, -/// `enabled!` may return a false negative if a specific field name would -/// cause the collector to enable something that would otherwise be disabled. +/// on field names, but `enabled!` is invoked without listing field names, +/// `enabled!` may return a false negative if a specific field name would +/// cause the collector to enable something that would otherwise be disabled. /// - If a collector is using a filter which enables or disables specific events by -/// file path and line number, a particular event may be enabled/disabled -/// even if an `enabled!` invocation with the same level, target, and fields -/// indicated otherwise. +/// file path and line number, a particular event may be enabled/disabled +/// even if an `enabled!` invocation with the same level, target, and fields +/// indicated otherwise. /// - The collector can choose to enable _only_ spans or _only_ events, which `enabled` -/// will not reflect. +/// will not reflect. /// /// `enabled!()` requires a [level](crate::Level) argument, an optional `target:` /// argument, and an optional set of field names. If the fields are not provided, From 189831148aef1fdd104ef1d2e669cfb0fddca5c1 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Wed, 24 Jul 2024 10:53:17 -0700 Subject: [PATCH 091/101] fix: prefix macro calls with ::core to avoid clashing with local macros (#3024) fix: prefix macro calls with `__macro_support ` to avoid clashes with local macros Fixes: This ensures that the tracing lib correctly builds when a crate is named `core`. See https://github.com/tokio-rs/tracing/issues/2761 and https://github.com/tokio-rs/tracing/issues/2762 for more info. --- tracing-core/src/lib.rs | 14 +++++++++++--- tracing/src/lib.rs | 2 +- tracing/src/macros.rs | 18 +++++++++--------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/tracing-core/src/lib.rs b/tracing-core/src/lib.rs index eafdba9120..b78cfcecdf 100644 --- a/tracing-core/src/lib.rs +++ b/tracing-core/src/lib.rs @@ -165,6 +165,14 @@ #[cfg(feature = "alloc")] extern crate alloc; +#[doc(hidden)] +pub mod __macro_support { + // 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::{file, line, module_path, option::Option}; +} + /// Statically constructs an [`Identifier`] for the provided [`Callsite`]. /// /// This may be used in contexts, such as static initializers, where the @@ -264,9 +272,9 @@ macro_rules! metadata { $name, $target, $level, - ::core::option::Option::Some(file!()), - ::core::option::Option::Some(line!()), - ::core::option::Option::Some(module_path!()), + $crate::__macro_support::Option::Some($crate::__macro_support::file!()), + $crate::__macro_support::Option::Some($crate::__macro_support::line!()), + $crate::__macro_support::Option::Some($crate::__macro_support::module_path!()), $crate::field::FieldSet::new($fields, $crate::identify_callsite!($callsite)), $kind, ) diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index de08ec408f..3e88cf9f2a 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -1005,7 +1005,7 @@ pub mod __macro_support { // 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, format_args, iter::Iterator, option::Option}; + pub use core::{concat, file, format_args, iter::Iterator, line, option::Option}; /// Callsite implementation used by macro-generated code. /// diff --git a/tracing/src/macros.rs b/tracing/src/macros.rs index 5c688e918c..b0791d63f9 100644 --- a/tracing/src/macros.rs +++ b/tracing/src/macros.rs @@ -693,11 +693,11 @@ macro_rules! event { (target: $target:expr, parent: $parent:expr, $lvl:expr, { $($fields:tt)* } )=> ({ use $crate::__macro_support::Callsite as _; static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { - name: concat!( + name: $crate::__macro_support::concat!( "event ", - file!(), + $crate::__macro_support::file!(), ":", - line!() + $crate::__macro_support::line!() ), kind: $crate::metadata::Kind::EVENT, target: $target, @@ -854,11 +854,11 @@ macro_rules! event { (target: $target:expr, $lvl:expr, { $($fields:tt)* } )=> ({ use $crate::__macro_support::Callsite as _; static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { - name: concat!( + name: $crate::__macro_support::concat!( "event ", - file!(), + $crate::__macro_support::file!(), ":", - line!() + $crate::__macro_support::line!() ), kind: $crate::metadata::Kind::EVENT, target: $target, @@ -1184,11 +1184,11 @@ macro_rules! enabled { if $crate::level_enabled!($lvl) { use $crate::__macro_support::Callsite as _; static __CALLSITE: $crate::__macro_support::MacroCallsite = $crate::callsite2! { - name: concat!( + name: $crate::__macro_support::concat!( "enabled ", - file!(), + $crate::__macro_support::file!(), ":", - line!() + $crate::__macro_support::line!() ), kind: $kind.hint(), target: $target, From acf92ab944f6fa0c9dee0302a334d4fdf0cfe52a Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Mon, 29 Jul 2024 20:54:48 +0200 Subject: [PATCH 092/101] fix: correct SerializeField definition and doc formatting (#3040) Clippy in 1.80.0 alerted us to the fact that `SerializeField` was never constructed (and due to its non-`pub` member, it can't be constructed outside the `tracing-serde` crate where it's from). This change fixes the definition to hold a reference to a `Field`, which is what the other `Serialize*` types do. It also implements `AsSerde` for this type and uses it inside the `SerializeFieldSet` type. As a bonus, Clippy is now also happy that the type is constructed. The example collector in the `tracing-serde` crate was also renamed from `JsonSubscriber` to `JsonCollector`. Some additional doc formatting issues in `tracing-subscriber` were fixed so that list items that run to multiple lines are correctly indented. --- tracing-serde/src/lib.rs | 30 +++++++++++++++----- tracing-subscriber/src/fmt/fmt_subscriber.rs | 2 +- tracing-subscriber/src/fmt/format/json.rs | 6 ++-- tracing-subscriber/src/fmt/format/mod.rs | 2 +- 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/tracing-serde/src/lib.rs b/tracing-serde/src/lib.rs index 97b78d55c0..0692f7d645 100644 --- a/tracing-serde/src/lib.rs +++ b/tracing-serde/src/lib.rs @@ -71,11 +71,17 @@ //! use tracing_serde::AsSerde; //! use serde_json::json; //! -//! pub struct JsonSubscriber { +//! pub struct JsonCollector { //! next_id: AtomicUsize, // you need to assign span IDs, so you need a counter //! } //! -//! impl Collect for JsonSubscriber { +//! impl JsonCollector { +//! fn new() -> Self { +//! Self { next_id: 1.into() } +//! } +//! } +//! +//! impl Collect for JsonCollector { //! //! fn new_span(&self, attrs: &Attributes<'_>) -> Id { //! let id = self.next_id.fetch_add(1, Ordering::Relaxed); @@ -97,7 +103,7 @@ //! } //! //! // ... -//! # fn enabled(&self, _: &Metadata<'_>) -> bool { false } +//! # fn enabled(&self, _: &Metadata<'_>) -> bool { true } //! # fn enter(&self, _: &Id) {} //! # fn exit(&self, _: &Id) {} //! # fn record(&self, _: &Id, _: &Record<'_>) {} @@ -107,7 +113,7 @@ //! ``` //! //! After you implement your `Collector`, you can use your `tracing` -//! subscriber (`JsonSubscriber` in the above example) to record serialized +//! collector (`JsonCollector` in the above example) to record serialized //! trace data. //! //! ## Crate Feature Flags @@ -188,9 +194,9 @@ use tracing_core::{ pub mod fields; #[derive(Debug)] -pub struct SerializeField(Field); +pub struct SerializeField<'a>(&'a Field); -impl Serialize for SerializeField { +impl<'a> Serialize for SerializeField<'a> { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -209,7 +215,7 @@ impl<'a> Serialize for SerializeFieldSet<'a> { { let mut seq = serializer.serialize_seq(Some(self.0.len()))?; for element in self.0 { - seq.serialize_element(element.name())?; + seq.serialize_element(&SerializeField(&element))?; } seq.end() } @@ -532,6 +538,14 @@ impl<'a> AsSerde<'a> for Level { } } +impl<'a> AsSerde<'a> for Field { + type Serializable = SerializeField<'a>; + + fn as_serde(&'a self) -> Self::Serializable { + SerializeField(self) + } +} + impl<'a> AsSerde<'a> for FieldSet { type Serializable = SerializeFieldSet<'a>; @@ -552,6 +566,8 @@ impl<'a> self::sealed::Sealed for Record<'a> {} impl<'a> self::sealed::Sealed for Metadata<'a> {} +impl self::sealed::Sealed for Field {} + impl self::sealed::Sealed for FieldSet {} mod sealed { diff --git a/tracing-subscriber/src/fmt/fmt_subscriber.rs b/tracing-subscriber/src/fmt/fmt_subscriber.rs index e3949b3361..90027f02fe 100644 --- a/tracing-subscriber/src/fmt/fmt_subscriber.rs +++ b/tracing-subscriber/src/fmt/fmt_subscriber.rs @@ -595,7 +595,7 @@ where /// # Options /// /// - [`Subscriber::flatten_event`] can be used to enable flattening event fields into the root - /// object. + /// object. /// #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] diff --git a/tracing-subscriber/src/fmt/format/json.rs b/tracing-subscriber/src/fmt/format/json.rs index 1f045d93b5..41ed58a3f7 100644 --- a/tracing-subscriber/src/fmt/format/json.rs +++ b/tracing-subscriber/src/fmt/format/json.rs @@ -60,11 +60,11 @@ use tracing_log::NormalizeEvent; /// output JSON objects: /// /// - [`Json::flatten_event`] can be used to enable flattening event fields into -/// the root +/// the root /// - [`Json::with_current_span`] can be used to control logging of the current -/// span +/// span /// - [`Json::with_span_list`] can be used to control logging of the span list -/// object. +/// object. /// /// By default, event fields are not flattened, and both current span and span /// list are logged. diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index e19d689486..f867d228d3 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -678,7 +678,7 @@ impl Format { /// # Options /// /// - [`Format::flatten_event`] can be used to enable flattening event fields into the root - /// object. + /// object. /// #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] From 527b4f66a604e7a6baa6aa7536428e3a303ba3c8 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Mon, 5 Aug 2024 19:03:29 +0200 Subject: [PATCH 093/101] mock: correct contextual/explicit parent assertions (#3004) ## Motivation When recording the parent of an event or span, the `MockCollector` treats an explicit parent of `None` (i.e. an event or span that is an explicit root) in the same way as if there is no explicit root. This leads to it picking up the contextual parent or treating the event or span as a contextual root. ## Solution This change refactors the recording of the parent to use `is_contextual` to distinguish whether or not an explicit parent has been specified. The actual parent is also written into an `Ancestry` enum so that the expected and actual values can be compared in a more explicit way. Additionally, the `Ancestry` struct has been moved into its own module and the check behavior has been fixed. The error message has also been unified across all cases. Another problem with the previous API is that the two methods `with_contextual_parent` and `with_explicit_parent` are actually mutually exclusive, a span or event cannot be both of them. It is also a (small) mental leap for the user to go from `with_*_parent(None)` to understanding that this means that a span or event is a root (either contextual or explicit). As such, the API has been reworked into a single method `with_ancestry`, which takes an enum with the following four variants: * `HasExplicitParent(String)` (parent span name) * `IsExplicitRoot` * `HasContextualParent(String)` (parent span name) * `IsContextualRoot` To make the interface as useable as possible, helper functions have been defined in the `expect` module which can be used to create the enum variants. Specifically, these take `Into` parameter for the span name. Given the number of different cases involved in checking ancestry, separate integration tests have been added to `tracing-mock` specifically for testing all the positive and negative cases when asserting on the ancestry of events and spans. There were two tests in `tracing-attributes` which specified both an explicit and a contextual parent. This behavior was never intended to work as all events and spans are either contextual or not. The tests have been corrected to only expect one of the two. Fixes: #2440 --- tracing-attributes/tests/parents.rs | 25 +- tracing-futures/tests/std_future.rs | 4 +- tracing-mock/src/ancestry.rs | 148 ++++++++++ tracing-mock/src/collector.rs | 53 ++-- tracing-mock/src/event.rs | 128 +++------ tracing-mock/src/expect.rs | 23 ++ tracing-mock/src/lib.rs | 86 +----- tracing-mock/src/span.rs | 179 +++--------- tracing-mock/src/subscriber.rs | 25 +- tracing-mock/tests/event_ancestry.rs | 346 +++++++++++++++++++++++ tracing-mock/tests/span_ancestry.rs | 401 +++++++++++++++++++++++++++ tracing/tests/event.rs | 12 +- tracing/tests/instrument.rs | 4 +- tracing/tests/span.rs | 54 +++- 14 files changed, 1110 insertions(+), 378 deletions(-) create mode 100644 tracing-mock/src/ancestry.rs create mode 100644 tracing-mock/tests/event_ancestry.rs create mode 100644 tracing-mock/tests/span_ancestry.rs diff --git a/tracing-attributes/tests/parents.rs b/tracing-attributes/tests/parents.rs index c33e44a74f..2111121b7d 100644 --- a/tracing-attributes/tests/parents.rs +++ b/tracing-attributes/tests/parents.rs @@ -21,23 +21,16 @@ fn default_parent_test() { .new_span( contextual_parent .clone() - .with_contextual_parent(None) - .with_explicit_parent(None), - ) - .new_span( - child - .clone() - .with_contextual_parent(Some("contextual_parent")) - .with_explicit_parent(None), + .with_ancestry(expect::is_contextual_root()), ) + .new_span(child.clone().with_ancestry(expect::is_contextual_root())) .enter(child.clone()) .exit(child.clone()) .enter(contextual_parent.clone()) .new_span( child .clone() - .with_contextual_parent(Some("contextual_parent")) - .with_explicit_parent(None), + .with_ancestry(expect::has_contextual_parent("contextual_parent")), ) .enter(child.clone()) .exit(child) @@ -68,20 +61,14 @@ fn explicit_parent_test() { .new_span( contextual_parent .clone() - .with_contextual_parent(None) - .with_explicit_parent(None), - ) - .new_span( - explicit_parent - .with_contextual_parent(None) - .with_explicit_parent(None), + .with_ancestry(expect::is_contextual_root()), ) + .new_span(explicit_parent.with_ancestry(expect::is_contextual_root())) .enter(contextual_parent.clone()) .new_span( child .clone() - .with_contextual_parent(Some("contextual_parent")) - .with_explicit_parent(Some("explicit_parent")), + .with_ancestry(expect::has_explicit_parent("explicit_parent")), ) .enter(child.clone()) .exit(child) diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs index d8ec669175..fb2a31c130 100644 --- a/tracing-futures/tests/std_future.rs +++ b/tracing-futures/tests/std_future.rs @@ -71,7 +71,7 @@ fn span_on_drop() { .enter(expect::span().named("foo")) .event( expect::event() - .with_contextual_parent(Some("foo")) + .with_ancestry(expect::has_contextual_parent("foo")) .at_level(Level::INFO), ) .exit(expect::span().named("foo")) @@ -81,7 +81,7 @@ fn span_on_drop() { .enter(expect::span().named("bar")) .event( expect::event() - .with_contextual_parent(Some("bar")) + .with_ancestry(expect::has_contextual_parent("bar")) .at_level(Level::INFO), ) .exit(expect::span().named("bar")) diff --git a/tracing-mock/src/ancestry.rs b/tracing-mock/src/ancestry.rs new file mode 100644 index 0000000000..ee661d45e8 --- /dev/null +++ b/tracing-mock/src/ancestry.rs @@ -0,0 +1,148 @@ +//! Define the ancestry of an event or span. +//! +//! See the documentation on the [`Ancestry`] enum for further details. + +use tracing_core::{ + span::{self, Attributes}, + Event, +}; + +/// The ancestry of an event or span. +/// +/// An event or span can have an explicitly assigned parent, or be an explicit root. Otherwise, +/// an event or span may have a contextually assigned parent or in the final case will be a +/// contextual root. +#[derive(Debug, Eq, PartialEq)] +pub enum Ancestry { + /// The event or span has an explicitly assigned parent (created with `parent: span_id`) with + /// the specified name. + HasExplicitParent(String), + /// The event or span is an explicitly defined root. It was created with `parent: None` and + /// has no parent. + IsExplicitRoot, + /// The event or span has a contextually assigned parent with the specified name. It has no + /// explicitly assigned parent, nor has it been explicitly defined as a root (it was created + /// without the `parent:` directive). There was a span in context when this event or span was + /// created. + HasContextualParent(String), + /// The event or span is a contextual root. It has no explicitly assigned parent, nor has it + /// been explicitly defined as a root (it was created without the `parent:` directive). + /// Additionally, no span was in context when this event or span was created. + IsContextualRoot, +} + +impl Ancestry { + #[track_caller] + pub(crate) fn check( + &self, + actual_ancestry: &Ancestry, + ctx: impl std::fmt::Display, + collector_name: &str, + ) { + let expected_description = |ancestry: &Ancestry| match ancestry { + Self::IsExplicitRoot => "be an explicit root".to_string(), + Self::HasExplicitParent(name) => format!("have an explicit parent with name='{name}'"), + Self::IsContextualRoot => "be a contextual root".to_string(), + Self::HasContextualParent(name) => { + format!("have a contextual parent with name='{name}'") + } + }; + + let actual_description = |ancestry: &Ancestry| match ancestry { + Self::IsExplicitRoot => "was actually an explicit root".to_string(), + Self::HasExplicitParent(name) => { + format!("actually has an explicit parent with name='{name}'") + } + Self::IsContextualRoot => "was actually a contextual root".to_string(), + Self::HasContextualParent(name) => { + format!("actually has a contextual parent with name='{name}'") + } + }; + + assert_eq!( + self, + actual_ancestry, + "[{collector_name}] expected {ctx} to {expected_description}, but {actual_description}", + expected_description = expected_description(self), + actual_description = actual_description(actual_ancestry) + ); + } +} + +pub(crate) trait HasAncestry { + fn is_contextual(&self) -> bool; + + fn is_root(&self) -> bool; + + fn parent(&self) -> Option<&span::Id>; +} + +impl HasAncestry for &Event<'_> { + fn is_contextual(&self) -> bool { + (self as &Event<'_>).is_contextual() + } + + fn is_root(&self) -> bool { + (self as &Event<'_>).is_root() + } + + fn parent(&self) -> Option<&span::Id> { + (self as &Event<'_>).parent() + } +} + +impl HasAncestry for &Attributes<'_> { + fn is_contextual(&self) -> bool { + (self as &Attributes<'_>).is_contextual() + } + + fn is_root(&self) -> bool { + (self as &Attributes<'_>).is_root() + } + + fn parent(&self) -> Option<&span::Id> { + (self as &Attributes<'_>).parent() + } +} + +/// Determines the ancestry of an actual span or event. +/// +/// The rules for determining the ancestry are as follows: +/// +/// +------------+--------------+-----------------+---------------------+ +/// | Contextual | Current Span | Explicit Parent | Ancestry | +/// +------------+--------------+-----------------+---------------------+ +/// | Yes | Yes | - | HasContextualParent | +/// | Yes | No | - | IsContextualRoot | +/// | No | - | Yes | HasExplicitParent | +/// | No | - | No | IsExplicitRoot | +/// +------------+--------------+-----------------+---------------------+ +pub(crate) fn get_ancestry( + item: impl HasAncestry, + lookup_current: impl FnOnce() -> Option, + span_name: impl FnOnce(&span::Id) -> Option<&str>, +) -> Ancestry { + if item.is_contextual() { + if let Some(parent_id) = lookup_current() { + let contextual_parent_name = span_name(&parent_id).expect( + "tracing-mock: contextual parent cannot \ + be looked up by ID. Was it recorded correctly?", + ); + Ancestry::HasContextualParent(contextual_parent_name.to_string()) + } else { + Ancestry::IsContextualRoot + } + } else if item.is_root() { + Ancestry::IsExplicitRoot + } else { + let parent_id = item.parent().expect( + "tracing-mock: is_contextual=false is_root=false \ + but no explicit parent found. This is a bug!", + ); + let explicit_parent_name = span_name(parent_id).expect( + "tracing-mock: explicit parent cannot be looked \ + up by ID. Is the provided Span ID valid: {parent_id}", + ); + Ancestry::HasExplicitParent(explicit_parent_name.to_string()) + } +} diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs index 97a0148825..89f74d05de 100644 --- a/tracing-mock/src/collector.rs +++ b/tracing-mock/src/collector.rs @@ -138,6 +138,7 @@ //! [`Collect`]: trait@tracing::Collect //! [`MockCollector`]: struct@crate::collector::MockCollector use crate::{ + ancestry::get_ancestry, event::ExpectedEvent, expect::Expect, field::ExpectedFields, @@ -1039,16 +1040,20 @@ where ) } } - let get_parent_name = || { - let stack = self.current.lock().unwrap(); - let spans = self.spans.lock().unwrap(); - event - .parent() - .and_then(|id| spans.get(id)) - .or_else(|| stack.last().and_then(|id| spans.get(id))) - .map(|s| s.name.to_string()) + let event_get_ancestry = || { + get_ancestry( + event, + || self.lookup_current(), + |span_id| { + self.spans + .lock() + .unwrap() + .get(span_id) + .map(|span| span.name) + }, + ) }; - expected.check(event, get_parent_name, &self.name); + expected.check(event, event_get_ancestry, &self.name); } Some(ex) => ex.bad(&self.name, format_args!("observed event {:#?}", event)), } @@ -1108,14 +1113,18 @@ where if let Some(expected_id) = &expected.span.id { expected_id.set(id.into_u64()).unwrap(); } - let get_parent_name = || { - let stack = self.current.lock().unwrap(); - span.parent() - .and_then(|id| spans.get(id)) - .or_else(|| stack.last().and_then(|id| spans.get(id))) - .map(|s| s.name.to_string()) - }; - expected.check(span, get_parent_name, &self.name); + + expected.check( + span, + || { + get_ancestry( + span, + || self.lookup_current(), + |span_id| spans.get(span_id).map(|span| span.name), + ) + }, + &self.name, + ); } } spans.insert( @@ -1265,6 +1274,16 @@ where } } +impl Running +where + F: Fn(&Metadata<'_>) -> bool, +{ + fn lookup_current(&self) -> Option { + let stack = self.current.lock().unwrap(); + stack.last().cloned() + } +} + impl MockHandle { #[cfg(feature = "tracing-subscriber")] pub(crate) fn new(expected: Arc>>, name: String) -> Self { diff --git a/tracing-mock/src/event.rs b/tracing-mock/src/event.rs index 840867d019..630f005a84 100644 --- a/tracing-mock/src/event.rs +++ b/tracing-mock/src/event.rs @@ -29,7 +29,7 @@ //! [`collector`]: mod@crate::collector //! [`expect::event`]: fn@crate::expect::event #![allow(missing_docs)] -use super::{expect, field, metadata::ExpectedMetadata, span, Parent}; +use crate::{ancestry::Ancestry, expect, field, metadata::ExpectedMetadata, span}; use std::fmt; @@ -42,7 +42,7 @@ use std::fmt; #[derive(Default, Eq, PartialEq)] pub struct ExpectedEvent { pub(super) fields: Option, - pub(super) parent: Option, + pub(super) ancestry: Option, pub(super) in_spans: Option>, pub(super) metadata: ExpectedMetadata, } @@ -253,32 +253,30 @@ impl ExpectedEvent { } } - /// Configures this `ExpectedEvent` to expect an explicit parent span - /// when matching events or to be an explicit root. + /// Configures this `ExpectedEvent` to expect the specified [`Ancestry`]. + /// An event's ancestry indicates whether is has a parent or is a root, and + /// whether the parent is explicitly or contextually assigned. /// - /// An _explicit_ parent span is one passed to the `span!` macro in the - /// `parent:` field. + /// An _explicit_ parent span is one passed to the `event!` macro in the + /// `parent:` field. If no `parent:` field is specified, then the event + /// will have a contextually determined parent or be a contextual root if + /// there is no parent. /// - /// If `Some("parent_name")` is passed to `with_explicit_parent` then - /// the provided string is the name of the parent span to expect. - /// - /// To expect that an event is recorded with `parent: None`, `None` - /// can be passed to `with_explicit_parent` instead. - /// - /// If an event is recorded without an explicit parent, or if the - /// explicit parent has a different name, this expectation will - /// fail. + /// If the parent is different from the provided one, this expectation + /// will fail. /// /// # Examples /// - /// The explicit parent is matched by name: + /// If `expect::has_explicit_parent("parent_name")` is passed + /// `with_ancestry` then the provided string is the name of the explicit + /// parent span to expect. /// /// ``` /// use tracing::collect::with_default; /// use tracing_mock::{collector, expect}; /// /// let event = expect::event() - /// .with_explicit_parent(Some("parent_span")); + /// .with_ancestry(expect::has_explicit_parent("parent_span")); /// /// let (collector, handle) = collector::mock() /// .event(event) @@ -300,29 +298,7 @@ impl ExpectedEvent { /// use tracing_mock::{collector, expect}; /// /// let event = expect::event() - /// .with_explicit_parent(None); - /// - /// let (collector, handle) = collector::mock() - /// .event(event) - /// .run_with_handle(); - /// - /// with_default(collector, || { - /// tracing::info!(parent: None, field = &"value"); - /// }); - /// - /// handle.assert_finished(); - /// ``` - /// - /// In the example below, the expectation fails because the - /// event is contextually (rather than explicitly) within the span - /// `parent_span`: - /// - /// ```should_panic - /// use tracing::collect::with_default; - /// use tracing_mock::{collector, expect}; - /// - /// let event = expect::event() - /// .with_explicit_parent(Some("parent_span")); + /// .with_ancestry(expect::is_explicit_root()); /// /// let (collector, handle) = collector::mock() /// .enter(expect::span()) @@ -330,48 +306,23 @@ impl ExpectedEvent { /// .run_with_handle(); /// /// with_default(collector, || { - /// let parent = tracing::info_span!("parent_span"); - /// let _guard = parent.enter(); - /// tracing::info!(field = &"value"); + /// let _guard = tracing::info_span!("contextual parent").entered(); + /// tracing::info!(parent: None, field = &"value"); /// }); /// /// handle.assert_finished(); /// ``` - pub fn with_explicit_parent(self, parent: Option<&str>) -> ExpectedEvent { - let parent = match parent { - Some(name) => Parent::Explicit(name.into()), - None => Parent::ExplicitRoot, - }; - Self { - parent: Some(parent), - ..self - } - } - - /// Configures this `ExpectedEvent` to match an event with a - /// contextually-determined parent span. - /// - /// The provided string is the name of the parent span to expect. - /// To expect that the event is a contextually-determined root, pass - /// `None` instead. - /// - /// To expect an event with an explicit parent span, use - /// [`ExpectedEvent::with_explicit_parent`]. - /// - /// If an event is recorded which is not inside a span, has an explicitly - /// overridden parent span, or with a differently-named span as its - /// parent, this expectation will fail. - /// - /// # Examples /// - /// The contextual parent is matched by name: + /// When `expect::has_contextual_parent("parent_name")` is passed to + /// `with_ancestry` then the provided string is the name of the contextual + /// parent span to expect. /// /// ``` /// use tracing::collect::with_default; /// use tracing_mock::{collector, expect}; /// /// let event = expect::event() - /// .with_contextual_parent(Some("parent_span")); + /// .with_ancestry(expect::has_contextual_parent("parent_span")); /// /// let (collector, handle) = collector::mock() /// .enter(expect::span()) @@ -387,14 +338,15 @@ impl ExpectedEvent { /// handle.assert_finished(); /// ``` /// - /// Matching an event recorded outside of a span: + /// Matching an event recorded outside of a span, a contextual + /// root: /// /// ``` /// use tracing::collect::with_default; /// use tracing_mock::{collector, expect}; /// /// let event = expect::event() - /// .with_contextual_parent(None); + /// .with_ancestry(expect::is_contextual_root()); /// /// let (collector, handle) = collector::mock() /// .event(event) @@ -407,15 +359,16 @@ impl ExpectedEvent { /// handle.assert_finished(); /// ``` /// - /// In the example below, the expectation fails because the - /// event is recorded with an explicit parent: + /// In the example below, the expectation fails because the event is + /// recorded with an explicit parent, however a contextual parent is + /// expected. /// /// ```should_panic /// use tracing::collect::with_default; /// use tracing_mock::{collector, expect}; /// /// let event = expect::event() - /// .with_contextual_parent(Some("parent_span")); + /// .with_ancestry(expect::has_contextual_parent("parent_span")); /// /// let (collector, handle) = collector::mock() /// .enter(expect::span()) @@ -429,13 +382,9 @@ impl ExpectedEvent { /// /// handle.assert_finished(); /// ``` - pub fn with_contextual_parent(self, parent: Option<&str>) -> ExpectedEvent { - let parent = match parent { - Some(name) => Parent::Contextual(name.into()), - None => Parent::ContextualRoot, - }; + pub fn with_ancestry(self, ancenstry: Ancestry) -> ExpectedEvent { Self { - parent: Some(parent), + ancestry: Some(ancenstry), ..self } } @@ -557,7 +506,7 @@ impl ExpectedEvent { pub(crate) fn check( &mut self, event: &tracing::Event<'_>, - get_parent_name: impl FnOnce() -> Option, + get_ancestry: impl FnOnce() -> Ancestry, collector_name: &str, ) { let meta = event.metadata(); @@ -577,14 +526,9 @@ impl ExpectedEvent { checker.finish(); } - if let Some(ref expected_parent) = self.parent { - let actual_parent = get_parent_name(); - expected_parent.check_parent_name( - actual_parent.as_deref(), - event.parent().cloned(), - event.metadata().name(), - collector_name, - ) + if let Some(ref expected_ancestry) = self.ancestry { + let actual_ancestry = get_ancestry(); + expected_ancestry.check(&actual_ancestry, event.metadata().name(), collector_name); } } } @@ -615,7 +559,7 @@ impl fmt::Debug for ExpectedEvent { s.field("fields", fields); } - if let Some(ref parent) = self.parent { + if let Some(ref parent) = self.ancestry { s.field("parent", &format_args!("{:?}", parent)); } diff --git a/tracing-mock/src/expect.rs b/tracing-mock/src/expect.rs index beb66d36f9..95ad3176ca 100644 --- a/tracing-mock/src/expect.rs +++ b/tracing-mock/src/expect.rs @@ -1,6 +1,7 @@ use std::fmt; use crate::{ + ancestry::Ancestry, event::ExpectedEvent, field::{ExpectedField, ExpectedFields, ExpectedValue}, span::{ExpectedId, ExpectedSpan, NewSpan}, @@ -70,6 +71,28 @@ pub fn id() -> ExpectedId { ExpectedId::new_unset() } +/// Convenience function that returns [`Ancestry::IsContextualRoot`]. +pub fn is_contextual_root() -> Ancestry { + Ancestry::IsContextualRoot +} + +/// Convenience function that returns [`Ancestry::HasContextualParent`] with +/// provided name. +pub fn has_contextual_parent>(name: S) -> Ancestry { + Ancestry::HasContextualParent(name.into()) +} + +/// Convenience function that returns [`Ancestry::IsExplicitRoot`]. +pub fn is_explicit_root() -> Ancestry { + Ancestry::IsExplicitRoot +} + +/// Convenience function that returns [`Ancestry::HasExplicitParent`] with +/// provided name. +pub fn has_explicit_parent>(name: S) -> Ancestry { + Ancestry::HasExplicitParent(name.into()) +} + impl Expect { pub(crate) fn bad(&self, name: impl AsRef, what: fmt::Arguments<'_>) { let name = name.as_ref(); diff --git a/tracing-mock/src/lib.rs b/tracing-mock/src/lib.rs index 9fdeab5866..1f6d2e4907 100644 --- a/tracing-mock/src/lib.rs +++ b/tracing-mock/src/lib.rs @@ -1,4 +1,5 @@ #![doc = include_str!("../README.md")] +pub mod ancestry; pub mod collector; pub mod event; pub mod expect; @@ -8,88 +9,3 @@ pub mod span; #[cfg(feature = "tracing-subscriber")] pub mod subscriber; - -#[derive(Debug, Eq, PartialEq)] -pub enum Parent { - ContextualRoot, - Contextual(String), - ExplicitRoot, - Explicit(String), -} - -impl Parent { - pub fn check_parent_name( - &self, - parent_name: Option<&str>, - provided_parent: Option, - ctx: impl std::fmt::Display, - collector_name: &str, - ) { - match self { - Parent::ExplicitRoot => { - assert!( - provided_parent.is_none(), - "[{}] expected {} to be an explicit root, but its parent was actually {:?} (name: {:?})", - collector_name, - ctx, - provided_parent, - parent_name, - ); - } - Parent::Explicit(expected_parent) => { - assert!( - provided_parent.is_some(), - "[{}] expected {} to have explicit parent {}, but it has no explicit parent", - collector_name, - ctx, - expected_parent, - ); - assert_eq!( - Some(expected_parent.as_ref()), - parent_name, - "[{}] expected {} to have explicit parent {}, but its parent was actually {:?} (name: {:?})", - collector_name, - ctx, - expected_parent, - provided_parent, - parent_name, - ); - } - Parent::ContextualRoot => { - assert!( - provided_parent.is_none(), - "[{}] expected {} to be a contextual root, but its parent was actually {:?} (name: {:?})", - collector_name, - ctx, - provided_parent, - parent_name, - ); - assert!( - parent_name.is_none(), - "[{}] expected {} to be contextual a root, but we were inside span {:?}", - collector_name, - ctx, - parent_name, - ); - } - Parent::Contextual(expected_parent) => { - assert!(provided_parent.is_none(), - "[{}] expected {} to have a contextual parent\nbut it has the explicit parent {:?} (name: {:?})", - collector_name, - ctx, - provided_parent, - parent_name, - ); - assert_eq!( - Some(expected_parent.as_ref()), - parent_name, - "[{}] expected {} to have contextual parent {:?}, but got {:?}", - collector_name, - ctx, - expected_parent, - parent_name, - ); - } - } - } -} diff --git a/tracing-mock/src/span.rs b/tracing-mock/src/span.rs index b666cd7108..a568484f76 100644 --- a/tracing-mock/src/span.rs +++ b/tracing-mock/src/span.rs @@ -41,7 +41,7 @@ //! let new_span = span //! .clone() //! .with_fields(expect::field("field.name").with_value(&"field_value")) -//! .with_explicit_parent(Some("parent_span")); +//! .with_ancestry(expect::has_explicit_parent("parent_span")); //! //! let (collector, handle) = collector::mock() //! .new_span(expect::span().named("parent_span")) @@ -92,7 +92,8 @@ //! [`expect::span`]: fn@crate::expect::span #![allow(missing_docs)] use crate::{ - collector::SpanState, expect, field::ExpectedFields, metadata::ExpectedMetadata, Parent, + ancestry::Ancestry, collector::SpanState, expect, field::ExpectedFields, + metadata::ExpectedMetadata, }; use std::{ error, fmt, @@ -134,7 +135,7 @@ pub struct ExpectedSpan { pub struct NewSpan { pub(crate) span: ExpectedSpan, pub(crate) fields: ExpectedFields, - pub(crate) parent: Option, + pub(crate) ancestry: Option, } pub fn named(name: I) -> ExpectedSpan @@ -421,8 +422,9 @@ impl ExpectedSpan { } } - /// Configures this `ExpectedSpan` to expect an explicit parent - /// span or to be an explicit root. + /// Configures this `ExpectedSpan` to expect the specified [`Ancestry`]. A + /// span's ancestry indicates whether it has a parent or is a root span + /// and whether the parent is explitly or contextually assigned. /// /// **Note**: This method returns a [`NewSpan`] and as such, this /// expectation can only be validated when expecting a new span via @@ -431,27 +433,24 @@ impl ExpectedSpan { /// method on [`MockCollector`] that takes an `ExpectedSpan`. /// /// An _explicit_ parent span is one passed to the `span!` macro in the - /// `parent:` field. + /// `parent:` field. If no `parent:` field is specified, then the span + /// will have a contextually determined parent or be a contextual root if + /// there is no parent. /// - /// If `Some("parent_name")` is passed to `with_explicit_parent` then, - /// the provided string is the name of the parent span to expect. - /// - /// To expect that a span is recorded with no parent, `None` - /// can be passed to `with_explicit_parent` instead. - /// - /// If a span is recorded without an explicit parent, or if the - /// explicit parent has a different name, this expectation will - /// fail. + /// If the ancestry is different from the provided one, this expectation + /// will fail. /// /// # Examples /// - /// The explicit parent is matched by name: + /// If `expect::has_explicit_parent("parent_name")` is passed + /// `with_ancestry` then the provided string is the name of the explicit + /// parent span to expect. /// /// ``` /// use tracing_mock::{collector, expect}; /// /// let span = expect::span() - /// .with_explicit_parent(Some("parent_span")); + /// .with_ancestry(expect::has_explicit_parent("parent_span")); /// /// let (collector, handle) = collector::mock() /// .new_span(expect::span().named("parent_span")) @@ -472,7 +471,7 @@ impl ExpectedSpan { /// use tracing_mock::{collector, expect}; /// /// let span = expect::span() - /// .with_explicit_parent(None); + /// .with_ancestry(expect::is_explicit_root()); /// /// let (collector, handle) = collector::mock() /// .new_span(span) @@ -485,79 +484,16 @@ impl ExpectedSpan { /// handle.assert_finished(); /// ``` /// - /// In the example below, the expectation fails because the - /// span is *contextually*—as opposed to explicitly—within the span - /// `parent_span`: - /// - /// ```should_panic - /// use tracing_mock::{collector, expect}; - /// - /// let parent_span = expect::span().named("parent_span"); - /// let span = expect::span() - /// .with_explicit_parent(Some("parent_span")); - /// - /// let (collector, handle) = collector::mock() - /// .new_span(parent_span.clone()) - /// .enter(parent_span) - /// .new_span(span) - /// .run_with_handle(); - /// - /// tracing::collect::with_default(collector, || { - /// let parent = tracing::info_span!("parent_span"); - /// let _guard = parent.enter(); - /// tracing::info_span!("span"); - /// }); - /// - /// handle.assert_finished(); - /// ``` - /// - /// [`MockCollector`]: struct@crate::collector::MockCollector - /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter - /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit - /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span - pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan { - let parent = match parent { - Some(name) => Parent::Explicit(name.into()), - None => Parent::ExplicitRoot, - }; - NewSpan { - parent: Some(parent), - span: self, - ..Default::default() - } - } - - /// Configures this `ExpectedSpan` to expect a - /// contextually-determined parent span, or be a contextual - /// root. - /// - /// **Note**: This method returns a [`NewSpan`] and as such, this - /// expectation can only be validated when expecting a new span via - /// [`MockCollector::new_span`]. It cannot be validated on - /// [`MockCollector::enter`], [`MockCollector::exit`], or any other - /// method on [`MockCollector`] that takes an `ExpectedSpan`. - /// - /// The provided string is the name of the parent span to expect. - /// To expect that the event is a contextually-determined root, pass - /// `None` instead. - /// - /// To expect a span with an explicit parent span, use - /// [`ExpectedSpan::with_explicit_parent`]. - /// - /// If a span is recorded which is not inside a span, has an explicitly - /// overridden parent span, or has a differently-named span as its - /// parent, this expectation will fail. - /// - /// # Examples - /// - /// The contextual parent is matched by name: + /// When `expect::has_contextual_parent("parent_name")` is passed to + /// `with_ancestry` then the provided string is the name of the contextual + /// parent span to expect. /// /// ``` /// use tracing_mock::{collector, expect}; /// /// let parent_span = expect::span().named("parent_span"); /// let span = expect::span() - /// .with_contextual_parent(Some("parent_span")); + /// .with_ancestry(expect::has_contextual_parent("parent_span")); /// /// let (collector, handle) = collector::mock() /// .new_span(parent_span.clone()) @@ -581,7 +517,7 @@ impl ExpectedSpan { /// use tracing_mock::{collector, expect}; /// /// let span = expect::span() - /// .with_contextual_parent(None); + /// .with_ancestry(expect::is_contextual_root()); /// /// let (collector, handle) = collector::mock() /// .new_span(span) @@ -595,22 +531,26 @@ impl ExpectedSpan { /// ``` /// /// In the example below, the expectation fails because the - /// span is recorded with an explicit parent: + /// span is *contextually*—as opposed to explicitly—within the span + /// `parent_span`: /// /// ```should_panic /// use tracing_mock::{collector, expect}; /// + /// let parent_span = expect::span().named("parent_span"); /// let span = expect::span() - /// .with_contextual_parent(Some("parent_span")); + /// .with_ancestry(expect::has_explicit_parent("parent_span")); /// /// let (collector, handle) = collector::mock() - /// .new_span(expect::span().named("parent_span")) + /// .new_span(parent_span.clone()) + /// .enter(parent_span) /// .new_span(span) /// .run_with_handle(); /// /// tracing::collect::with_default(collector, || { /// let parent = tracing::info_span!("parent_span"); - /// tracing::info_span!(parent: parent.id(), "span"); + /// let _guard = parent.enter(); + /// tracing::info_span!("span"); /// }); /// /// handle.assert_finished(); @@ -620,13 +560,9 @@ impl ExpectedSpan { /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span - pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan { - let parent = match parent { - Some(name) => Parent::Contextual(name.into()), - None => Parent::ContextualRoot, - }; + pub fn with_ancestry(self, ancestry: Ancestry) -> NewSpan { NewSpan { - parent: Some(parent), + ancestry: Some(ancestry), span: self, ..Default::default() } @@ -769,39 +705,15 @@ impl From for NewSpan { } impl NewSpan { - /// Configures this `ExpectedSpan` to expect an explicit parent - /// span or to be an explicit root. - /// - /// For more information and examples, see the documentation on - /// [`ExpectedSpan::with_explicit_parent`]. - /// - /// [`ExpectedSpan::with_explicit_parent`]: fn@crate::span::ExpectedSpan::with_explicit_parent - pub fn with_explicit_parent(self, parent: Option<&str>) -> NewSpan { - let parent = match parent { - Some(name) => Parent::Explicit(name.into()), - None => Parent::ExplicitRoot, - }; - NewSpan { - parent: Some(parent), - ..self - } - } - - /// Configures this `NewSpan` to expect a - /// contextually-determined parent span, or to be a contextual - /// root. + /// Configures this `NewSpan` to expect the specified [`Ancestry`]. A + /// span's ancestry indicates whether it has a parent or is a root span + /// and whether the parent is explitly or contextually assigned. /// /// For more information and examples, see the documentation on - /// [`ExpectedSpan::with_contextual_parent`]. - /// - /// [`ExpectedSpan::with_contextual_parent`]: fn@crate::span::ExpectedSpan::with_contextual_parent - pub fn with_contextual_parent(self, parent: Option<&str>) -> NewSpan { - let parent = match parent { - Some(name) => Parent::Contextual(name.into()), - None => Parent::ContextualRoot, - }; + /// [`ExpectedSpan::with_ancestry`]. + pub fn with_ancestry(self, ancestry: Ancestry) -> NewSpan { NewSpan { - parent: Some(parent), + ancestry: Some(ancestry), ..self } } @@ -825,7 +737,7 @@ impl NewSpan { pub(crate) fn check( &mut self, span: &tracing_core::span::Attributes<'_>, - get_parent_name: impl FnOnce() -> Option, + get_ancestry: impl FnOnce() -> Ancestry, collector_name: &str, ) { let meta = span.metadata(); @@ -837,14 +749,13 @@ impl NewSpan { span.record(&mut checker); checker.finish(); - if let Some(expected_parent) = self.parent.as_ref() { - let actual_parent = get_parent_name(); - expected_parent.check_parent_name( - actual_parent.as_deref(), - span.parent().cloned(), + if let Some(ref expected_ancestry) = self.ancestry { + let actual_ancestry = get_ancestry(); + expected_ancestry.check( + &actual_ancestry, format_args!("span `{}`", name), collector_name, - ) + ); } } } @@ -875,7 +786,7 @@ impl fmt::Debug for NewSpan { s.field("target", &target); } - if let Some(ref parent) = self.parent { + if let Some(ref parent) = self.ancestry { s.field("parent", &format_args!("{:?}", parent)); } diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs index f9560065ff..07d8bb39a0 100644 --- a/tracing-mock/src/subscriber.rs +++ b/tracing-mock/src/subscriber.rs @@ -116,6 +116,7 @@ //! //! [`Subscribe`]: trait@tracing_subscriber::subscribe::Subscribe use crate::{ + ancestry::{get_ancestry, Ancestry, HasAncestry}, collector::MockHandle, event::ExpectedEvent, expect::Expect, @@ -414,7 +415,7 @@ impl MockSubscriberBuilder { /// /// This function accepts `Into` instead of /// [`ExpectedSpan`] directly. [`NewSpan`] can be used to test - /// span fields and the span parent. + /// span fields and the span ancestry. /// /// The new span doesn't need to be entered for this expectation /// to succeed. @@ -905,8 +906,7 @@ where match self.expected.lock().unwrap().pop_front() { None => {} Some(Expect::Event(mut expected)) => { - let get_parent_name = || cx.event_span(event).map(|span| span.name().to_string()); - expected.check(event, get_parent_name, &self.name); + expected.check(event, || context_get_ancestry(event, &cx), &self.name); if let Some(expected_scope) = expected.scope_mut() { self.check_event_scope(cx.event_scope(event), expected_scope); @@ -937,13 +937,7 @@ where let was_expected = matches!(expected.front(), Some(Expect::NewSpan(_))); if was_expected { if let Expect::NewSpan(mut expected) = expected.pop_front().unwrap() { - let get_parent_name = || { - span.parent() - .and_then(|id| cx.span(id)) - .or_else(|| cx.lookup_current()) - .map(|span| span.name().to_string()) - }; - expected.check(span, get_parent_name, &self.name); + expected.check(span, || context_get_ancestry(span, &cx), &self.name); } } } @@ -1043,6 +1037,17 @@ where } } +fn context_get_ancestry(item: impl HasAncestry, ctx: &Context<'_, C>) -> Ancestry +where + C: Collect + for<'a> LookupSpan<'a>, +{ + get_ancestry( + item, + || ctx.lookup_current().map(|s| s.id()), + |span_id| ctx.span(span_id).map(|span| span.name()), + ) +} + impl fmt::Debug for MockSubscriber { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("ExpectSubscriber"); diff --git a/tracing-mock/tests/event_ancestry.rs b/tracing-mock/tests/event_ancestry.rs new file mode 100644 index 0000000000..6bd253d016 --- /dev/null +++ b/tracing-mock/tests/event_ancestry.rs @@ -0,0 +1,346 @@ +//! Tests assertions for the parent made on [`ExpectedEvent`]. +//! +//! The tests in this module completely cover the positive and negative cases +//! when expecting that an event is a contextual or explicit root or expecting +//! that an event has a specific contextual or explicit parent. +//! +//! [`ExpectedEvent`]: crate::event::ExpectedEvent +use tracing::collect::with_default; +use tracing_mock::{collector, expect}; + +#[test] +fn contextual_parent() { + let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but \ + actually has a contextual parent with name='another parent'" +)] +fn contextual_parent_wrong_name() { + let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("another parent").entered(); + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but was actually a \ + contextual root" +)] +fn expect_contextual_parent_actual_contextual_root() { + let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but actually has an \ + explicit parent with name='explicit parent'" +)] +fn expect_contextual_parent_actual_explicit_parent() { + let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info!(parent: span.id(), field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but was actually an \ + explicit root" +)] +fn expect_contextual_parent_actual_explicit_root() { + let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(parent: None, field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +fn contextual_root() { + let event = expect::event().with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be a contextual root, but actually has a contextual parent with \ + name='contextual parent'" +)] +fn expect_contextual_root_actual_contextual_parent() { + let event = expect::event().with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be a contextual root, but actually has an explicit parent with \ + name='explicit parent'" +)] +fn expect_contextual_root_actual_explicit_parent() { + let event = expect::event().with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info!(parent: span.id(), field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic(expected = "to be a contextual root, but was actually an explicit root")] +fn expect_contextual_root_actual_explicit_root() { + let event = expect::event().with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(parent: None, field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_parent() { + let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info!(parent: span.id(), field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but actually has an \ + explicit parent with name='another parent'" +)] +fn explicit_parent_wrong_name() { + let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("another parent"); + tracing::info!(parent: span.id(), field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but actually has a \ + contextual parent with name='contextual parent'" +)] +fn expect_explicit_parent_actual_contextual_parent() { + let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but was actually a \ + contextual root" +)] +fn expect_explicit_parent_actual_contextual_root() { + let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but was actually an \ + explicit root" +)] +fn expect_explicit_parent_actual_explicit_root() { + let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(parent: None, field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_root() { + let event = expect::event().with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(parent: None, field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be an explicit root, but actually has a contextual parent with \ + name='contextual parent'" +)] +fn expect_explicit_root_actual_contextual_parent() { + let event = expect::event().with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .event(event) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic(expected = "to be an explicit root, but was actually a contextual root")] +fn expect_explicit_root_actual_contextual_root() { + let event = expect::event().with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + tracing::info!(field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be an explicit root, but actually has an explicit parent with name='explicit parent'" +)] +fn expect_explicit_root_actual_explicit_parent() { + let event = expect::event().with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info!(parent: span.id(), field = &"value"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_and_contextual_root_is_explicit() { + let event = expect::event().with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock().event(event).run_with_handle(); + + with_default(collector, || { + tracing::info!(parent: None, field = &"value"); + }); + + handle.assert_finished(); +} diff --git a/tracing-mock/tests/span_ancestry.rs b/tracing-mock/tests/span_ancestry.rs new file mode 100644 index 0000000000..603c0a6b3c --- /dev/null +++ b/tracing-mock/tests/span_ancestry.rs @@ -0,0 +1,401 @@ +//! Tests assertions for the parent made on [`ExpectedSpan`]. +//! +//! The tests in this module completely cover the positive and negative cases +//! when expecting that a span is a contextual or explicit root or expecting +//! that a span has a specific contextual or explicit parent. +//! +//! [`ExpectedSpan`]: crate::span::ExpectedSpan +//! +use tracing::collect::with_default; +use tracing_mock::{collector, expect}; + +#[test] +fn contextual_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but \ + actually has a contextual parent with name='another parent'" +)] +fn contextual_parent_wrong_name() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("another parent").entered(); + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but was actually a \ + contextual root" +)] +fn expect_contextual_parent_actual_contextual_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock().new_span(span).run_with_handle(); + + with_default(collector, || { + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but actually has an \ + explicit parent with name='explicit parent'" +)] +fn expect_contextual_parent_actual_explicit_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info_span!(parent: span.id(), "span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have a contextual parent with name='contextual parent', but was actually an \ + explicit root" +)] +fn expect_contextual_parent_actual_explicit_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_contextual_parent("contextual parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!(parent: None, "span"); + }); + + handle.assert_finished(); +} + +#[test] +fn contextual_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock().new_span(span).run_with_handle(); + + with_default(collector, || { + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be a contextual root, but actually has a contextual parent with \ + name='contextual parent'" +)] +fn expect_contextual_root_actual_contextual_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be a contextual root, but actually has an explicit parent with \ + name='explicit parent'" +)] +fn expect_contextual_root_actual_explicit_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info_span!(parent: span.id(), "span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic(expected = "to be a contextual root, but was actually an explicit root")] +fn expect_contextual_root_actual_explicit_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_contextual_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!(parent: None, "span"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info_span!(parent: span.id(), "span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but actually has an \ + explicit parent with name='another parent'" +)] +fn explicit_parent_wrong_name() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("another parent"); + tracing::info_span!(parent: span.id(), "span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but actually has a \ + contextual parent with name='contextual parent'" +)] +fn expect_explicit_parent_actual_contextual_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but was actually a \ + contextual root" +)] +fn expect_explicit_parent_actual_contextual_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock().new_span(span).run_with_handle(); + + with_default(collector, || { + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to have an explicit parent with name='explicit parent', but was actually an \ + explicit root" +)] +fn expect_explicit_parent_actual_explicit_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::has_explicit_parent("explicit parent")); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!(parent: None, "span"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!(parent: None, "span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be an explicit root, but actually has a contextual parent with \ + name='contextual parent'" +)] +fn expect_explicit_root_actual_contextual_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock() + .enter(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let _guard = tracing::info_span!("contextual parent").entered(); + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic(expected = "to be an explicit root, but was actually a contextual root")] +fn expect_explicit_root_actual_contextual_root() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock().new_span(span).run_with_handle(); + + with_default(collector, || { + tracing::info_span!("span"); + }); + + handle.assert_finished(); +} + +#[test] +#[should_panic( + expected = "to be an explicit root, but actually has an explicit parent with name='explicit parent'" +)] +fn expect_explicit_root_actual_explicit_parent() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock() + .new_span(expect::span()) + .new_span(span) + .run_with_handle(); + + with_default(collector, || { + let span = tracing::info_span!("explicit parent"); + tracing::info_span!(parent: span.id(), "span"); + }); + + handle.assert_finished(); +} + +#[test] +fn explicit_and_contextual_root_is_explicit() { + let span = expect::span() + .named("span") + .with_ancestry(expect::is_explicit_root()); + + let (collector, handle) = collector::mock().new_span(span).run_with_handle(); + + with_default(collector, || { + tracing::info_span!(parent: None, "span"); + }); + + handle.assert_finished(); +} diff --git a/tracing/tests/event.rs b/tracing/tests/event.rs index 48a6af774a..950801373e 100644 --- a/tracing/tests/event.rs +++ b/tracing/tests/event.rs @@ -337,7 +337,7 @@ fn both_shorthands() { fn explicit_child() { let (collector, handle) = collector::mock() .new_span(expect::span().named("foo")) - .event(expect::event().with_explicit_parent(Some("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) .only() .run_with_handle(); @@ -354,11 +354,11 @@ fn explicit_child() { fn explicit_child_at_levels() { let (collector, handle) = collector::mock() .new_span(expect::span().named("foo")) - .event(expect::event().with_explicit_parent(Some("foo"))) - .event(expect::event().with_explicit_parent(Some("foo"))) - .event(expect::event().with_explicit_parent(Some("foo"))) - .event(expect::event().with_explicit_parent(Some("foo"))) - .event(expect::event().with_explicit_parent(Some("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) + .event(expect::event().with_ancestry(expect::has_explicit_parent("foo"))) .only() .run_with_handle(); diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs index 6e48de6bfc..36b7394883 100644 --- a/tracing/tests/instrument.rs +++ b/tracing/tests/instrument.rs @@ -37,7 +37,7 @@ fn span_on_drop() { .enter(expect::span().named("foo")) .event( expect::event() - .with_contextual_parent(Some("foo")) + .with_ancestry(expect::has_contextual_parent("foo")) .at_level(Level::INFO), ) .exit(expect::span().named("foo")) @@ -47,7 +47,7 @@ fn span_on_drop() { .enter(expect::span().named("bar")) .event( expect::event() - .with_contextual_parent(Some("bar")) + .with_ancestry(expect::has_contextual_parent("bar")) .at_level(Level::INFO), ) .exit(expect::span().named("bar")) diff --git a/tracing/tests/span.rs b/tracing/tests/span.rs index 9a17cd04d3..bc0798f162 100644 --- a/tracing/tests/span.rs +++ b/tracing/tests/span.rs @@ -636,7 +636,11 @@ fn new_span_with_target_and_log_level() { #[test] fn explicit_root_span_is_root() { let (collector, handle) = collector::mock() - .new_span(expect::span().named("foo").with_explicit_parent(None)) + .new_span( + expect::span() + .named("foo") + .with_ancestry(expect::is_explicit_root()), + ) .only() .run_with_handle(); @@ -653,7 +657,11 @@ fn explicit_root_span_is_root_regardless_of_ctx() { let (collector, handle) = collector::mock() .new_span(expect::span().named("foo")) .enter(expect::span().named("foo")) - .new_span(expect::span().named("bar").with_explicit_parent(None)) + .new_span( + expect::span() + .named("bar") + .with_ancestry(expect::is_explicit_root()), + ) .exit(expect::span().named("foo")) .only() .run_with_handle(); @@ -675,7 +683,7 @@ fn explicit_child() { .new_span( expect::span() .named("bar") - .with_explicit_parent(Some("foo")), + .with_ancestry(expect::has_explicit_parent("foo")), ) .only() .run_with_handle(); @@ -693,11 +701,31 @@ fn explicit_child() { fn explicit_child_at_levels() { let (collector, handle) = collector::mock() .new_span(expect::span().named("foo")) - .new_span(expect::span().named("a").with_explicit_parent(Some("foo"))) - .new_span(expect::span().named("b").with_explicit_parent(Some("foo"))) - .new_span(expect::span().named("c").with_explicit_parent(Some("foo"))) - .new_span(expect::span().named("d").with_explicit_parent(Some("foo"))) - .new_span(expect::span().named("e").with_explicit_parent(Some("foo"))) + .new_span( + expect::span() + .named("a") + .with_ancestry(expect::has_explicit_parent("foo")), + ) + .new_span( + expect::span() + .named("b") + .with_ancestry(expect::has_explicit_parent("foo")), + ) + .new_span( + expect::span() + .named("c") + .with_ancestry(expect::has_explicit_parent("foo")), + ) + .new_span( + expect::span() + .named("d") + .with_ancestry(expect::has_explicit_parent("foo")), + ) + .new_span( + expect::span() + .named("e") + .with_ancestry(expect::has_explicit_parent("foo")), + ) .only() .run_with_handle(); @@ -723,7 +751,7 @@ fn explicit_child_regardless_of_ctx() { .new_span( expect::span() .named("baz") - .with_explicit_parent(Some("foo")), + .with_ancestry(expect::has_explicit_parent("foo")), ) .exit(expect::span().named("bar")) .only() @@ -742,7 +770,11 @@ fn explicit_child_regardless_of_ctx() { #[test] fn contextual_root() { let (collector, handle) = collector::mock() - .new_span(expect::span().named("foo").with_contextual_parent(None)) + .new_span( + expect::span() + .named("foo") + .with_ancestry(expect::is_contextual_root()), + ) .only() .run_with_handle(); @@ -762,7 +794,7 @@ fn contextual_child() { .new_span( expect::span() .named("bar") - .with_contextual_parent(Some("foo")), + .with_ancestry(expect::has_contextual_parent("foo")), ) .exit(expect::span().named("foo")) .only() From fafc5c9ffdf9ff7fb9a0cefa4890b3c159e5e47e Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 1 Oct 2024 12:30:02 -0400 Subject: [PATCH 094/101] chore: clean up warnings on master (#3087) Forward-porting https://github.com/tokio-rs/tracing/pull/3069. --- Cargo.toml | 5 +++++ examples/Cargo.toml | 3 +++ tracing-attributes/Cargo.toml | 3 +++ tracing-attributes/tests/instrument.rs | 7 +++---- tracing-core/Cargo.toml | 9 ++++++++- tracing-core/src/field.rs | 10 ++++------ tracing-futures/Cargo.toml | 3 +++ tracing-futures/src/executor/futures_01.rs | 2 +- tracing-futures/src/executor/mod.rs | 3 ++- tracing-futures/tests/std_future.rs | 2 +- tracing-journald/Cargo.toml | 2 ++ tracing-log/Cargo.toml | 3 +++ tracing-macros/Cargo.toml | 3 +++ tracing-mock/Cargo.toml | 3 +++ tracing-serde/Cargo.toml | 3 +++ tracing-subscriber/Cargo.toml | 6 +++--- tracing-subscriber/src/fmt/format/mod.rs | 1 - tracing-subscriber/src/fmt/time/chrono_crate.rs | 7 ++----- tracing-subscriber/src/fmt/time/datetime.rs | 6 +++++- tracing-subscriber/src/lib.rs | 2 +- tracing-subscriber/src/registry/sharded.rs | 2 +- tracing-tower/Cargo.toml | 3 +++ tracing/Cargo.toml | 9 ++++++++- tracing/tests/instrument.rs | 2 +- 24 files changed, 71 insertions(+), 28 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b83b1fcc47..1624740e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,8 @@ members = [ "tracing-journald", "examples" ] + + # 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)"] } diff --git a/examples/Cargo.toml b/examples/Cargo.toml index f10a324c79..fefb90ef05 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -50,3 +50,6 @@ tempfile = "3.3.0" # fmt examples snafu = "0.6.10" thiserror = "1.0.31" + +[lints] +workspace = true diff --git a/tracing-attributes/Cargo.toml b/tracing-attributes/Cargo.toml index 33d988a5cf..145464cd29 100644 --- a/tracing-attributes/Cargo.toml +++ b/tracing-attributes/Cargo.toml @@ -60,3 +60,6 @@ rustversion = "1.0.9" [badges] maintenance = { status = "experimental" } + +[lints] +workspace = true diff --git a/tracing-attributes/tests/instrument.rs b/tracing-attributes/tests/instrument.rs index 722919fad5..2897316bec 100644 --- a/tracing-attributes/tests/instrument.rs +++ b/tracing-attributes/tests/instrument.rs @@ -100,8 +100,7 @@ fn fields() { #[test] fn skip() { - #[allow(dead_code)] - struct UnDebug(pub u32); + struct UnDebug(); #[instrument(target = "my_target", level = "debug", skip(_arg2, _arg3))] fn my_fn(arg1: usize, _arg2: UnDebug, _arg3: UnDebug) {} @@ -135,8 +134,8 @@ fn skip() { .run_with_handle(); with_default(collector, || { - my_fn(2, UnDebug(0), UnDebug(1)); - my_fn(3, UnDebug(0), UnDebug(1)); + my_fn(2, UnDebug(), UnDebug()); + my_fn(3, UnDebug(), UnDebug()); }); handle.assert_finished(); diff --git a/tracing-core/Cargo.toml b/tracing-core/Cargo.toml index c56b59f262..88ff413eac 100644 --- a/tracing-core/Cargo.toml +++ b/tracing-core/Cargo.toml @@ -39,4 +39,11 @@ once_cell = { version = "1.13.0", optional = true } [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] +# enable unstable features in the documentation +rustdoc-args = ["--cfg", "docsrs", "--cfg", "tracing_unstable"] +# it's necessary to _also_ pass `--cfg tracing_unstable` to rustc, or else +# dependencies will not be enabled, and the docs build will fail. +rustc-args = ["--cfg", "tracing_unstable"] + +[lints] +workspace = true diff --git a/tracing-core/src/field.rs b/tracing-core/src/field.rs index fbaa50dec4..c46b003170 100644 --- a/tracing-core/src/field.rs +++ b/tracing-core/src/field.rs @@ -999,9 +999,8 @@ mod test { use crate::metadata::{Kind, Level, Metadata}; // Make sure TEST_CALLSITE_* have non-zero size, so they can't be located at the same address. - #[allow(dead_code)] - struct TestCallsite1(u8); - static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1(0); + struct TestCallsite1(); + static TEST_CALLSITE_1: TestCallsite1 = TestCallsite1(); static TEST_META_1: Metadata<'static> = metadata! { name: "field_test1", target: module_path!(), @@ -1021,9 +1020,8 @@ mod test { } } - #[allow(dead_code)] - struct TestCallsite2(u8); - static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2(0); + struct TestCallsite2(); + static TEST_CALLSITE_2: TestCallsite2 = TestCallsite2(); static TEST_META_2: Metadata<'static> = metadata! { name: "field_test2", target: module_path!(), diff --git a/tracing-futures/Cargo.toml b/tracing-futures/Cargo.toml index dc48f28eed..bc600f6a29 100644 --- a/tracing-futures/Cargo.toml +++ b/tracing-futures/Cargo.toml @@ -52,3 +52,6 @@ maintenance = { status = "actively-developed" } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/tracing-futures/src/executor/futures_01.rs b/tracing-futures/src/executor/futures_01.rs index 7d4b674af8..fb558878a2 100644 --- a/tracing-futures/src/executor/futures_01.rs +++ b/tracing-futures/src/executor/futures_01.rs @@ -35,7 +35,7 @@ where } #[cfg(feature = "tokio")] -#[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 +#[allow(unreachable_pub, unused_imports)] // https://github.com/rust-lang/rust/issues/57411 pub use self::tokio::*; #[cfg(feature = "tokio")] diff --git a/tracing-futures/src/executor/mod.rs b/tracing-futures/src/executor/mod.rs index 86bad9379c..1e646cafc1 100644 --- a/tracing-futures/src/executor/mod.rs +++ b/tracing-futures/src/executor/mod.rs @@ -3,5 +3,6 @@ mod futures_01; #[cfg(feature = "futures-03")] mod futures_03; +#[allow(unreachable_pub, unused_imports)] #[cfg(feature = "futures-03")] -pub use self::futures_03::*; +pub use futures_03::*; diff --git a/tracing-futures/tests/std_future.rs b/tracing-futures/tests/std_future.rs index fb2a31c130..0ee64dacc5 100644 --- a/tracing-futures/tests/std_future.rs +++ b/tracing-futures/tests/std_future.rs @@ -55,7 +55,7 @@ fn span_on_drop() { } } - #[allow(dead_code)] + #[allow(dead_code)] // Field unused, but logs on `Drop` struct Fut(Option); impl Future for Fut { diff --git a/tracing-journald/Cargo.toml b/tracing-journald/Cargo.toml index ef22014c3d..11e0f05b39 100644 --- a/tracing-journald/Cargo.toml +++ b/tracing-journald/Cargo.toml @@ -25,3 +25,5 @@ serde_json = "1.0.82" serde = { version = "1.0.139", features = ["derive"] } tracing = { path = "../tracing", version = "0.2" } +[lints] +workspace = true diff --git a/tracing-log/Cargo.toml b/tracing-log/Cargo.toml index 8e51f2561c..7caedb5522 100644 --- a/tracing-log/Cargo.toml +++ b/tracing-log/Cargo.toml @@ -37,3 +37,6 @@ maintenance = { status = "actively-maintained" } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/tracing-macros/Cargo.toml b/tracing-macros/Cargo.toml index 3efd4e4374..1c5a74433b 100644 --- a/tracing-macros/Cargo.toml +++ b/tracing-macros/Cargo.toml @@ -25,3 +25,6 @@ tracing-subscriber = { path = "../tracing-subscriber", version = "0.3" } [badges] maintenance = { status = "experimental" } + +[lints] +workspace = true diff --git a/tracing-mock/Cargo.toml b/tracing-mock/Cargo.toml index 22689f3f29..71cc20a25d 100644 --- a/tracing-mock/Cargo.toml +++ b/tracing-mock/Cargo.toml @@ -28,3 +28,6 @@ tokio-stream = "0.1.9" [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/tracing-serde/Cargo.toml b/tracing-serde/Cargo.toml index 8fb16eeac1..83e186afc5 100644 --- a/tracing-serde/Cargo.toml +++ b/tracing-serde/Cargo.toml @@ -31,3 +31,6 @@ serde_json = "1.0.82" [badges] maintenance = { status = "experimental" } + +[lints] +workspace = true diff --git a/tracing-subscriber/Cargo.toml b/tracing-subscriber/Cargo.toml index 92efbff841..edc7f23468 100644 --- a/tracing-subscriber/Cargo.toml +++ b/tracing-subscriber/Cargo.toml @@ -36,6 +36,7 @@ json = ["tracing-serde", "serde", "serde_json"] # Enables support for local time when using the `time` crate timestamp # formatters. local-time = ["time/local-offset"] +nu-ansi-term = ["dep:nu-ansi-term"] [dependencies] tracing-core = { path = "../tracing-core", version = "0.2", default-features = false } @@ -103,6 +104,5 @@ harness = false name = "enter" harness = false -[[bench]] -name = "reload" -harness = false +[lints] +workspace = true diff --git a/tracing-subscriber/src/fmt/format/mod.rs b/tracing-subscriber/src/fmt/format/mod.rs index f867d228d3..f979121da2 100644 --- a/tracing-subscriber/src/fmt/format/mod.rs +++ b/tracing-subscriber/src/fmt/format/mod.rs @@ -679,7 +679,6 @@ impl Format { /// /// - [`Format::flatten_event`] can be used to enable flattening event fields into the root /// object. - /// #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json(self) -> Format { diff --git a/tracing-subscriber/src/fmt/time/chrono_crate.rs b/tracing-subscriber/src/fmt/time/chrono_crate.rs index 1a831efa1b..def3b3b6c4 100644 --- a/tracing-subscriber/src/fmt/time/chrono_crate.rs +++ b/tracing-subscriber/src/fmt/time/chrono_crate.rs @@ -109,18 +109,15 @@ impl FormatTime for ChronoUtc { /// /// [`chrono::format::strftime`]: https://docs.rs/chrono/0.4.9/chrono/format/strftime/index.html #[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Default)] enum ChronoFmtType { /// Format according to the RFC 3339 convention. + #[default] Rfc3339, /// Format according to a custom format string. Custom(String), } -impl Default for ChronoFmtType { - fn default() -> Self { - ChronoFmtType::Rfc3339 - } -} #[cfg(test)] mod tests { diff --git a/tracing-subscriber/src/fmt/time/datetime.rs b/tracing-subscriber/src/fmt/time/datetime.rs index e7f58f9890..68cb730c86 100644 --- a/tracing-subscriber/src/fmt/time/datetime.rs +++ b/tracing-subscriber/src/fmt/time/datetime.rs @@ -192,7 +192,6 @@ // permissive licensing, and of not having licensing issues being an // obstacle to adoption, that text has been removed. - use std::fmt; /// A date/time type which exists primarily to convert `SystemTime` timestamps into an ISO 8601 @@ -332,6 +331,7 @@ impl From for DateTime { #[cfg(test)] mod tests { + use i32; use std::time::{Duration, UNIX_EPOCH}; use super::*; @@ -381,6 +381,8 @@ mod tests { 1, ); + case("2038-01-19T03:14:07.000000Z", i32::MAX as i64, 0); + case("2038-01-19T03:14:08.000000Z", i32::MAX as i64 + 1, 0); case("2038-01-19T03:14:07.000000Z", i32::MAX as i64, 0); case("2038-01-19T03:14:08.000000Z", i32::MAX as i64 + 1, 0); case("1901-12-13T20:45:52.000000Z", i32::MIN as i64, 0); @@ -391,6 +393,8 @@ mod tests { // high date value tests to panic #[cfg(not(target_os = "windows"))] { + case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0); + case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0); case("+292277026596-12-04T15:30:07.000000Z", i64::MAX, 0); case("+292277026596-12-04T15:30:06.000000Z", i64::MAX - 1, 0); case("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0); diff --git a/tracing-subscriber/src/lib.rs b/tracing-subscriber/src/lib.rs index 8f390e19e6..b5787915c2 100644 --- a/tracing-subscriber/src/lib.rs +++ b/tracing-subscriber/src/lib.rs @@ -214,7 +214,7 @@ feature! { /// implementation which tracks per-span data and exposes it to /// [`Subscribe`]s. /// - /// For more information see [`Registry`]. + /// Returns a default [`Registry`]. pub fn registry() -> Registry { Registry::default() } diff --git a/tracing-subscriber/src/registry/sharded.rs b/tracing-subscriber/src/registry/sharded.rs index 0ebcdf9a58..5ad789e9fb 100644 --- a/tracing-subscriber/src/registry/sharded.rs +++ b/tracing-subscriber/src/registry/sharded.rs @@ -588,7 +588,7 @@ mod tests { closed: Vec<(&'static str, Weak<()>)>, } - #[allow(dead_code)] + #[allow(dead_code)] // Field is exercised via checking `Arc::downgrade()` struct SetRemoved(Arc<()>); impl Subscribe for CloseSubscriber diff --git a/tracing-tower/Cargo.toml b/tracing-tower/Cargo.toml index 9153e9bdb7..62fdb00cbc 100644 --- a/tracing-tower/Cargo.toml +++ b/tracing-tower/Cargo.toml @@ -40,3 +40,6 @@ maintenance = { status = "experimental" } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[lints] +workspace = true diff --git a/tracing/Cargo.toml b/tracing/Cargo.toml index a653458fab..4e4a516968 100644 --- a/tracing/Cargo.toml +++ b/tracing/Cargo.toml @@ -108,4 +108,11 @@ maintenance = { status = "actively-developed" } [package.metadata.docs.rs] all-features = true -rustdoc-args = ["--cfg", "docsrs"] +# enable unstable features in the documentation +rustdoc-args = ["--cfg", "docsrs", "--cfg", "tracing_unstable"] +# it's necessary to _also_ pass `--cfg tracing_unstable` to rustc, or else +# dependencies will not be enabled, and the docs build will fail. +rustc-args = ["--cfg", "tracing_unstable"] + +[lints] +workspace = true diff --git a/tracing/tests/instrument.rs b/tracing/tests/instrument.rs index 36b7394883..9ab29c7179 100644 --- a/tracing/tests/instrument.rs +++ b/tracing/tests/instrument.rs @@ -21,7 +21,7 @@ fn span_on_drop() { } } - #[allow(dead_code)] + #[allow(dead_code)] // Field not used, but logs on `Drop` struct Fut(Option); impl Future for Fut { From 4779211d08b58ac4fce56bfcd229d7f775a0162a Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Fri, 26 Jan 2024 23:14:00 +0100 Subject: [PATCH 095/101] attributes: change order of async and unsafe modifier (#2864) When using `#[tracing::instrument]` and the `async unsafe` modifiers the generated function read `unsafe async fn`, which is wrong. Corrected the order and added a test. Fixes: #2576 Signed-off-by: Gabriel Goller --- tracing-attributes/src/expand.rs | 2 +- tracing-attributes/tests/async_fn.rs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index b52cb12aba..b4da403324 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -92,7 +92,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>( quote!( #(#outer_attrs) * - #vis #constness #unsafety #asyncness #abi fn #ident<#gen_params>(#params) #output + #vis #constness #asyncness #unsafety #abi fn #ident<#gen_params>(#params) #output #where_clause { #(#inner_attrs) * diff --git a/tracing-attributes/tests/async_fn.rs b/tracing-attributes/tests/async_fn.rs index 60d772ebd3..31d705c6b0 100644 --- a/tracing-attributes/tests/async_fn.rs +++ b/tracing-attributes/tests/async_fn.rs @@ -32,6 +32,9 @@ async fn test_ret_impl_trait_err(n: i32) -> Result, &' #[instrument] async fn test_async_fn_empty() {} +#[instrument] +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)] From 2d30094781d10f6f66fa056b72cb50f234a3f0ad Mon Sep 17 00:00:00 2001 From: Gabriel Goller Date: Tue, 12 Mar 2024 02:29:33 +0100 Subject: [PATCH 096/101] attributes: extract match scrutinee (#2880) On clippy version 1.76.0 this gives a warning, extracting the scrutinee to a variable fixes this. Fixes: #2876 --- tracing-attributes/src/expand.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tracing-attributes/src/expand.rs b/tracing-attributes/src/expand.rs index b4da403324..37034e3ede 100644 --- a/tracing-attributes/src/expand.rs +++ b/tracing-attributes/src/expand.rs @@ -277,7 +277,8 @@ fn gen_block( let mk_fut = match (err_event, ret_event) { (Some(err_event), Some(ret_event)) => quote_spanned!(block.span()=> async move { - match async move #block.await { + let __match_scrutinee = async move #block.await; + match __match_scrutinee { #[allow(clippy::unit_arg)] Ok(x) => { #ret_event; From bdbaf8007364ed2a766cca851d63de31b7c47e72 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Wed, 9 Oct 2024 21:31:11 +0200 Subject: [PATCH 097/101] examples: add note to examples that they are for tracing 0.2.0 (#3099) It is not uncommon that users who are new to tracing look at the examples in the `master` branch of the repository and find that they don't compile. This is because they are examples which compile with the code from the master branch, which is for the as yet unreleased tracing 0.2.0 ecosystem. Users should instead go to the `v0.1.x` branch to find examples compatible with the crates published on crates.io. This change adds a doc-comment to the beginning of every example file informing the user of this fact and suggesting that they check out the `v0.1.x` branch instead. --- examples/examples/all-levels.rs | 2 + examples/examples/appender-multifile.rs | 4 +- examples/examples/async-fn.rs | 3 ++ examples/examples/attrs-args.rs | 2 + examples/examples/attrs-basic.rs | 2 + .../examples/attrs-literal-field-names.rs | 2 + examples/examples/counters.rs | 2 + examples/examples/custom-error.rs | 3 ++ examples/examples/echo.rs | 3 ++ examples/examples/fmt-compact.rs | 2 + examples/examples/fmt-custom-event.rs | 2 + examples/examples/fmt-custom-field.rs | 3 ++ examples/examples/fmt-json.rs | 2 + examples/examples/fmt-multiple-writers.rs | 3 ++ examples/examples/fmt-pretty.rs | 2 + examples/examples/fmt-source-locations.rs | 3 ++ examples/examples/fmt-stderr.rs | 2 + examples/examples/fmt.rs | 2 + examples/examples/fmt/yak_shave.rs | 3 ++ examples/examples/futures-proxy-server.rs | 3 ++ examples/examples/hyper-echo.rs | 2 + examples/examples/inferno-flame.rs | 2 + examples/examples/instrumented-error.rs | 3 ++ examples/examples/journald.rs | 2 + examples/examples/log.rs | 2 + examples/examples/map-traced-error.rs | 3 ++ examples/examples/panic_hook.rs | 3 ++ examples/examples/serde-yak-shave.rs | 2 + examples/examples/sloggish/main.rs | 3 ++ .../examples/sloggish/sloggish_collector.rs | 2 + examples/examples/spawny-thing.rs | 16 ++++---- examples/examples/subscriber-filter.rs | 2 + examples/examples/thread-info.rs | 39 ++++++++++--------- examples/examples/toggle-subscribers.rs | 23 ++++++----- examples/examples/tokio-spawny-thing.rs | 19 +++++---- examples/examples/tokio_panic_hook.rs | 3 ++ examples/examples/tower-client.rs | 2 + examples/examples/tower-load.rs | 3 ++ examples/examples/tower-server.rs | 2 + 39 files changed, 139 insertions(+), 44 deletions(-) diff --git a/examples/examples/all-levels.rs b/examples/examples/all-levels.rs index 9ae95e632d..4cb1ee542c 100644 --- a/examples/examples/all-levels.rs +++ b/examples/examples/all-levels.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use tracing::Level; #[no_mangle] diff --git a/examples/examples/appender-multifile.rs b/examples/examples/appender-multifile.rs index f96bd8f58e..ebf9acd2d5 100644 --- a/examples/examples/appender-multifile.rs +++ b/examples/examples/appender-multifile.rs @@ -1,6 +1,8 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates the use of multiple files with //! `tracing-appender`'s `RollingFileAppender` -//! use tracing_appender::rolling; use tracing_subscriber::fmt::writer::MakeWriterExt; diff --git a/examples/examples/async-fn.rs b/examples/examples/async-fn.rs index d0d2adecfb..e5fa4a003c 100644 --- a/examples/examples/async-fn.rs +++ b/examples/examples/async-fn.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! Demonstrates using the `trace` attribute macro to instrument `async` //! functions. //! diff --git a/examples/examples/attrs-args.rs b/examples/examples/attrs-args.rs index 2806fe9bc7..460d2192d4 100644 --- a/examples/examples/attrs-args.rs +++ b/examples/examples/attrs-args.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use tracing::{debug, info}; diff --git a/examples/examples/attrs-basic.rs b/examples/examples/attrs-basic.rs index 43ec86d755..cc49ef8eb7 100644 --- a/examples/examples/attrs-basic.rs +++ b/examples/examples/attrs-basic.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use tracing::{debug, info, span, Level}; diff --git a/examples/examples/attrs-literal-field-names.rs b/examples/examples/attrs-literal-field-names.rs index 4ded16561b..60dcfe62f8 100644 --- a/examples/examples/attrs-literal-field-names.rs +++ b/examples/examples/attrs-literal-field-names.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use tracing::{debug, span, Level}; diff --git a/examples/examples/counters.rs b/examples/examples/counters.rs index d5674e445d..43313f75e6 100644 --- a/examples/examples/counters.rs +++ b/examples/examples/counters.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use tracing::{ diff --git a/examples/examples/custom-error.rs b/examples/examples/custom-error.rs index 4be47ffea3..9cc4d939fe 100644 --- a/examples/examples/custom-error.rs +++ b/examples/examples/custom-error.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates using the `tracing-error` crate's `SpanTrace` type //! to attach a trace context to a custom error type. #![deny(rust_2018_idioms)] diff --git a/examples/examples/echo.rs b/examples/examples/echo.rs index ffa2bcb3a7..9d9c4ac8b7 100644 --- a/examples/examples/echo.rs +++ b/examples/examples/echo.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! A "hello world" echo server [from Tokio][echo-example] //! //! This server will create a TCP listener, accept connections in a loop, and diff --git a/examples/examples/fmt-compact.rs b/examples/examples/fmt-compact.rs index a4899ce379..3ec58b9275 100644 --- a/examples/examples/fmt-compact.rs +++ b/examples/examples/fmt-compact.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/fmt-custom-event.rs b/examples/examples/fmt-custom-event.rs index b16fe95f52..f1e8adb06d 100644 --- a/examples/examples/fmt-custom-event.rs +++ b/examples/examples/fmt-custom-event.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/fmt-custom-field.rs b/examples/examples/fmt-custom-field.rs index 2fb5021fa8..044055383e 100644 --- a/examples/examples/fmt-custom-field.rs +++ b/examples/examples/fmt-custom-field.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates overriding the way `tracing-subscriber`'s //! `FmtSubscriber` formats fields on spans and events, using a closure. //! diff --git a/examples/examples/fmt-json.rs b/examples/examples/fmt-json.rs index a0771b9809..4a4e56d110 100644 --- a/examples/examples/fmt-json.rs +++ b/examples/examples/fmt-json.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/fmt-multiple-writers.rs b/examples/examples/fmt-multiple-writers.rs index 41c70b6a93..0beda11d02 100644 --- a/examples/examples/fmt-multiple-writers.rs +++ b/examples/examples/fmt-multiple-writers.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! An example demonstrating how `fmt::Subcriber` can write to multiple //! destinations (in this instance, `stdout` and a file) simultaneously. diff --git a/examples/examples/fmt-pretty.rs b/examples/examples/fmt-pretty.rs index 570b5fb71a..ac2d4d42c1 100644 --- a/examples/examples/fmt-pretty.rs +++ b/examples/examples/fmt-pretty.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/fmt-source-locations.rs b/examples/examples/fmt-source-locations.rs index 799ebb17d5..03e521588b 100644 --- a/examples/examples/fmt-source-locations.rs +++ b/examples/examples/fmt-source-locations.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! Demonstrates displaying events' source code locations with the `fmt` //! subscriber. #![deny(rust_2018_idioms)] diff --git a/examples/examples/fmt-stderr.rs b/examples/examples/fmt-stderr.rs index 116f51b159..4c78b704e3 100644 --- a/examples/examples/fmt-stderr.rs +++ b/examples/examples/fmt-stderr.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use std::io; use tracing::error; diff --git a/examples/examples/fmt.rs b/examples/examples/fmt.rs index a990821df4..ba1aff1751 100644 --- a/examples/examples/fmt.rs +++ b/examples/examples/fmt.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/fmt/yak_shave.rs b/examples/examples/fmt/yak_shave.rs index 6639f8ddd4..ce9e0928c9 100644 --- a/examples/examples/fmt/yak_shave.rs +++ b/examples/examples/fmt/yak_shave.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! use snafu::{ResultExt, Snafu}; use std::error::Error; use thiserror::Error; diff --git a/examples/examples/futures-proxy-server.rs b/examples/examples/futures-proxy-server.rs index f8727df270..dcf936f3b9 100644 --- a/examples/examples/futures-proxy-server.rs +++ b/examples/examples/futures-proxy-server.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! A proxy that forwards data to another server and forwards that server's //! responses back to clients. //! diff --git a/examples/examples/hyper-echo.rs b/examples/examples/hyper-echo.rs index 8c09ac1ccd..13e0d2dc9f 100644 --- a/examples/examples/hyper-echo.rs +++ b/examples/examples/hyper-echo.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use http::{Method, Request, Response, StatusCode}; diff --git a/examples/examples/inferno-flame.rs b/examples/examples/inferno-flame.rs index b68b824678..28e01e4aaf 100644 --- a/examples/examples/inferno-flame.rs +++ b/examples/examples/inferno-flame.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use std::{ env, fs::File, diff --git a/examples/examples/instrumented-error.rs b/examples/examples/instrumented-error.rs index 02b9888e76..3a78b824c5 100644 --- a/examples/examples/instrumented-error.rs +++ b/examples/examples/instrumented-error.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates using the `tracing-error` crate's `SpanTrace` type //! to attach a trace context to a custom error type. #![deny(rust_2018_idioms)] diff --git a/examples/examples/journald.rs b/examples/examples/journald.rs index ba811ac4a1..542bfddf03 100644 --- a/examples/examples/journald.rs +++ b/examples/examples/journald.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] use tracing::{error, info}; use tracing_subscriber::prelude::*; diff --git a/examples/examples/log.rs b/examples/examples/log.rs index 6b1055d962..f587f1527e 100644 --- a/examples/examples/log.rs +++ b/examples/examples/log.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. fn main() { tracing_subscriber::fmt() .with_max_level(tracing::Level::TRACE) diff --git a/examples/examples/map-traced-error.rs b/examples/examples/map-traced-error.rs index 9911e762ef..fb4d382690 100644 --- a/examples/examples/map-traced-error.rs +++ b/examples/examples/map-traced-error.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! An example on composing errors inside of a `TracedError`, such that the //! SpanTrace captured is captured when creating the inner error, but still wraps //! the outer error. diff --git a/examples/examples/panic_hook.rs b/examples/examples/panic_hook.rs index 5bfd77355c..722e0c68eb 100644 --- a/examples/examples/panic_hook.rs +++ b/examples/examples/panic_hook.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates how `tracing` events can be recorded from within a //! panic hook, capturing the span context in which the program panicked. //! diff --git a/examples/examples/serde-yak-shave.rs b/examples/examples/serde-yak-shave.rs index 819dc22a1e..a718543dfd 100644 --- a/examples/examples/serde-yak-shave.rs +++ b/examples/examples/serde-yak-shave.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use std::sync::atomic::{AtomicUsize, Ordering}; use tracing::debug; diff --git a/examples/examples/sloggish/main.rs b/examples/examples/sloggish/main.rs index f4e59c3f84..d49cf71e7e 100644 --- a/examples/examples/sloggish/main.rs +++ b/examples/examples/sloggish/main.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! A simple example demonstrating how one might implement a custom //! collector. //! diff --git a/examples/examples/sloggish/sloggish_collector.rs b/examples/examples/sloggish/sloggish_collector.rs index 32bce14880..68a236a653 100644 --- a/examples/examples/sloggish/sloggish_collector.rs +++ b/examples/examples/sloggish/sloggish_collector.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use nu_ansi_term::{Color, Style}; use tracing::{ field::{Field, Visit}, diff --git a/examples/examples/spawny-thing.rs b/examples/examples/spawny-thing.rs index 2f166cec4a..22a8d667fe 100644 --- a/examples/examples/spawny-thing.rs +++ b/examples/examples/spawny-thing.rs @@ -1,12 +1,14 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! This is a example showing how information is scoped. +//! +//! You can run this example by running the following command in a terminal +//! +//! ``` +//! cargo run --example spawny_thing +//! ``` #![deny(rust_2018_idioms)] -/// This is a example showing how information is scoped. -/// -/// You can run this example by running the following command in a terminal -/// -/// ``` -/// cargo run --example spawny_thing -/// ``` use futures::future::join_all; use std::error::Error; use tracing::{debug, info}; diff --git a/examples/examples/subscriber-filter.rs b/examples/examples/subscriber-filter.rs index 3c6988e5dd..61322767ff 100644 --- a/examples/examples/subscriber-filter.rs +++ b/examples/examples/subscriber-filter.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. #![deny(rust_2018_idioms)] #[path = "fmt/yak_shave.rs"] mod yak_shave; diff --git a/examples/examples/thread-info.rs b/examples/examples/thread-info.rs index beeb7de5c5..41722ea9a2 100644 --- a/examples/examples/thread-info.rs +++ b/examples/examples/thread-info.rs @@ -1,22 +1,25 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! +//! This is a example showing how thread info can be displayed when +//! formatting events with `tracing_subscriber::fmt`. This is useful +//! as `tracing` spans can be entered by multiple threads concurrently, +//! or move across threads freely. +//! +//! You can run this example by running the following command in a terminal +//! +//! ``` +//! cargo run --example thread-info +//! ``` +//! +//! Example output: +//! +//! ```not_rust +//! Jul 17 00:38:07.177 INFO ThreadId(02) thread_info: i=9 +//! Jul 17 00:38:07.177 INFO thread 1 ThreadId(03) thread_info: i=9 +//! Jul 17 00:38:07.177 INFO large name thread 2 ThreadId(04) thread_info: i=9 +//! ``` #![deny(rust_2018_idioms)] -/// This is a example showing how thread info can be displayed when -/// formatting events with `tracing_subscriber::fmt`. This is useful -/// as `tracing` spans can be entered by multiple threads concurrently, -/// or move across threads freely. -/// -/// You can run this example by running the following command in a terminal -/// -/// ``` -/// cargo run --example thread-info -/// ``` -/// -/// Example output: -/// -/// ```not_rust -/// Jul 17 00:38:07.177 INFO ThreadId(02) thread_info: i=9 -/// Jul 17 00:38:07.177 INFO thread 1 ThreadId(03) thread_info: i=9 -/// Jul 17 00:38:07.177 INFO large name thread 2 ThreadId(04) thread_info: i=9 -/// ``` use std::thread; use std::time::Duration; use tracing::info; diff --git a/examples/examples/toggle-subscribers.rs b/examples/examples/toggle-subscribers.rs index c59ac7a3c5..d35551732e 100644 --- a/examples/examples/toggle-subscribers.rs +++ b/examples/examples/toggle-subscribers.rs @@ -1,14 +1,17 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! +//! This is a example showing how `Subscriber`s can be enabled or disabled by +//! by wrapping them with an `Option`. This example shows `fmt` and `json` +//! being toggled based on the `json` command line flag. +//! +//! You can run this example by running the following command in a terminal +//! +//! ``` +//! cargo run --example toggle-subscribers -- --json +//! ``` +//! #![deny(rust_2018_idioms)] -/// This is a example showing how `Subscriber`s can be enabled or disabled by -/// by wrapping them with an `Option`. This example shows `fmt` and `json` -/// being toggled based on the `json` command line flag. -/// -/// You can run this example by running the following command in a terminal -/// -/// ``` -/// cargo run --example toggle-subscribers -- --json -/// ``` -/// use argh::FromArgs; use tracing::info; use tracing_subscriber::{subscribe::CollectExt, util::SubscriberInitExt}; diff --git a/examples/examples/tokio-spawny-thing.rs b/examples/examples/tokio-spawny-thing.rs index b97318a79a..6c79c43e97 100644 --- a/examples/examples/tokio-spawny-thing.rs +++ b/examples/examples/tokio-spawny-thing.rs @@ -1,12 +1,15 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! +//! This is a example showing how information is scoped with tokio's +//! `task::spawn`. +//! +//! You can run this example by running the following command in a terminal +//! +//! ``` +//! cargo run --example tokio-spawny-thing +//! ``` #![deny(rust_2018_idioms)] -/// This is a example showing how information is scoped with tokio's -/// `task::spawn`. -/// -/// You can run this example by running the following command in a terminal -/// -/// ``` -/// cargo run --example tokio-spawny-thing -/// ``` use futures::future::try_join_all; use tracing::{debug, info, instrument, span, Instrument as _, Level}; diff --git a/examples/examples/tokio_panic_hook.rs b/examples/examples/tokio_panic_hook.rs index 634cc328d7..fdb1bef930 100644 --- a/examples/examples/tokio_panic_hook.rs +++ b/examples/examples/tokio_panic_hook.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! This example demonstrates that a custom panic hook can be used to log panic //! messages even when panics are captured (such as when a Tokio task panics). //! diff --git a/examples/examples/tower-client.rs b/examples/examples/tower-client.rs index 355e3b4c35..186427c6f1 100644 --- a/examples/examples/tower-client.rs +++ b/examples/examples/tower-client.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use http::{Method, Request, Uri}; use hyper::{client::Client, Body}; use std::time::Duration; diff --git a/examples/examples/tower-load.rs b/examples/examples/tower-load.rs index 3449bf731e..808a9a31c7 100644 --- a/examples/examples/tower-load.rs +++ b/examples/examples/tower-load.rs @@ -1,3 +1,6 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. +//! //! A demo showing how filtering on values and dynamic filter reloading can be //! used together to help make sense of complex or noisy traces. //! diff --git a/examples/examples/tower-server.rs b/examples/examples/tower-server.rs index da859dd1df..045522593b 100644 --- a/examples/examples/tower-server.rs +++ b/examples/examples/tower-server.rs @@ -1,3 +1,5 @@ +//! NOTE: This is pre-release documentation for the upcoming tracing 0.2.0 ecosystem. For the +//! release examples, please see the `v0.1.x` branch instead. use futures::future; use http::{Request, Response}; use hyper::{Body, Server}; From cb9ec622721c13f63583484c105b05edc598a031 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Tue, 29 Oct 2024 15:38:35 +0100 Subject: [PATCH 098/101] mock: improve ergonomics when an `ExpectedSpan` is needed (#3097) Many of the methods on `MockCollector` take an `ExpectedSpan`. This often requires significant boilerplate. For example, to expect that a span with a specific name enters and then exits, the following code is needed: ```rust let span = expect::span().named("span name"); let (collector, handle) = collector::mock() .enter(span.clone()) .exit(span) .run_with_handle(); ``` In order to make using `tracing-mock` more ergonomic and also more compact, the `MockCollector` and `MockSubscriber` methods that previous took an `ExpectedSpan`, are now generic over `Into`. There are currently 3 implementations of `From` for `ExpectedSpan` which allow the following shorthand uses: `T: Into` - an `ExpectedSpan` will be created that expects to have a name specified by `T`. ```rust let (collector, handle) = collector::mock() .enter("span name") .exit("span name") .run_with_handle(); ``` `&ExpectedId` - an `ExpectedSpan` will be created that expects to have the expected Id. A reference is taken and cloned internally because the caller always needs to use an `ExpectedId` in at least 2 calls to the mock collector/subscriber. ```rust let id = expect::id(); let (collector, handle) = collector::mock() .new_span(&id) .enter(&id) .run_with_handle(); ``` `&ExpectedSpan` - The expected span is taken by reference and cloned. ```rust let span = expect::span().named("span name"); let (collector, handle) = collector::mock() .enter(&span) .exit(&span) .run_with_handle(); ``` In Rust, taking a reference to an object and immediately cloning it is an anti-pattern. It is considered better to force the user to clone outside the API to make the cloning explict. However, in the case of a testing framework, it seems reasonable to prefer a more concise API, rather than having it more explicit. To reduce the size of this PR and to avoid unnecessary churn in other crates, the tests within the tracing repo which use `tracing-mock` will not be updated to use the new `Into` capabilities. The new API is backwards compatible and those tests can remain as they are. --- tracing-mock/src/collector.rs | 74 ++++++++++------- tracing-mock/src/span.rs | 142 +++++++++++++++++++++++++++------ tracing-mock/src/subscriber.rs | 42 +++++----- 3 files changed, 190 insertions(+), 68 deletions(-) diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs index 89f74d05de..4789b7ce64 100644 --- a/tracing-mock/src/collector.rs +++ b/tracing-mock/src/collector.rs @@ -38,11 +38,11 @@ //! .named("my_span"); //! let (collector, handle) = collector::mock() //! // Enter a matching span -//! .enter(span.clone()) +//! .enter(&span) //! // Record an event with message "collect parting message" //! .event(expect::event().with_fields(expect::message("collect parting message"))) //! // Record a value for the field `parting` on a matching span -//! .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +//! .record(&span, expect::field("parting").with_value(&"goodbye world!")) //! // Exit a matching span //! .exit(span) //! // Expect no further messages to be recorded @@ -80,9 +80,9 @@ //! let span = expect::span() //! .named("my_span"); //! let (collector, handle) = collector::mock() -//! .enter(span.clone()) +//! .enter(&span) //! .event(expect::event().with_fields(expect::message("collect parting message"))) -//! .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +//! .record(&span, expect::field("parting").with_value(&"goodbye world!")) //! .exit(span) //! .only() //! .run_with_handle(); @@ -225,11 +225,11 @@ pub struct MockHandle(Arc>>, String); /// .named("my_span"); /// let (collector, handle) = collector::mock() /// // Enter a matching span -/// .enter(span.clone()) +/// .enter(&span) /// // Record an event with message "collect parting message" /// .event(expect::event().with_fields(expect::message("collect parting message"))) /// // Record a value for the field `parting` on a matching span -/// .record(span.clone(), expect::field("parting").with_value(&"goodbye world!")) +/// .record(&span, expect::field("parting").with_value(&"goodbye world!")) /// // Exit a matching span /// .exit(span) /// // Expect no further messages to be recorded @@ -472,8 +472,8 @@ where /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (collector, handle) = collector::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -495,8 +495,8 @@ where /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (collector, handle) = collector::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -511,8 +511,11 @@ where /// /// [`exit`]: fn@Self::exit /// [`only`]: fn@Self::only - pub fn enter(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::Enter(span)); + pub fn enter(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::Enter(span.into())); self } @@ -536,8 +539,8 @@ where /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (collector, handle) = collector::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .run_with_handle(); /// /// tracing::collect::with_default(collector, || { @@ -558,8 +561,8 @@ where /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (collector, handle) = collector::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .run_with_handle(); /// /// tracing::collect::with_default(collector, || { @@ -572,8 +575,11 @@ where /// ``` /// /// [`enter`]: fn@Self::enter - pub fn exit(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::Exit(span)); + pub fn exit(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::Exit(span.into())); self } @@ -627,8 +633,11 @@ where /// /// handle.assert_finished(); /// ``` - pub fn clone_span(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::CloneSpan(span)); + pub fn clone_span(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::CloneSpan(span.into())); self } @@ -644,8 +653,11 @@ where /// /// [`Collect::drop_span`]: fn@tracing::Collect::drop_span #[allow(deprecated)] - pub fn drop_span(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::DropSpan(span)); + pub fn drop_span(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::DropSpan(span.into())); self } @@ -710,9 +722,15 @@ where /// ``` /// /// [`Span::follows_from`]: fn@tracing::Span::follows_from - pub fn follows_from(mut self, consequence: ExpectedSpan, cause: ExpectedSpan) -> Self { - self.expected - .push_back(Expect::FollowsFrom { consequence, cause }); + pub fn follows_from(mut self, consequence: S1, cause: S2) -> Self + where + S1: Into, + S2: Into, + { + self.expected.push_back(Expect::FollowsFrom { + consequence: consequence.into(), + cause: cause.into(), + }); self } @@ -775,11 +793,13 @@ where /// ``` /// /// [`field`]: mod@crate::field - pub fn record(mut self, span: ExpectedSpan, fields: I) -> Self + pub fn record(mut self, span: S, fields: I) -> Self where + S: Into, I: Into, { - self.expected.push_back(Expect::Visit(span, fields.into())); + self.expected + .push_back(Expect::Visit(span.into(), fields.into())); self } diff --git a/tracing-mock/src/span.rs b/tracing-mock/src/span.rs index a568484f76..34568551b3 100644 --- a/tracing-mock/src/span.rs +++ b/tracing-mock/src/span.rs @@ -18,8 +18,8 @@ //! .at_level(tracing::Level::INFO); //! //! let (collector, handle) = collector::mock() -//! .enter(span.clone()) -//! .exit(span) +//! .enter(&span) +//! .exit(&span) //! .run_with_handle(); //! //! tracing::collect::with_default(collector, || { @@ -30,6 +30,25 @@ //! handle.assert_finished(); //! ``` //! +//! Instead of passing an `ExpectedSpan`, the collector methods will also accept +//! anything that implements `Into` which is shorthand for +//! `expect::span().named(name)`. +//! +//! ``` +//! use tracing_mock::collector; +//! +//! let (collector, handle) = collector::mock() +//! .enter("interesting_span") +//! .run_with_handle(); +//! +//! tracing::collect::with_default(collector, || { +//! let span = tracing::info_span!("interesting_span"); +//! let _guard = span.enter(); +//! }); +//! +//! handle.assert_finished(); +//! ``` +// //! The following example asserts the name, level, parent, and fields of the span: //! //! ``` @@ -44,10 +63,10 @@ //! .with_ancestry(expect::has_explicit_parent("parent_span")); //! //! let (collector, handle) = collector::mock() -//! .new_span(expect::span().named("parent_span")) +//! .new_span("parent_span") //! .new_span(new_span) -//! .enter(span.clone()) -//! .exit(span) +//! .enter(&span) +//! .exit(&span) //! .run_with_handle(); //! //! tracing::collect::with_default(collector, || { @@ -75,8 +94,8 @@ //! .at_level(tracing::Level::INFO); //! //! let (collector, handle) = collector::mock() -//! .enter(span.clone()) -//! .exit(span) +//! .enter(&span) +//! .exit(&span) //! .run_with_handle(); //! //! tracing::collect::with_default(collector, || { @@ -115,6 +134,27 @@ pub struct ExpectedSpan { pub(crate) metadata: ExpectedMetadata, } +impl From for ExpectedSpan +where + I: Into, +{ + fn from(name: I) -> Self { + ExpectedSpan::default().named(name) + } +} + +impl From<&ExpectedId> for ExpectedSpan { + fn from(id: &ExpectedId) -> Self { + ExpectedSpan::default().with_id(id.clone()) + } +} + +impl From<&ExpectedSpan> for ExpectedSpan { + fn from(span: &ExpectedSpan) -> Self { + span.clone() + } +} + /// A mock new span. /// /// **Note**: This struct contains expectations that can only be asserted @@ -166,7 +206,8 @@ pub struct ExpectedId { impl ExpectedSpan { /// Sets a name to expect when matching a span. /// - /// If an event is recorded with a name that differs from the one provided to this method, the expectation will fail. + /// If an event is recorded with a name that differs from the one provided to this method, the + /// expectation will fail. /// /// # Examples /// @@ -187,6 +228,25 @@ impl ExpectedSpan { /// handle.assert_finished(); /// ``` /// + /// If only the name of the span needs to be validated, then + /// instead of using the `named` method, a string can be passed + /// to the [`MockCollector`] functions directly. + /// + /// ``` + /// use tracing_mock::collector; + /// + /// let (collector, handle) = collector::mock() + /// .enter("span name") + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// let span = tracing::info_span!("span name"); + /// let _guard = span.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// /// When the span name is different, the assertion will fail: /// /// ```should_panic @@ -205,6 +265,8 @@ impl ExpectedSpan { /// /// handle.assert_finished(); /// ``` + /// + /// [`MockCollector`]: struct@crate::collector::MockCollector pub fn named(self, name: I) -> Self where I: Into, @@ -247,10 +309,41 @@ impl ExpectedSpan { /// let span2 = expect::span().named("span").with_id(id2.clone()); /// /// let (collector, handle) = collector::mock() - /// .new_span(span1.clone()) - /// .new_span(span2.clone()) - /// .enter(span2) - /// .enter(span1) + /// .new_span(&span1) + /// .new_span(&span2) + /// .enter(&span2) + /// .enter(&span1) + /// .run_with_handle(); + /// + /// tracing::collect::with_default(collector, || { + /// fn create_span() -> tracing::Span { + /// tracing::info_span!("span") + /// } + /// + /// let span1 = create_span(); + /// let span2 = create_span(); + /// + /// let _guard2 = span2.enter(); + /// let _guard1 = span1.enter(); + /// }); + /// + /// handle.assert_finished(); + /// ``` + /// + /// Since `ExpectedId` implements `Into`, in cases where + /// only checking on Id is desired, a shorthand version of the previous + /// example can be used. + /// + /// ``` + /// use tracing_mock::{collector, expect}; + /// let id1 = expect::id(); + /// let id2 = expect::id(); + /// + /// let (collector, handle) = collector::mock() + /// .new_span(&id1) + /// .new_span(&id2) + /// .enter(&id2) + /// .enter(&id1) /// .run_with_handle(); /// /// tracing::collect::with_default(collector, || { @@ -279,10 +372,10 @@ impl ExpectedSpan { /// let span2 = expect::span().named("span").with_id(id2.clone()); /// /// let (collector, handle) = collector::mock() - /// .new_span(span1.clone()) - /// .new_span(span2.clone()) - /// .enter(span2) - /// .enter(span1) + /// .new_span(&span1) + /// .new_span(&span2) + /// .enter(&span2) + /// .enter(&span1) /// .run_with_handle(); /// /// tracing::collect::with_default(collector, || { @@ -496,8 +589,8 @@ impl ExpectedSpan { /// .with_ancestry(expect::has_contextual_parent("parent_span")); /// /// let (collector, handle) = collector::mock() - /// .new_span(parent_span.clone()) - /// .enter(parent_span) + /// .new_span(&parent_span) + /// .enter(&parent_span) /// .new_span(span) /// .run_with_handle(); /// @@ -542,8 +635,8 @@ impl ExpectedSpan { /// .with_ancestry(expect::has_explicit_parent("parent_span")); /// /// let (collector, handle) = collector::mock() - /// .new_span(parent_span.clone()) - /// .enter(parent_span) + /// .new_span(&parent_span) + /// .enter(&parent_span) /// .new_span(span) /// .run_with_handle(); /// @@ -695,10 +788,13 @@ impl fmt::Display for ExpectedSpan { } } -impl From for NewSpan { - fn from(span: ExpectedSpan) -> Self { +impl From for NewSpan +where + S: Into, +{ + fn from(span: S) -> Self { Self { - span, + span: span.into(), ..Default::default() } } diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs index 07d8bb39a0..16ed3a59f8 100644 --- a/tracing-mock/src/subscriber.rs +++ b/tracing-mock/src/subscriber.rs @@ -40,11 +40,11 @@ //! .named("my_span"); //! let (subscriber, handle) = subscriber::mock() //! // Enter a matching span -//! .enter(span.clone()) +//! .enter(&span) //! // Record an event with message "collect parting message" //! .event(expect::event().with_fields(expect::message("say hello"))) //! // Exit a matching span -//! .exit(span) +//! .exit(&span) //! // Expect no further messages to be recorded //! .only() //! // Return the collector and handle @@ -82,11 +82,11 @@ //! .named("my_span"); //! let (subscriber, handle) = subscriber::mock() //! // Enter a matching span -//! .enter(span.clone()) +//! .enter(&span) //! // Record an event with message "collect parting message" //! .event(expect::event().with_fields(expect::message("say hello"))) //! // Exit a matching span -//! .exit(span) +//! .exit(&span) //! // Expect no further messages to be recorded //! .only() //! // Return the collector and handle @@ -153,11 +153,11 @@ use std::{ /// .named("my_span"); /// let (subscriber, handle) = subscriber::mock() /// // Enter a matching span -/// .enter(span.clone()) +/// .enter(&span) /// // Record an event with message "collect parting message" /// .event(expect::event().with_fields(expect::message("say hello"))) /// // Exit a matching span -/// .exit(span) +/// .exit(&span) /// // Expect no further messages to be recorded /// .only() /// // Return the collector and handle @@ -505,8 +505,8 @@ impl MockSubscriberBuilder { /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (subscriber, handle) = subscriber::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -533,8 +533,8 @@ impl MockSubscriberBuilder { /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (subscriber, handle) = subscriber::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -553,8 +553,11 @@ impl MockSubscriberBuilder { /// /// [`exit`]: fn@Self::exit /// [`only`]: fn@Self::only - pub fn enter(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::Enter(span)); + pub fn enter(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::Enter(span.into())); self } @@ -582,8 +585,8 @@ impl MockSubscriberBuilder { /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (subscriber, handle) = subscriber::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -609,8 +612,8 @@ impl MockSubscriberBuilder { /// .at_level(tracing::Level::INFO) /// .named("the span we're testing"); /// let (subscriber, handle) = subscriber::mock() - /// .enter(span.clone()) - /// .exit(span) + /// .enter(&span) + /// .exit(&span) /// .only() /// .run_with_handle(); /// @@ -630,8 +633,11 @@ impl MockSubscriberBuilder { /// [`enter`]: fn@Self::enter /// [`MockHandle::assert_finished`]: fn@crate::collector::MockHandle::assert_finished /// [`Span::enter`]: fn@tracing::Span::enter - pub fn exit(mut self, span: ExpectedSpan) -> Self { - self.expected.push_back(Expect::Exit(span)); + pub fn exit(mut self, span: S) -> Self + where + S: Into, + { + self.expected.push_back(Expect::Exit(span.into())); self } From 8bf753e2eef2116218ef0eb3d0a12b7da56da6d5 Mon Sep 17 00:00:00 2001 From: Hayden Stainsby Date: Wed, 30 Oct 2024 13:59:25 +0100 Subject: [PATCH 099/101] ci: pin Rust to 1.81 for `wasm32-unknown-unknown` tests (#3125) There is an incompatibility with the version of Node available on our test runners and wasm32 in Rust 1.82 (#3123). To unblock the CI, this change pins Rust to 1.81 for the tests using the `wasm32-unknown-unknown` target. This is the same strategy used in Tokio to mitigate tokio-rs/tokio#6910 until a more permanent fix can be put in place. This change also bumps the MSRV on the `tracing-examples` crate from 1.63.0 to 1.64.0 to avoid triggering a lint about the MSRV after a change in Tokio 1.41.0 which bumps the required Rust version for the `try_join!` macro. The Tokio MSRV is 1.70 now, so needing this bump for the examples seems reasonable. --- .github/workflows/CI.yml | 4 +++- examples/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1f1ee1b63f..5359105391 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -269,8 +269,10 @@ jobs: - tracing steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Install Rust 1.81 + uses: dtolnay/rust-toolchain@stable with: + toolchain: 1.81 target: wasm32-unknown-unknown - name: install test runner for wasm uses: taiki-e/install-action@wasm-pack diff --git a/examples/Cargo.toml b/examples/Cargo.toml index fefb90ef05..8d15f8757b 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -3,7 +3,7 @@ name = "tracing-examples" version = "0.0.0" publish = false edition = "2018" -rust-version = "1.63.0" +rust-version = "1.64.0" [features] default = [] From 2fdbf0952e3c447d28f2ce144c38d33b58eb316f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Wed, 30 Oct 2024 15:05:25 +0100 Subject: [PATCH 100/101] Add `json-subscriber` to ecosystem (#3121) ## Motivation I've created a library for better customization of JSON log lines and would like to make it more discoverable. This subscriber could help with a lot of issues such as #1531 ## Solution Add `json-subscriber` to the ecosystem. --- tracing/README.md | 2 ++ tracing/src/lib.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/tracing/README.md b/tracing/README.md index 73f5e75f08..c94185ad25 100644 --- a/tracing/README.md +++ b/tracing/README.md @@ -379,6 +379,7 @@ maintained by the `tokio` project. These include: - [`sentry-tracing`] provides a layer for reporting events and traces to [Sentry]. - [`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. +- [`json-subscriber`] provides a layer for emitting JSON logs. The output can be customized much more than with [`FmtSubscriber`]'s JSON output. If you're the maintainer of a `tracing` ecosystem crate not listed above, please let us know! We'd love to add your project to the list! @@ -410,6 +411,7 @@ please let us know! We'd love to add your project to the list! [`tracing-loki`]: https://crates.io/crates/tracing-loki [Grafana Loki]: https://grafana.com/oss/loki/ [`tracing-logfmt`]: https://crates.io/crates/tracing-logfmt +[`json-subscriber`]: https://crates.io/crates/json-subscriber **Note:** that some of the ecosystem crates are currently unreleased and undergoing active development. They may be less stable than `tracing` and diff --git a/tracing/src/lib.rs b/tracing/src/lib.rs index 3e88cf9f2a..0b403f3b20 100644 --- a/tracing/src/lib.rs +++ b/tracing/src/lib.rs @@ -817,6 +817,7 @@ //! - [`reqwest-tracing`] provides a middleware to trace [`reqwest`] HTTP requests. //! - [`tracing-cloudwatch`] provides a layer that sends events to AWS CloudWatch Logs. //! - [`clippy-tracing`] provides a tool to add, remove and check for `tracing::instrument`. +//! - [`json-subscriber`] provides a subscriber for emitting JSON logs. The output can be customized much more than with [`tracing-subscriber`]'s JSON output. //! //! If you're the maintainer of a `tracing` ecosystem crate not listed above, //! please let us know! We'd love to add your project to the list! @@ -860,6 +861,7 @@ //! [`reqwest`]: https://crates.io/crates/reqwest //! [`tracing-cloudwatch`]: https://crates.io/crates/tracing-cloudwatch //! [`clippy-tracing`]: https://crates.io/crates/clippy-tracing +//! [`json-subscriber`]: https://crates.io/crates/json-subscriber //! //!
//!

From 21c5ce05ccf5fbcd2e1244228b18438f18a71cd4 Mon Sep 17 00:00:00 2001
From: Hayden Stainsby 
Date: Wed, 30 Oct 2024 15:31:46 +0100
Subject: [PATCH 101/101] mock: match parent span on `ExpectedSpan` (#3098)

## Motivation

The `with_ancestry` methods on `NewSpan` and `ExpectedEvent` provide a
way to match whether the span or event is a contextual or explicit root
or if it has a contextual or explicit parent span.

However, in the case of matching on a contextual or explicit parent
span, only the span name could be used for matching. This is
sufficiently precise when testing tracing instrumentation in other
libraries or applications as opposed to testing tracing itself.

It is likely that a user would like to test that some span or event has
a specific span as a parent, and not just any span with a specific name,
in many cases, all the possible parent spans may have the same name.
This is the case when testing tracing instrumentation in Tokio.

## Solution

To solve this problem, the `Ancestry` struct was renamed to
`ExpectedAncestry` and in the case of expecting an explicit or
conextual parent, an `ExpectedSpan` object can be passed in. This
provides the maximum possible flexibility.

The convenience functions in the `expect` module now take
`Into` so that existing tests that pass a string type
object for the parent will see the same behaviour as previously and
shorthand use for expected Ids is also available.

Additionally, the span checking code has been unified between the
`MockCollector` and `MockSubscriber` cases and the assertion
descriptions have been improved to make them more readable.
---
 tracing-mock/src/ancestry.rs         | 119 ++++++++++++--------
 tracing-mock/src/collector.rs        |  69 ++++++------
 tracing-mock/src/event.rs            |  53 +++++++--
 tracing-mock/src/expect.rs           |  26 ++---
 tracing-mock/src/metadata.rs         |  65 +++++++----
 tracing-mock/src/span.rs             | 159 ++++++++++++++++++++------
 tracing-mock/src/subscriber.rs       |  86 +++-----------
 tracing-mock/tests/event_ancestry.rs | 148 +++++++++++++++++--------
 tracing-mock/tests/span_ancestry.rs  | 160 +++++++++++++++++++--------
 9 files changed, 574 insertions(+), 311 deletions(-)

diff --git a/tracing-mock/src/ancestry.rs b/tracing-mock/src/ancestry.rs
index ee661d45e8..817e30fef0 100644
--- a/tracing-mock/src/ancestry.rs
+++ b/tracing-mock/src/ancestry.rs
@@ -1,71 +1,102 @@
 //! Define the ancestry of an event or span.
 //!
-//! See the documentation on the [`Ancestry`] enum for further details.
+//! See the documentation on the [`ExpectedAncestry`] enum for further details.
 
 use tracing_core::{
     span::{self, Attributes},
     Event,
 };
 
+use crate::span::{ActualSpan, ExpectedSpan};
+
 /// The ancestry of an event or span.
 ///
 /// An event or span can have an explicitly assigned parent, or be an explicit root. Otherwise,
 /// an event or span may have a contextually assigned parent or in the final case will be a
 /// contextual root.
 #[derive(Debug, Eq, PartialEq)]
-pub enum Ancestry {
-    /// The event or span has an explicitly assigned parent (created with `parent: span_id`) with
-    /// the specified name.
-    HasExplicitParent(String),
+pub enum ExpectedAncestry {
+    /// The event or span has an explicitly assigned parent (created with `parent: span_id`) span.
+    HasExplicitParent(ExpectedSpan),
     /// The event or span is an explicitly defined root. It was created with `parent: None` and
     /// has no parent.
     IsExplicitRoot,
-    /// The event or span has a contextually assigned parent with the specified name. It has no
-    /// explicitly assigned parent, nor has it been explicitly defined as a root (it was created
-    /// without the `parent:` directive). There was a span in context when this event or span was
-    /// created.
-    HasContextualParent(String),
+    /// The event or span has a contextually assigned parent span. It has no explicitly assigned
+    /// parent span, nor has it been explicitly defined as a root (it was created without the
+    /// `parent:` directive). There was a span in context when this event or span was created.
+    HasContextualParent(ExpectedSpan),
     /// The event or span is a contextual root. It has no explicitly assigned parent, nor has it
     /// been explicitly defined as a root (it was created without the `parent:` directive).
     /// Additionally, no span was in context when this event or span was created.
     IsContextualRoot,
 }
 
-impl Ancestry {
+pub(crate) enum ActualAncestry {
+    HasExplicitParent(ActualSpan),
+    IsExplicitRoot,
+    HasContextualParent(ActualSpan),
+    IsContextualRoot,
+}
+
+impl ExpectedAncestry {
     #[track_caller]
     pub(crate) fn check(
         &self,
-        actual_ancestry: &Ancestry,
+        actual_ancestry: &ActualAncestry,
         ctx: impl std::fmt::Display,
         collector_name: &str,
     ) {
-        let expected_description = |ancestry: &Ancestry| match ancestry {
-            Self::IsExplicitRoot => "be an explicit root".to_string(),
-            Self::HasExplicitParent(name) => format!("have an explicit parent with name='{name}'"),
-            Self::IsContextualRoot => "be a contextual root".to_string(),
-            Self::HasContextualParent(name) => {
-                format!("have a contextual parent with name='{name}'")
+        match (self, actual_ancestry) {
+            (Self::IsExplicitRoot, ActualAncestry::IsExplicitRoot) => {}
+            (Self::IsContextualRoot, ActualAncestry::IsContextualRoot) => {}
+            (
+                Self::HasExplicitParent(expected_parent),
+                ActualAncestry::HasExplicitParent(actual_parent),
+            ) => {
+                expected_parent.check(
+                    actual_parent,
+                    format_args!("{ctx} to have an explicit parent span"),
+                    collector_name,
+                );
             }
-        };
-
-        let actual_description = |ancestry: &Ancestry| match ancestry {
-            Self::IsExplicitRoot => "was actually an explicit root".to_string(),
-            Self::HasExplicitParent(name) => {
-                format!("actually has an explicit parent with name='{name}'")
+            (
+                Self::HasContextualParent(expected_parent),
+                ActualAncestry::HasContextualParent(actual_parent),
+            ) => {
+                println!("----> [{collector_name}] check {expected_parent:?} against actual parent with Id={id:?}", id = actual_parent.id());
+                expected_parent.check(
+                    actual_parent,
+                    format_args!("{ctx} to have a contextual parent span"),
+                    collector_name,
+                );
             }
-            Self::IsContextualRoot => "was actually a contextual root".to_string(),
-            Self::HasContextualParent(name) => {
-                format!("actually has a contextual parent with name='{name}'")
+            _ => {
+                // Ancestry types don't match at all.
+                let expected_description = match self {
+                    Self::IsExplicitRoot => "be an explicit root",
+                    Self::HasExplicitParent(_) => "have an explicit parent span",
+                    Self::IsContextualRoot => "be a contextual root",
+                    Self::HasContextualParent(_) => "have a contextual parent span",
+                };
+
+                let actual_description = match actual_ancestry {
+                    ActualAncestry::IsExplicitRoot => "is actually an explicit root",
+                    ActualAncestry::HasExplicitParent(_) => "actually has an explicit parent span",
+                    ActualAncestry::IsContextualRoot => "is actually a contextual root",
+                    ActualAncestry::HasContextualParent(_) => {
+                        "actually has a contextual parent span"
+                    }
+                };
+
+                panic!(
+                    "{}",
+                    format!(
+                        "[{collector_name}] expected {ctx} to {expected_description}, \
+                        but it {actual_description}"
+                    )
+                );
             }
-        };
-
-        assert_eq!(
-            self,
-            actual_ancestry,
-            "[{collector_name}] expected {ctx} to {expected_description}, but {actual_description}",
-            expected_description = expected_description(self),
-            actual_description = actual_description(actual_ancestry)
-        );
+        }
     }
 }
 
@@ -120,29 +151,29 @@ impl HasAncestry for &Attributes<'_> {
 pub(crate) fn get_ancestry(
     item: impl HasAncestry,
     lookup_current: impl FnOnce() -> Option,
-    span_name: impl FnOnce(&span::Id) -> Option<&str>,
-) -> Ancestry {
+    actual_span: impl FnOnce(&span::Id) -> Option,
+) -> ActualAncestry {
     if item.is_contextual() {
         if let Some(parent_id) = lookup_current() {
-            let contextual_parent_name = span_name(&parent_id).expect(
+            let contextual_parent_span = actual_span(&parent_id).expect(
                 "tracing-mock: contextual parent cannot \
                             be looked up by ID. Was it recorded correctly?",
             );
-            Ancestry::HasContextualParent(contextual_parent_name.to_string())
+            ActualAncestry::HasContextualParent(contextual_parent_span)
         } else {
-            Ancestry::IsContextualRoot
+            ActualAncestry::IsContextualRoot
         }
     } else if item.is_root() {
-        Ancestry::IsExplicitRoot
+        ActualAncestry::IsExplicitRoot
     } else {
         let parent_id = item.parent().expect(
             "tracing-mock: is_contextual=false is_root=false \
                         but no explicit parent found. This is a bug!",
         );
-        let explicit_parent_name = span_name(parent_id).expect(
+        let explicit_parent_span = actual_span(parent_id).expect(
             "tracing-mock: explicit parent cannot be looked \
                         up by ID. Is the provided Span ID valid: {parent_id}",
         );
-        Ancestry::HasExplicitParent(explicit_parent_name.to_string())
+        ActualAncestry::HasExplicitParent(explicit_parent_span)
     }
 }
diff --git a/tracing-mock/src/collector.rs b/tracing-mock/src/collector.rs
index 4789b7ce64..9f27e10785 100644
--- a/tracing-mock/src/collector.rs
+++ b/tracing-mock/src/collector.rs
@@ -142,7 +142,7 @@ use crate::{
     event::ExpectedEvent,
     expect::Expect,
     field::ExpectedFields,
-    span::{ExpectedSpan, NewSpan},
+    span::{ActualSpan, ExpectedSpan, NewSpan},
 };
 use std::{
     collections::{HashMap, VecDeque},
@@ -160,19 +160,15 @@ use tracing::{
 };
 
 pub(crate) struct SpanState {
-    id: u64,
+    id: Id,
     name: &'static str,
     refs: usize,
     meta: &'static Metadata<'static>,
 }
 
-impl SpanState {
-    pub(crate) fn id(&self) -> u64 {
-        self.id
-    }
-
-    pub(crate) fn metadata(&self) -> &'static Metadata<'static> {
-        self.meta
+impl From<&SpanState> for ActualSpan {
+    fn from(span_state: &SpanState) -> Self {
+        Self::new(span_state.id.clone(), Some(span_state.meta))
     }
 }
 
@@ -1069,7 +1065,7 @@ where
                                 .lock()
                                 .unwrap()
                                 .get(span_id)
-                                .map(|span| span.name)
+                                .map(|span| span.into())
                         },
                     )
                 };
@@ -1140,7 +1136,7 @@ where
                         get_ancestry(
                             span,
                             || self.lookup_current(),
-                            |span_id| spans.get(span_id).map(|span| span.name),
+                            |span_id| spans.get(span_id).map(|span| span.into()),
                         )
                     },
                     &self.name,
@@ -1150,7 +1146,7 @@ where
         spans.insert(
             id.clone(),
             SpanState {
-                id: id.into_u64(),
+                id: id.clone(),
                 name: meta.name(),
                 refs: 1,
                 meta,
@@ -1166,7 +1162,7 @@ where
             match self.expected.lock().unwrap().pop_front() {
                 None => {}
                 Some(Expect::Enter(ref expected_span)) => {
-                    expected_span.check(span, &self.name);
+                    expected_span.check(&span.into(), "to enter a span", &self.name);
                 }
                 Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name)),
             }
@@ -1189,7 +1185,7 @@ where
         match self.expected.lock().unwrap().pop_front() {
             None => {}
             Some(Expect::Exit(ref expected_span)) => {
-                expected_span.check(span, &self.name);
+                expected_span.check(&span.into(), "to exit a span", &self.name);
                 let curr = self.current.lock().unwrap().pop();
                 assert_eq!(
                     Some(id),
@@ -1205,27 +1201,34 @@ where
     }
 
     fn clone_span(&self, id: &Id) -> Id {
-        let name = self.spans.lock().unwrap().get_mut(id).map(|span| {
-            let name = span.name;
-            println!(
-                "[{}] clone_span: {}; id={:?}; refs={:?};",
-                self.name, name, id, span.refs
-            );
-            span.refs += 1;
-            name
-        });
-        if name.is_none() {
-            println!("[{}] clone_span: id={:?};", self.name, id);
+        let mut spans = self.spans.lock().unwrap();
+        let mut span = spans.get_mut(id);
+        match span.as_deref_mut() {
+            Some(span) => {
+                println!(
+                    "[{}] clone_span: {}; id={:?}; refs={:?};",
+                    self.name, span.name, id, span.refs,
+                );
+                span.refs += 1;
+            }
+            None => {
+                println!(
+                    "[{}] clone_span: id={:?} (not found in span list);",
+                    self.name, id
+                );
+            }
         }
+
         let mut expected = self.expected.lock().unwrap();
-        let was_expected = if let Some(Expect::CloneSpan(ref span)) = expected.front() {
-            assert_eq!(
-                name,
-                span.name(),
-                "[{}] expected to clone a span named {:?}",
-                self.name,
-                span.name()
-            );
+        let was_expected = if let Some(Expect::CloneSpan(ref expected_span)) = expected.front() {
+            match span {
+                Some(actual_span) => {
+                    let actual_span: &_ = actual_span;
+                    expected_span.check(&actual_span.into(), "to clone a span", &self.name);
+                }
+                // Check only by Id
+                None => expected_span.check(&id.into(), "to clone a span", &self.name),
+            }
             true
         } else {
             false
diff --git a/tracing-mock/src/event.rs b/tracing-mock/src/event.rs
index 630f005a84..c9445a107e 100644
--- a/tracing-mock/src/event.rs
+++ b/tracing-mock/src/event.rs
@@ -29,7 +29,12 @@
 //! [`collector`]: mod@crate::collector
 //! [`expect::event`]: fn@crate::expect::event
 #![allow(missing_docs)]
-use crate::{ancestry::Ancestry, expect, field, metadata::ExpectedMetadata, span};
+use crate::{
+    ancestry::{ActualAncestry, ExpectedAncestry},
+    expect, field,
+    metadata::ExpectedMetadata,
+    span,
+};
 
 use std::fmt;
 
@@ -42,7 +47,7 @@ use std::fmt;
 #[derive(Default, Eq, PartialEq)]
 pub struct ExpectedEvent {
     pub(super) fields: Option,
-    pub(super) ancestry: Option,
+    pub(super) ancestry: Option,
     pub(super) in_spans: Option>,
     pub(super) metadata: ExpectedMetadata,
 }
@@ -253,9 +258,10 @@ impl ExpectedEvent {
         }
     }
 
-    /// Configures this `ExpectedEvent` to expect the specified [`Ancestry`].
-    /// An event's ancestry indicates whether is has a parent or is a root, and
-    /// whether the parent is explicitly or contextually assigned.
+    /// Configures this `ExpectedEvent` to expect the specified
+    /// [`ExpectedAncestry`]. An event's ancestry indicates whether is has a
+    /// parent or is a root, and whether the parent is explicitly or
+    /// contextually assigned.
     ///
     /// An _explicit_ parent span is one passed to the `event!` macro in the
     /// `parent:` field. If no `parent:` field is specified, then the event
@@ -267,9 +273,34 @@ impl ExpectedEvent {
     ///
     /// # Examples
     ///
-    /// If `expect::has_explicit_parent("parent_name")` is passed
-    /// `with_ancestry` then the provided string is the name of the explicit
-    /// parent span to expect.
+    /// An explicit or contextual can be matched on an `ExpectedSpan`.
+    ///
+    /// ```
+    /// use tracing::collect::with_default;
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let parent = expect::span()
+    ///     .named("parent_span")
+    ///     .with_target("custom-target")
+    ///     .at_level(tracing::Level::INFO);
+    /// let event = expect::event()
+    ///     .with_ancestry(expect::has_explicit_parent(parent));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .event(event)
+    ///     .run_with_handle();
+    ///
+    /// with_default(collector, || {
+    ///     let parent = tracing::info_span!(target: "custom-target", "parent_span");
+    ///     tracing::info!(parent: parent.id(), field = &"value");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    /// The functions `expect::has_explicit_parent` and
+    /// `expect::has_contextual_parent` take `Into`, so a string
+    /// passed directly will match on a span with that name, or an
+    /// [`ExpectedId`] can be passed to match a span with that Id.
     ///
     /// ```
     /// use tracing::collect::with_default;
@@ -382,7 +413,9 @@ impl ExpectedEvent {
     ///
     /// handle.assert_finished();
     /// ```
-    pub fn with_ancestry(self, ancenstry: Ancestry) -> ExpectedEvent {
+    ///
+    /// [`ExpectedId`]: struct@crate::span::ExpectedId
+    pub fn with_ancestry(self, ancenstry: ExpectedAncestry) -> ExpectedEvent {
         Self {
             ancestry: Some(ancenstry),
             ..self
@@ -506,7 +539,7 @@ impl ExpectedEvent {
     pub(crate) fn check(
         &mut self,
         event: &tracing::Event<'_>,
-        get_ancestry: impl FnOnce() -> Ancestry,
+        get_ancestry: impl FnOnce() -> ActualAncestry,
         collector_name: &str,
     ) {
         let meta = event.metadata();
diff --git a/tracing-mock/src/expect.rs b/tracing-mock/src/expect.rs
index 95ad3176ca..aebcc9eecd 100644
--- a/tracing-mock/src/expect.rs
+++ b/tracing-mock/src/expect.rs
@@ -1,7 +1,7 @@
 use std::fmt;
 
 use crate::{
-    ancestry::Ancestry,
+    ancestry::ExpectedAncestry,
     event::ExpectedEvent,
     field::{ExpectedField, ExpectedFields, ExpectedValue},
     span::{ExpectedId, ExpectedSpan, NewSpan},
@@ -71,26 +71,26 @@ pub fn id() -> ExpectedId {
     ExpectedId::new_unset()
 }
 
-/// Convenience function that returns [`Ancestry::IsContextualRoot`].
-pub fn is_contextual_root() -> Ancestry {
-    Ancestry::IsContextualRoot
+/// Convenience function that returns [`ExpectedAncestry::IsContextualRoot`].
+pub fn is_contextual_root() -> ExpectedAncestry {
+    ExpectedAncestry::IsContextualRoot
 }
 
-/// Convenience function that returns [`Ancestry::HasContextualParent`] with
+/// Convenience function that returns [`ExpectedAncestry::HasContextualParent`] with
 /// provided name.
-pub fn has_contextual_parent>(name: S) -> Ancestry {
-    Ancestry::HasContextualParent(name.into())
+pub fn has_contextual_parent>(span: S) -> ExpectedAncestry {
+    ExpectedAncestry::HasContextualParent(span.into())
 }
 
-/// Convenience function that returns [`Ancestry::IsExplicitRoot`].
-pub fn is_explicit_root() -> Ancestry {
-    Ancestry::IsExplicitRoot
+/// Convenience function that returns [`ExpectedAncestry::IsExplicitRoot`].
+pub fn is_explicit_root() -> ExpectedAncestry {
+    ExpectedAncestry::IsExplicitRoot
 }
 
-/// Convenience function that returns [`Ancestry::HasExplicitParent`] with
+/// Convenience function that returns [`ExpectedAncestry::HasExplicitParent`] with
 /// provided name.
-pub fn has_explicit_parent>(name: S) -> Ancestry {
-    Ancestry::HasExplicitParent(name.into())
+pub fn has_explicit_parent>(span: S) -> ExpectedAncestry {
+    ExpectedAncestry::HasExplicitParent(span.into())
 }
 
 impl Expect {
diff --git a/tracing-mock/src/metadata.rs b/tracing-mock/src/metadata.rs
index 9d2d341145..d33ca209cb 100644
--- a/tracing-mock/src/metadata.rs
+++ b/tracing-mock/src/metadata.rs
@@ -9,48 +9,69 @@ pub(crate) struct ExpectedMetadata {
 }
 
 impl ExpectedMetadata {
+    /// Checks the given metadata against this expected metadata and panics if
+    /// there is a mismatch.
+    ///
+    /// The context `ctx` should fit into the followint sentence:
+    ///
+    /// > expected {ctx} named `expected_name`, but got one named `actual_name`
+    ///
+    /// Examples could be:
+    /// * a new span
+    /// * to enter a span
+    /// * an event
+    ///
+    /// # Panics
+    ///
+    /// This method will panic if any of the expectations that have been
+    /// specified are noto met.
+    ///
     pub(crate) fn check(
         &self,
         actual: &Metadata<'_>,
-        ctx: fmt::Arguments<'_>,
+        ctx: impl fmt::Display,
         collector_name: &str,
     ) {
         if let Some(ref expected_name) = self.name {
-            let name = actual.name();
+            let actual_name = actual.name();
             assert!(
-                expected_name == name,
-                "\n[{}] expected {} to be named `{}`, but got one named `{}`",
-                collector_name,
-                ctx,
-                expected_name,
-                name
+                expected_name == actual_name,
+                "{}",
+                format_args!(
+                    "\n[{collector_name}] expected {ctx} named `{expected_name}`,\n\
+                    [{collector_name}] but got one named `{actual_name}` instead."
+                ),
             )
         }
 
         if let Some(ref expected_level) = self.level {
-            let level = actual.level();
+            let actual_level = actual.level();
             assert!(
-                expected_level == level,
-                "\n[{}] expected {} to be at level `{:?}`, but it was at level `{:?}` instead",
-                collector_name,
-                ctx,
-                expected_level,
-                level,
+                expected_level == actual_level,
+                "{}",
+                format_args!(
+                    "\n[{collector_name}] expected {ctx} at level `{expected_level:?}`,\n\
+                    [{collector_name}] but got one at level `{actual_level:?}` instead."
+                ),
             )
         }
 
         if let Some(ref expected_target) = self.target {
-            let target = actual.target();
+            let actual_target = actual.target();
             assert!(
-                expected_target == target,
-                "\n[{}] expected {} to have target `{}`, but it had target `{}` instead",
-                collector_name,
-                ctx,
-                expected_target,
-                target,
+                expected_target == actual_target,
+                "{}",
+                format_args!(
+                    "\n[{collector_name}] expected {ctx} with target `{expected_target}`,\n\
+                    [{collector_name}] but got one with target `{actual_target}` instead."
+                ),
             )
         }
     }
+
+    pub(crate) fn has_expectations(&self) -> bool {
+        self.name.is_some() || self.level.is_some() || self.target.is_some()
+    }
 }
 
 impl fmt::Display for ExpectedMetadata {
diff --git a/tracing-mock/src/span.rs b/tracing-mock/src/span.rs
index 34568551b3..56406ed422 100644
--- a/tracing-mock/src/span.rs
+++ b/tracing-mock/src/span.rs
@@ -111,7 +111,9 @@
 //! [`expect::span`]: fn@crate::expect::span
 #![allow(missing_docs)]
 use crate::{
-    ancestry::Ancestry, collector::SpanState, expect, field::ExpectedFields,
+    ancestry::{ActualAncestry, ExpectedAncestry},
+    expect,
+    field::ExpectedFields,
     metadata::ExpectedMetadata,
 };
 use std::{
@@ -175,7 +177,7 @@ impl From<&ExpectedSpan> for ExpectedSpan {
 pub struct NewSpan {
     pub(crate) span: ExpectedSpan,
     pub(crate) fields: ExpectedFields,
-    pub(crate) ancestry: Option,
+    pub(crate) ancestry: Option,
 }
 
 pub fn named(name: I) -> ExpectedSpan
@@ -185,6 +187,36 @@ where
     expect::span().named(name)
 }
 
+pub(crate) struct ActualSpan {
+    id: tracing_core::span::Id,
+    metadata: Option<&'static tracing_core::Metadata<'static>>,
+}
+
+impl ActualSpan {
+    pub(crate) fn new(
+        id: tracing_core::span::Id,
+        metadata: Option<&'static tracing_core::Metadata<'static>>,
+    ) -> Self {
+        Self { id, metadata }
+    }
+
+    /// The Id of the actual span.
+    pub(crate) fn id(&self) -> tracing_core::span::Id {
+        self.id.clone()
+    }
+
+    /// The metadata for the actual span if it is available.
+    pub(crate) fn metadata(&self) -> Option<&'static tracing_core::Metadata<'static>> {
+        self.metadata
+    }
+}
+
+impl From<&tracing_core::span::Id> for ActualSpan {
+    fn from(id: &tracing_core::span::Id) -> Self {
+        Self::new(id.clone(), None)
+    }
+}
+
 /// A mock span ID.
 ///
 /// This ID makes it possible to link together calls to different
@@ -515,9 +547,10 @@ impl ExpectedSpan {
         }
     }
 
-    /// Configures this `ExpectedSpan` to expect the specified [`Ancestry`]. A
-    /// span's ancestry indicates whether it has a parent or is a root span
-    /// and whether the parent is explitly or contextually assigned.
+    /// Configures this `ExpectedSpan` to expect the specified
+    /// [`ExpectedAncestry`]. A span's ancestry indicates whether it has a
+    /// parent or is a root span and whether the parent is explitly or
+    /// contextually assigned.
     ///
     /// **Note**: This method returns a [`NewSpan`] and as such, this
     /// expectation can only be validated when expecting a new span via
@@ -535,9 +568,35 @@ impl ExpectedSpan {
     ///
     /// # Examples
     ///
-    /// If `expect::has_explicit_parent("parent_name")` is passed
-    /// `with_ancestry` then the provided string is the name of the explicit
-    /// parent span to expect.
+    /// An explicit or contextual parent can be matched on an `ExpectedSpan`.
+    ///
+    /// ```
+    /// use tracing_mock::{collector, expect};
+    ///
+    /// let parent = expect::span()
+    ///     .named("parent_span")
+    ///     .with_target("custom-target")
+    ///     .at_level(tracing::Level::INFO);
+    /// let span = expect::span()
+    ///     .with_ancestry(expect::has_explicit_parent(&parent));
+    ///
+    /// let (collector, handle) = collector::mock()
+    ///     .new_span(&parent)
+    ///     .new_span(span)
+    ///     .run_with_handle();
+    ///
+    /// tracing::collect::with_default(collector, || {
+    ///     let parent = tracing::info_span!(target: "custom-target", "parent_span");
+    ///     tracing::info_span!(parent: parent.id(), "span");
+    /// });
+    ///
+    /// handle.assert_finished();
+    /// ```
+    ///
+    /// The functions `expect::has_explicit_parent` and
+    /// `expect::has_contextual_parent` take `Into`, so a string
+    /// passed directly will match on a span with that name, or an
+    /// [`ExpectedId`] can be passed to match a span with that Id.
     ///
     /// ```
     /// use tracing_mock::{collector, expect};
@@ -653,7 +712,7 @@ impl ExpectedSpan {
     /// [`MockCollector::enter`]: fn@crate::collector::MockCollector::enter
     /// [`MockCollector::exit`]: fn@crate::collector::MockCollector::exit
     /// [`MockCollector::new_span`]: fn@crate::collector::MockCollector::new_span
-    pub fn with_ancestry(self, ancestry: Ancestry) -> NewSpan {
+    pub fn with_ancestry(self, ancestry: ExpectedAncestry) -> NewSpan {
         NewSpan {
             ancestry: Some(ancestry),
             span: self,
@@ -733,6 +792,10 @@ impl ExpectedSpan {
         }
     }
 
+    pub(crate) fn id(&self) -> Option<&ExpectedId> {
+        self.id.as_ref()
+    }
+
     pub(crate) fn name(&self) -> Option<&str> {
         self.metadata.name.as_ref().map(String::as_ref)
     }
@@ -745,16 +808,26 @@ impl ExpectedSpan {
         self.metadata.target.as_deref()
     }
 
-    pub(crate) fn check(&self, actual: &SpanState, collector_name: &str) {
-        let meta = actual.metadata();
-        let name = meta.name();
-
+    pub(crate) fn check(&self, actual: &ActualSpan, ctx: impl fmt::Display, collector_name: &str) {
         if let Some(expected_id) = &self.id {
-            expected_id.check(actual.id(), format_args!("span `{}`", name), collector_name);
+            expected_id.check(&actual.id(), format_args!("{ctx} a span"), collector_name);
         }
 
-        self.metadata
-            .check(meta, format_args!("span `{}`", name), collector_name);
+        match actual.metadata() {
+            Some(actual_metadata) => self.metadata.check(actual_metadata, ctx, collector_name),
+            None => {
+                if self.metadata.has_expectations() {
+                    panic!(
+                        "{}",
+                        format_args!(
+                            "[{collector_name}] expected {ctx} a span with valid metadata, \
+                            but got one with unknown Id={actual_id}",
+                            actual_id = actual.id().into_u64()
+                        )
+                    );
+                }
+            }
+        }
     }
 }
 
@@ -762,6 +835,10 @@ impl fmt::Debug for ExpectedSpan {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let mut s = f.debug_struct("MockSpan");
 
+        if let Some(id) = self.id() {
+            s.field("id", &id);
+        }
+
         if let Some(name) = self.name() {
             s.field("name", &name);
         }
@@ -801,13 +878,13 @@ where
 }
 
 impl NewSpan {
-    /// Configures this `NewSpan` to expect the specified [`Ancestry`]. A
-    /// span's ancestry indicates whether it has a parent or is a root span
+    /// Configures this `NewSpan` to expect the specified [`ExpectedAncestry`].
+    /// A span's ancestry indicates whether it has a parent or is a root span
     /// and whether the parent is explitly or contextually assigned.
     ///
     /// For more information and examples, see the documentation on
     /// [`ExpectedSpan::with_ancestry`].
-    pub fn with_ancestry(self, ancestry: Ancestry) -> NewSpan {
+    pub fn with_ancestry(self, ancestry: ExpectedAncestry) -> NewSpan {
         NewSpan {
             ancestry: Some(ancestry),
             ..self
@@ -833,14 +910,12 @@ impl NewSpan {
     pub(crate) fn check(
         &mut self,
         span: &tracing_core::span::Attributes<'_>,
-        get_ancestry: impl FnOnce() -> Ancestry,
+        get_ancestry: impl FnOnce() -> ActualAncestry,
         collector_name: &str,
     ) {
         let meta = span.metadata();
         let name = meta.name();
-        self.span
-            .metadata
-            .check(meta, format_args!("span `{}`", name), collector_name);
+        self.span.metadata.check(meta, "a new span", collector_name);
         let mut checker = self.fields.checker(name, collector_name);
         span.record(&mut checker);
         checker.finish();
@@ -902,6 +977,12 @@ impl PartialEq for ExpectedId {
 
 impl Eq for ExpectedId {}
 
+impl fmt::Debug for ExpectedId {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_tuple("ExpectedId").field(&self.inner).finish()
+    }
+}
+
 impl ExpectedId {
     const UNSET: u64 = 0;
 
@@ -921,21 +1002,33 @@ impl ExpectedId {
         Ok(())
     }
 
-    pub(crate) fn check(&self, actual: u64, ctx: fmt::Arguments<'_>, collector_name: &str) {
-        let id = self.inner.load(Ordering::Relaxed);
+    pub(crate) fn check(
+        &self,
+        actual: &tracing_core::span::Id,
+        ctx: fmt::Arguments<'_>,
+        collector_name: &str,
+    ) {
+        let expected_id = self.inner.load(Ordering::Relaxed);
+        let actual_id = actual.into_u64();
 
         assert!(
-            id != Self::UNSET,
-            "\n[{}] expected {} to have expected ID set, but it hasn't been, \
-            perhaps this `ExpectedId` wasn't used in a call to `MockCollector::new_span()`?",
-            collector_name,
-            ctx,
+            expected_id != Self::UNSET,
+            "{}",
+            format!(
+                "\n[{collector_name}] expected {ctx} with an expected Id set,\n\
+                [{collector_name}] but it hasn't been, perhaps this `ExpectedId` \
+                wasn't used in a call to `new_span()`?"
+            )
         );
 
         assert_eq!(
-            id, actual,
-            "\n[{}] expected {} to have ID `{}`, but it has `{}` instead",
-            collector_name, ctx, id, actual,
+            expected_id,
+            actual_id,
+            "{}",
+            format_args!(
+                "\n[{collector_name}] expected {ctx} with Id `{expected_id}`,\n\
+                [{collector_name}] but got one with Id `{actual_id}` instead",
+            )
         );
     }
 }
diff --git a/tracing-mock/src/subscriber.rs b/tracing-mock/src/subscriber.rs
index 16ed3a59f8..33cab2bf21 100644
--- a/tracing-mock/src/subscriber.rs
+++ b/tracing-mock/src/subscriber.rs
@@ -116,11 +116,11 @@
 //!
 //! [`Subscribe`]: trait@tracing_subscriber::subscribe::Subscribe
 use crate::{
-    ancestry::{get_ancestry, Ancestry, HasAncestry},
+    ancestry::{get_ancestry, ActualAncestry, HasAncestry},
     collector::MockHandle,
     event::ExpectedEvent,
     expect::Expect,
-    span::{ExpectedSpan, NewSpan},
+    span::{ActualSpan, ExpectedSpan, NewSpan},
 };
 use tracing_core::{
     span::{Attributes, Id, Record},
@@ -779,66 +779,16 @@ impl MockSubscriberBuilder {
     }
 }
 
-impl MockSubscriber {
-    fn check_span_ref<'spans, S>(
-        &self,
-        expected: &ExpectedSpan,
-        actual: &SpanRef<'spans, S>,
-        what_happened: impl fmt::Display,
-    ) where
-        S: LookupSpan<'spans>,
-    {
-        if let Some(exp_name) = expected.name() {
-            assert_eq!(
-                actual.name(),
-                exp_name,
-                "\n[{}] expected {} a span named {:?}\n\
-                 [{}] but it was named {:?} instead (span {} {:?})",
-                self.name,
-                what_happened,
-                exp_name,
-                self.name,
-                actual.name(),
-                actual.name(),
-                actual.id()
-            );
-        }
-
-        if let Some(exp_level) = expected.level() {
-            let actual_level = actual.metadata().level();
-            assert_eq!(
-                actual_level,
-                &exp_level,
-                "\n[{}] expected {} a span at {:?}\n\
-                 [{}] but it was at {:?} instead (span {} {:?})",
-                self.name,
-                what_happened,
-                exp_level,
-                self.name,
-                actual_level,
-                actual.name(),
-                actual.id(),
-            );
-        }
-
-        if let Some(exp_target) = expected.target() {
-            let actual_target = actual.metadata().target();
-            assert_eq!(
-                actual_target,
-                exp_target,
-                "\n[{}] expected {} a span with target {:?}\n\
-                 [{}] but it had the target {:?} instead (span {} {:?})",
-                self.name,
-                what_happened,
-                exp_target,
-                self.name,
-                actual_target,
-                actual.name(),
-                actual.id(),
-            );
-        }
+impl<'a, S> From<&SpanRef<'a, S>> for ActualSpan
+where
+    S: LookupSpan<'a>,
+{
+    fn from(span_ref: &SpanRef<'a, S>) -> Self {
+        Self::new(span_ref.id(), Some(span_ref.metadata()))
     }
+}
 
+impl MockSubscriber {
     fn check_event_scope(
         &self,
         current_scope: Option>,
@@ -857,10 +807,10 @@ impl MockSubscriber {
                 actual.id(),
                 expected
             );
-            self.check_span_ref(
-                expected,
-                &actual,
+            expected.check(
+                &(&actual).into(),
                 format_args!("the {}th span in the event's scope to be", i),
+                &self.name,
             );
             i += 1;
         }
@@ -956,7 +906,7 @@ where
         match self.expected.lock().unwrap().pop_front() {
             None => {}
             Some(Expect::Enter(ref expected_span)) => {
-                self.check_span_ref(expected_span, &span, "to enter");
+                expected_span.check(&(&span).into(), "to enter", &self.name);
             }
             Some(ex) => ex.bad(&self.name, format_args!("entered span {:?}", span.name())),
         }
@@ -977,7 +927,7 @@ where
         match self.expected.lock().unwrap().pop_front() {
             None => {}
             Some(Expect::Exit(ref expected_span)) => {
-                self.check_span_ref(expected_span, &span, "to exit");
+                expected_span.check(&(&span).into(), "to exit", &self.name);
                 let curr = self.current.lock().unwrap().pop();
                 assert_eq!(
                     Some(id),
@@ -1014,7 +964,7 @@ where
                     // as failing the assertion can cause a double panic.
                     if !::std::thread::panicking() {
                         if let Some(ref span) = span {
-                            self.check_span_ref(expected_span, span, "to close");
+                            expected_span.check(&span.into(), "to close a span", &self.name);
                         }
                     }
                     true
@@ -1043,14 +993,14 @@ where
     }
 }
 
-fn context_get_ancestry(item: impl HasAncestry, ctx: &Context<'_, C>) -> Ancestry
+fn context_get_ancestry(item: impl HasAncestry, ctx: &Context<'_, C>) -> ActualAncestry
 where
     C: Collect + for<'a> LookupSpan<'a>,
 {
     get_ancestry(
         item,
         || ctx.lookup_current().map(|s| s.id()),
-        |span_id| ctx.span(span_id).map(|span| span.name()),
+        |span_id| ctx.span(span_id).map(|span| (&span).into()),
     )
 }
 
diff --git a/tracing-mock/tests/event_ancestry.rs b/tracing-mock/tests/event_ancestry.rs
index 6bd253d016..0d9db87588 100644
--- a/tracing-mock/tests/event_ancestry.rs
+++ b/tracing-mock/tests/event_ancestry.rs
@@ -5,7 +5,7 @@
 //! that an event has a specific contextual or explicit parent.
 //!
 //! [`ExpectedEvent`]: crate::event::ExpectedEvent
-use tracing::collect::with_default;
+use tracing::{collect::with_default, Level};
 use tracing_mock::{collector, expect};
 
 #[test]
@@ -27,8 +27,8 @@ fn contextual_parent() {
 
 #[test]
 #[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but \
-    actually has a contextual parent with name='another parent'"
+    expected = "to have a contextual parent span named `contextual parent`,\n\
+    [contextual_parent_wrong_name] but got one named `another parent` instead."
 )]
 fn contextual_parent_wrong_name() {
     let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent"));
@@ -46,11 +46,53 @@ fn contextual_parent_wrong_name() {
     handle.assert_finished();
 }
 
+#[test]
+#[should_panic(expected = "to have a contextual parent span a span with Id `1`,\n\
+    [contextual_parent_wrong_id] but got one with Id `2` instead")]
+fn contextual_parent_wrong_id() {
+    let id = expect::id();
+    let event = expect::event().with_ancestry(expect::has_contextual_parent(&id));
+
+    let (collector, handle) = collector::mock()
+        .new_span(&id)
+        .enter(expect::span())
+        .event(event)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _span = tracing::info_span!("contextual parent");
+        let _guard = tracing::info_span!("another parent").entered();
+        tracing::info!(field = &"value");
+    });
+
+    handle.assert_finished();
+}
+
 #[test]
 #[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but was actually a \
-    contextual root"
+    expected = "to have a contextual parent span at level `Level(Info)`,\n\
+    [contextual_parent_wrong_level] but got one at level `Level(Debug)` instead."
 )]
+fn contextual_parent_wrong_level() {
+    let parent = expect::span().at_level(Level::INFO);
+    let event = expect::event().with_ancestry(expect::has_contextual_parent(parent));
+
+    let (collector, handle) = collector::mock()
+        .enter(expect::span())
+        .event(event)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _guard = tracing::debug_span!("contextual parent").entered();
+        tracing::info!(field = &"value");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have a contextual parent span, but it is actually a \
+    contextual root")]
 fn expect_contextual_parent_actual_contextual_root() {
     let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent"));
 
@@ -64,10 +106,8 @@ fn expect_contextual_parent_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but actually has an \
-    explicit parent with name='explicit parent'"
-)]
+#[should_panic(expected = "to have a contextual parent span, but it actually has an \
+    explicit parent span")]
 fn expect_contextual_parent_actual_explicit_parent() {
     let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent"));
 
@@ -82,10 +122,8 @@ fn expect_contextual_parent_actual_explicit_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but was actually an \
-    explicit root"
-)]
+#[should_panic(expected = "to have a contextual parent span, but it is actually an \
+    explicit root")]
 fn expect_contextual_parent_actual_explicit_root() {
     let event = expect::event().with_ancestry(expect::has_contextual_parent("contextual parent"));
 
@@ -116,10 +154,7 @@ fn contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be a contextual root, but actually has a contextual parent with \
-    name='contextual parent'"
-)]
+#[should_panic(expected = "to be a contextual root, but it actually has a contextual parent span")]
 fn expect_contextual_root_actual_contextual_parent() {
     let event = expect::event().with_ancestry(expect::is_contextual_root());
 
@@ -137,10 +172,7 @@ fn expect_contextual_root_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be a contextual root, but actually has an explicit parent with \
-    name='explicit parent'"
-)]
+#[should_panic(expected = "to be a contextual root, but it actually has an explicit parent span")]
 fn expect_contextual_root_actual_explicit_parent() {
     let event = expect::event().with_ancestry(expect::is_contextual_root());
 
@@ -155,7 +187,7 @@ fn expect_contextual_root_actual_explicit_parent() {
 }
 
 #[test]
-#[should_panic(expected = "to be a contextual root, but was actually an explicit root")]
+#[should_panic(expected = "to be a contextual root, but it is actually an explicit root")]
 fn expect_contextual_root_actual_explicit_root() {
     let event = expect::event().with_ancestry(expect::is_contextual_root());
 
@@ -188,8 +220,8 @@ fn explicit_parent() {
 
 #[test]
 #[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but actually has an \
-    explicit parent with name='another parent'"
+    expected = "to have an explicit parent span named `explicit parent`,\n\
+    [explicit_parent_wrong_name] but got one named `another parent` instead."
 )]
 fn explicit_parent_wrong_name() {
     let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent"));
@@ -205,10 +237,47 @@ fn explicit_parent_wrong_name() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but actually has a \
-    contextual parent with name='contextual parent'"
-)]
+#[should_panic(expected = "to have an explicit parent span a span with Id `1`,\n\
+    [explicit_parent_wrong_id] but got one with Id `2` instead")]
+fn explicit_parent_wrong_id() {
+    let id = expect::id();
+    let event = expect::event().with_ancestry(expect::has_explicit_parent(&id));
+
+    let (collector, handle) = collector::mock()
+        .new_span(&id)
+        .new_span(expect::span())
+        .event(event)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _span = tracing::info_span!("explicit parent");
+        let another_span = tracing::info_span!("another parent");
+        tracing::info!(parent: another_span.id(), field = &"value");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have an explicit parent span at level `Level(Info)`,\n\
+    [explicit_parent_wrong_level] but got one at level `Level(Debug)` instead.")]
+fn explicit_parent_wrong_level() {
+    let parent = expect::span().at_level(Level::INFO);
+    let event = expect::event().with_ancestry(expect::has_explicit_parent(parent));
+
+    let (collector, handle) = collector::mock().event(event).run_with_handle();
+
+    with_default(collector, || {
+        let span = tracing::debug_span!("explicit parent");
+        tracing::info!(parent: span.id(), field = &"value");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have an explicit parent span, but it actually has a \
+    contextual parent span")]
 fn expect_explicit_parent_actual_contextual_parent() {
     let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent"));
 
@@ -226,10 +295,8 @@ fn expect_explicit_parent_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but was actually a \
-    contextual root"
-)]
+#[should_panic(expected = "to have an explicit parent span, but it is actually a \
+    contextual root")]
 fn expect_explicit_parent_actual_contextual_root() {
     let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent"));
 
@@ -243,10 +310,8 @@ fn expect_explicit_parent_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but was actually an \
-    explicit root"
-)]
+#[should_panic(expected = "to have an explicit parent span, but it is actually an \
+    explicit root")]
 fn expect_explicit_parent_actual_explicit_root() {
     let event = expect::event().with_ancestry(expect::has_explicit_parent("explicit parent"));
 
@@ -281,10 +346,7 @@ fn explicit_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be an explicit root, but actually has a contextual parent with \
-    name='contextual parent'"
-)]
+#[should_panic(expected = "to be an explicit root, but it actually has a contextual parent span")]
 fn expect_explicit_root_actual_contextual_parent() {
     let event = expect::event().with_ancestry(expect::is_explicit_root());
 
@@ -302,7 +364,7 @@ fn expect_explicit_root_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(expected = "to be an explicit root, but was actually a contextual root")]
+#[should_panic(expected = "to be an explicit root, but it is actually a contextual root")]
 fn expect_explicit_root_actual_contextual_root() {
     let event = expect::event().with_ancestry(expect::is_explicit_root());
 
@@ -316,9 +378,7 @@ fn expect_explicit_root_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be an explicit root, but actually has an explicit parent with name='explicit parent'"
-)]
+#[should_panic(expected = "to be an explicit root, but it actually has an explicit parent span")]
 fn expect_explicit_root_actual_explicit_parent() {
     let event = expect::event().with_ancestry(expect::is_explicit_root());
 
diff --git a/tracing-mock/tests/span_ancestry.rs b/tracing-mock/tests/span_ancestry.rs
index 603c0a6b3c..0136e7cd14 100644
--- a/tracing-mock/tests/span_ancestry.rs
+++ b/tracing-mock/tests/span_ancestry.rs
@@ -6,7 +6,7 @@
 //!
 //! [`ExpectedSpan`]: crate::span::ExpectedSpan
 //!
-use tracing::collect::with_default;
+use tracing::{collect::with_default, Level};
 use tracing_mock::{collector, expect};
 
 #[test]
@@ -30,8 +30,8 @@ fn contextual_parent() {
 
 #[test]
 #[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but \
-    actually has a contextual parent with name='another parent'"
+    expected = "to have a contextual parent span named `contextual parent`,\n\
+    [contextual_parent_wrong_name] but got one named `another parent` instead."
 )]
 fn contextual_parent_wrong_name() {
     let span = expect::span()
@@ -51,11 +51,58 @@ fn contextual_parent_wrong_name() {
     handle.assert_finished();
 }
 
+#[test]
+#[should_panic(expected = "to have a contextual parent span a span with Id `1`,\n\
+    [contextual_parent_wrong_id] but got one with Id `2` instead")]
+fn contextual_parent_wrong_id() {
+    let id = expect::id();
+    let span = expect::span()
+        .named("span")
+        .with_ancestry(expect::has_contextual_parent(&id));
+
+    let (collector, handle) = collector::mock()
+        .new_span(&id)
+        .new_span(expect::span())
+        .enter(expect::span())
+        .new_span(span)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _span = tracing::info_span!("contextual parent");
+        let _guard = tracing::info_span!("another parent").entered();
+        tracing::info_span!("span");
+    });
+
+    handle.assert_finished();
+}
+
 #[test]
 #[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but was actually a \
-    contextual root"
+    expected = "to have a contextual parent span at level `Level(Info)`,\n\
+    [contextual_parent_wrong_level] but got one at level `Level(Debug)` instead."
 )]
+fn contextual_parent_wrong_level() {
+    let parent = expect::span().at_level(Level::INFO);
+    let span = expect::span()
+        .named("span")
+        .with_ancestry(expect::has_contextual_parent(parent));
+
+    let (collector, handle) = collector::mock()
+        .enter(expect::span())
+        .new_span(span)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _guard = tracing::debug_span!("contextual parent").entered();
+        tracing::info_span!("span");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have a contextual parent span, but it is actually a \
+    contextual root")]
 fn expect_contextual_parent_actual_contextual_root() {
     let span = expect::span()
         .named("span")
@@ -71,10 +118,8 @@ fn expect_contextual_parent_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but actually has an \
-    explicit parent with name='explicit parent'"
-)]
+#[should_panic(expected = "to have a contextual parent span, but it actually has an \
+    explicit parent span")]
 fn expect_contextual_parent_actual_explicit_parent() {
     let span = expect::span()
         .named("span")
@@ -94,10 +139,8 @@ fn expect_contextual_parent_actual_explicit_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have a contextual parent with name='contextual parent', but was actually an \
-    explicit root"
-)]
+#[should_panic(expected = "to have a contextual parent span, but it is actually an \
+    explicit root")]
 fn expect_contextual_parent_actual_explicit_root() {
     let span = expect::span()
         .named("span")
@@ -132,10 +175,7 @@ fn contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be a contextual root, but actually has a contextual parent with \
-    name='contextual parent'"
-)]
+#[should_panic(expected = "to be a contextual root, but it actually has a contextual parent span")]
 fn expect_contextual_root_actual_contextual_parent() {
     let span = expect::span()
         .named("span")
@@ -155,10 +195,7 @@ fn expect_contextual_root_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be a contextual root, but actually has an explicit parent with \
-    name='explicit parent'"
-)]
+#[should_panic(expected = "to be a contextual root, but it actually has an explicit parent span")]
 fn expect_contextual_root_actual_explicit_parent() {
     let span = expect::span()
         .named("span")
@@ -178,7 +215,7 @@ fn expect_contextual_root_actual_explicit_parent() {
 }
 
 #[test]
-#[should_panic(expected = "to be a contextual root, but was actually an explicit root")]
+#[should_panic(expected = "to be a contextual root, but it is actually an explicit root")]
 fn expect_contextual_root_actual_explicit_root() {
     let span = expect::span()
         .named("span")
@@ -218,8 +255,8 @@ fn explicit_parent() {
 
 #[test]
 #[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but actually has an \
-    explicit parent with name='another parent'"
+    expected = "to have an explicit parent span named `explicit parent`,\n\
+    [explicit_parent_wrong_name] but got one named `another parent` instead."
 )]
 fn explicit_parent_wrong_name() {
     let span = expect::span()
@@ -240,10 +277,54 @@ fn explicit_parent_wrong_name() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but actually has a \
-    contextual parent with name='contextual parent'"
-)]
+#[should_panic(expected = "to have an explicit parent span a span with Id `1`,\n\
+    [explicit_parent_wrong_id] but got one with Id `2` instead")]
+fn explicit_parent_wrong_id() {
+    let id = expect::id();
+    let span = expect::span()
+        .named("span")
+        .with_ancestry(expect::has_explicit_parent(&id));
+
+    let (collector, handle) = collector::mock()
+        .new_span(&id)
+        .new_span(expect::span())
+        .new_span(span)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let _span = tracing::info_span!("explicit parent");
+        let another_span = tracing::info_span!("another parent");
+        tracing::info_span!(parent: another_span.id(), "span");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have an explicit parent span at level `Level(Info)`,\n\
+    [explicit_parent_wrong_level] but got one at level `Level(Debug)` instead.")]
+fn explicit_parent_wrong_level() {
+    let parent = expect::span().at_level(Level::INFO);
+    let span = expect::span()
+        .named("span")
+        .with_ancestry(expect::has_explicit_parent(parent));
+
+    let (collector, handle) = collector::mock()
+        .new_span(expect::span())
+        .new_span(span)
+        .run_with_handle();
+
+    with_default(collector, || {
+        let span = tracing::debug_span!("explicit parent");
+        tracing::info_span!(parent: span.id(), "span");
+    });
+
+    handle.assert_finished();
+}
+
+#[test]
+#[should_panic(expected = "to have an explicit parent span, but it actually has a \
+    contextual parent span")]
 fn expect_explicit_parent_actual_contextual_parent() {
     let span = expect::span()
         .named("span")
@@ -263,10 +344,8 @@ fn expect_explicit_parent_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but was actually a \
-    contextual root"
-)]
+#[should_panic(expected = "to have an explicit parent span, but it is actually a \
+    contextual root")]
 fn expect_explicit_parent_actual_contextual_root() {
     let span = expect::span()
         .named("span")
@@ -282,10 +361,8 @@ fn expect_explicit_parent_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to have an explicit parent with name='explicit parent', but was actually an \
-    explicit root"
-)]
+#[should_panic(expected = "to have an explicit parent span, but it is actually an \
+    explicit root")]
 fn expect_explicit_parent_actual_explicit_root() {
     let span = expect::span()
         .named("span")
@@ -325,10 +402,7 @@ fn explicit_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be an explicit root, but actually has a contextual parent with \
-    name='contextual parent'"
-)]
+#[should_panic(expected = "to be an explicit root, but it actually has a contextual parent span")]
 fn expect_explicit_root_actual_contextual_parent() {
     let span = expect::span()
         .named("span")
@@ -348,7 +422,7 @@ fn expect_explicit_root_actual_contextual_parent() {
 }
 
 #[test]
-#[should_panic(expected = "to be an explicit root, but was actually a contextual root")]
+#[should_panic(expected = "to be an explicit root, but it is actually a contextual root")]
 fn expect_explicit_root_actual_contextual_root() {
     let span = expect::span()
         .named("span")
@@ -364,9 +438,7 @@ fn expect_explicit_root_actual_contextual_root() {
 }
 
 #[test]
-#[should_panic(
-    expected = "to be an explicit root, but actually has an explicit parent with name='explicit parent'"
-)]
+#[should_panic(expected = "to be an explicit root, but it actually has an explicit parent span")]
 fn expect_explicit_root_actual_explicit_parent() {
     let span = expect::span()
         .named("span")