tracing-subscriber-0.3.18/.cargo_vcs_info.json0000644000000001600000000000100147120ustar { "git": { "sha1": "8b7a1dde69797b33ecfa20da71e72eb5e61f0b25" }, "path_in_vcs": "tracing-subscriber" }tracing-subscriber-0.3.18/CHANGELOG.md000064400000000000000000001423531046102023000153260ustar 00000000000000# 0.3.18 (November 13, 2023) This release of `tracing-subscriber` adds support for the [`NO_COLOR`] environment variable (an informal standard to disable emitting ANSI color escape codes) in `fmt::Layer`, reintroduces support for the [`chrono`] crate, and increases the minimum supported Rust version (MSRV) to Rust 1.63.0. It also introduces several minor API improvements. ### Added - **chrono**: Add [`chrono`] implementations of `FormatTime` ([#2690]) - **subscriber**: Add support for the [`NO_COLOR`] environment variable in `fmt::Layer` ([#2647]) - **fmt**: make `format::Writer::new()` public ([#2680]) - **filter**: Implement `layer::Filter` for `Option` ([#2407]) ### Changed - **log**: bump version of `tracing-log` to 0.2 ([#2772]) - Increased minimum supported Rust version (MSRV) to 1.63.0+. [`chrono`]: https://github.com/chronotope/chrono [`NO_COLOR`]: https://no-color.org/ [#2690]: https://github.com/tokio-rs/tracing/pull/2690 [#2647]: https://github.com/tokio-rs/tracing/pull/2647 [#2680]: https://github.com/tokio-rs/tracing/pull/2680 [#2407]: https://github.com/tokio-rs/tracing/pull/2407 [#2772]: https://github.com/tokio-rs/tracing/pull/2772 Thanks to @shayne-fletcher, @dmlary, @kaifastromai, and @jsgf for contributing! # 0.3.17 (April 21, 2023) This release of `tracing-subscriber` fixes a build error when using `env-filter` with recent versions of the `regex` crate. It also introduces several minor API improvements. ### Fixed - **env-filter**: Add "unicode-case" and "unicode-perl" to the `regex` dependency, fixing a build error with recent versions of `regex` ([#2566]) - A number of minor documentation typos and other fixes ([#2384], [#2378], [#2368], [#2548]) ### Added - **filter**: Add `fmt::Display` impl for `filter::Targets` ([#2343]) - **fmt**: Made `with_ansi(false)` no longer require the "ansi" feature, so that ANSI formatting escapes can be disabled without requiring ANSI-specific dependencies ([#2532]) ### Changed - **fmt**: Dim targets in the `Compact` formatter, matching the default formatter ([#2409]) Thanks to @keepsimple1, @andrewhalle, @LeoniePhiline, @LukeMathWalker, @howardjohn, @daxpedda, and @dbidwell94 for contributing to this release! [#2566]: https://github.com/tokio-rs/tracing/pull/2566 [#2384]: https://github.com/tokio-rs/tracing/pull/2384 [#2378]: https://github.com/tokio-rs/tracing/pull/2378 [#2368]: https://github.com/tokio-rs/tracing/pull/2368 [#2548]: https://github.com/tokio-rs/tracing/pull/2548 [#2343]: https://github.com/tokio-rs/tracing/pull/2343 [#2532]: https://github.com/tokio-rs/tracing/pull/2532 [#2409]: https://github.com/tokio-rs/tracing/pull/2409 # 0.3.16 (October 6, 2022) This release of `tracing-subscriber` fixes a regression introduced in [v0.3.15][subscriber-0.3.15] where `Option::None`'s `Layer` implementation would set the max level hint to `OFF`. In addition, it adds several new APIs, including the `Filter::event_enabled` method for filtering events based on fields values, and the ability to log internal errors that occur when writing a log line. This release also replaces the dependency on the unmaintained [`ansi-term`] crate with the [`nu-ansi-term`] crate, resolving an *informational* security advisory ([RUSTSEC-2021-0139]) for [`ansi-term`]'s maintainance status. This increases the minimum supported Rust version (MSRV) to Rust 1.50+, although the crate should still compile for the previous MSRV of Rust 1.49+ when the `ansi` feature is not enabled. ### Fixed - **layer**: `Option::None`'s `Layer` impl always setting the `max_level_hint` to `LevelFilter::OFF` ([#2321]) - Compilation with `-Z minimal versions` ([#2246]) - **env-filter**: Clarify that disabled level warnings are emitted by `tracing-subscriber` ([#2285]) ### Added - **fmt**: Log internal errors to `stderr` if writing a log line fails ([#2102]) - **fmt**: `FmtLayer::log_internal_errors` and `FmtSubscriber::log_internal_errors` methods for configuring whether internal writer errors are printed to `stderr` ([#2102]) - **fmt**: `#[must_use]` attributes on builders to warn if a `Subscriber` is configured but not set as the default subscriber ([#2239]) - **filter**: `Filter::event_enabled` method for filtering an event based on its fields ([#2245], [#2251]) - **filter**: `Targets::default_level` accessor ([#2242]) ### Changed - **ansi**: Replaced dependency on unmaintained `ansi-term` crate with `nu-ansi-term` (([#2287], fixes informational advisory [RUSTSEC-2021-0139]) - `tracing-core`: updated to [0.1.30][core-0.1.30] - Minimum Supported Rust Version (MSRV) increased to Rust 1.50+ (when the `ansi`) feature flag is enabled ([#2287]) ### Documented - **fmt**: Correct inaccuracies in `fmt::init` documentation ([#2224]) - **filter**: Fix incorrect doc link in `filter::Not` combinator ([#2249]) Thanks to new contributors @cgbur, @DesmondWillowbrook, @RalfJung, and @poliorcetics, as well as returning contributors @CAD97, @connec, @jswrenn, @guswynn, and @bryangarza, for contributing to this release! [nu-ansi-term]: https://github.com/nushell/nu-ansi-term [ansi_term]: https://github.com/ogham/rust-ansi-term [RUSTSEC-2021-0139]: https://rustsec.org/advisories/RUSTSEC-2021-0139.html [core-0.1.30]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.30 [subscriber-0.3.15]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.15 [#2321]: https://github.com/tokio-rs/tracing/pull/2321 [#2246]: https://github.com/tokio-rs/tracing/pull/2246 [#2285]: https://github.com/tokio-rs/tracing/pull/2285 [#2102]: https://github.com/tokio-rs/tracing/pull/2102 [#2239]: https://github.com/tokio-rs/tracing/pull/2239 [#2245]: https://github.com/tokio-rs/tracing/pull/2245 [#2251]: https://github.com/tokio-rs/tracing/pull/2251 [#2287]: https://github.com/tokio-rs/tracing/pull/2287 [#2224]: https://github.com/tokio-rs/tracing/pull/2224 [#2249]: https://github.com/tokio-rs/tracing/pull/2249 # 0.3.15 (Jul 20, 2022) This release fixes a bug where the `reload` layer would fail to pass through `max_level_hint` to the underlying layer, potentially breaking filtering. ### Fixed - **reload**: pass through `max_level_hint` to the inner `Layer` ([#2204]) Thanks to @guswynn for contributing to this release! [#2204]: https://github.com/tokio-rs/tracing/pull/2204 # 0.3.14 (Jul 1, 2022) This release fixes multiple filtering bugs in the `Layer` implementations for `Option` and `Vec`. ### Fixed - **layer**: `Layer::event_enabled` implementation for `Option>` returning `false` when the `Option` is `None`, disabling all events globally ([#2193]) - **layer**: `Layer::max_level_hint` implementation for `Option>` incorrectly disabling max level filtering when the option is `None` ([#2195]) - **layer**: `Layer::max_level_hint` implementation for `Vec>` returning `LevelFilter::ERROR` rather than `LevelFilter::OFF` when the `Vec` is empty ([#2195]) Thanks to @CAD97 and @guswynn for contributing to this release! [#2193]: https://github.com/tokio-rs/tracing/pull/2193 [#2195]: https://github.com/tokio-rs/tracing/pull/2195 # 0.3.13 (Jun 30, 2022) (YANKED) This release of `tracing-subscriber` fixes a compilation failure due to an incorrect `tracing-core` dependency that was introduced in v0.3.12. ### Changed - **tracing_core**: Updated minimum dependency version to 0.1.28 ([#2190]) [#2190]: https://github.com/tokio-rs/tracing/pull/2190 # 0.3.12 (Jun 29, 2022) (YANKED) This release of `tracing-subscriber` adds a new `Layer::event_enabled` method, which allows `Layer`s to filter events *after* their field values are recorded; a `Filter` implementation for `reload::Layer`, to make using `reload` with per-layer filtering more ergonomic, and additional inherent method downcasting APIs for the `Layered` type. In addition, it includes dependency updates, and minor fixes for documentation and feature flagging. ### Added - **layer**: `Layer::event_enabled` method, which can be implemented to filter events based on their field values ([#2008]) - **reload**: `Filter` implementation for `reload::Layer` ([#2159]) - **layer**: `Layered::downcast_ref` and `Layered::is` inherent methods ([#2160]) ### Changed - **parking_lot**: Updated dependency on `parking_lot` to 0.13.0 ([#2143]) - Replaced `lazy_static` dependency with `once_cell` ([#2147]) ### Fixed - Don't enable `tracing-core` features by default ([#2107]) - Several documentation link and typo fixes ([#2064], [#2068], #[2077], [#2161], [#1088]) Thanks to @ben0x539, @jamesmunns, @georgemp, @james7132, @jswrenn, @CAD97, and @guswynn for contributing to this release! [#2008]: https://github.com/tokio-rs/tracing/pull/2008 [#2159]: https://github.com/tokio-rs/tracing/pull/2159 [#2160]: https://github.com/tokio-rs/tracing/pull/2160 [#2143]: https://github.com/tokio-rs/tracing/pull/2143 [#2107]: https://github.com/tokio-rs/tracing/pull/2107 [#2064]: https://github.com/tokio-rs/tracing/pull/2064 [#2068]: https://github.com/tokio-rs/tracing/pull/2068 [#2077]: https://github.com/tokio-rs/tracing/pull/2077 [#2161]: https://github.com/tokio-rs/tracing/pull/2161 [#1088]: https://github.com/tokio-rs/tracing/pull/1088 # 0.3.11 (Apr 9, 2022) This is a bugfix release for the `Filter` implementation for `EnvFilter` added in [v0.3.10]. ### Fixed - **env-filter**: Added missing `Filter::on_record` callback to `EnvFilter`'s `Filter` impl ([#2058]) - **env-filter**: Fixed method resolution issues when calling `EnvFilter` methods with both the `Filter` and `Layer` traits in scope ([#2057]) - **env-filter**: Fixed `EnvFilter::builder().parse()` and other parsing methods returning an error when parsing an empty string ([#2052]) Thanks to new contributor @Ma124 for contributing to this release! [v0.3.10]: https://github.com/tokio-rs/tracing/releases/tag/tracing-subscriber-0.3.10 [#2058]: https://github.com/tokio-rs/tracing/pull/2058 [#2057]: https://github.com/tokio-rs/tracing/pull/2057 [#2052]: https://github.com/tokio-rs/tracing/pull/2052 # 0.3.10 (Apr 1, 2022) This release adds several new features, including a `Filter` implementation and new builder API for `EnvFilter`, support for using a `Vec where L: Layer` as a `Layer`, and a number of smaller API improvements to make working with dynamic and reloadable layers easier. ### Added - **registry**: Implement `Filter` for `EnvFilter`, allowing it to be used with per-layer filtering ([#1983]) - **registry**: `Filter::on_new_span`, `Filter::on_enter`, `Filter::on_exit`, `Filter::on_close` and `Filter::on_record` callbacks to allow `Filter`s to track span states internally ([#1973], [#2017], [#2031]) - **registry**: `Filtered::filter` and `Filtered::filter_mut` accessors ([#1959]) - **registry**: `Filtered::inner` and `Filtered::inner_mut` accessors to borrow the wrapped `Layer` ([#2034]) - **layer**: Implement `Layer` for `Vec`, to allow composing together a dynamically sized list of `Layer`s ([#2027]) - **layer**: `Layer::boxed` method to make type-erasing `Layer`s easier ([#2026]) - **fmt**: `fmt::Layer::writer` and `fmt::Layer::writer_mut` accessors ([#2034]) - **fmt**: `fmt::Layer::set_ansi` method to allow changing the ANSI formatting configuration at runtime ([#2034]) - **env-filter**: `EnvFilter::builder` to configure a new `EnvFilter` prior to parsing it ([#2035]) - Several documentation fixes and improvements ([#1972], [#1971], [#2023], [#2023]) ### Fixed - **fmt**: `fmt::Layer`'s auto traits no longer depend on the `Subscriber` type parameter's auto traits ([#2025]) - **env-filter**: Fixed missing help text when the `ansi` feature is disabled ([#2029]) Thanks to new contributors @TimoFreiberg and @wagenet, as well as @CAD97 for contributing to this release! [#1983]: https://github.com/tokio-rs/tracing/pull/1983 [#1973]: https://github.com/tokio-rs/tracing/pull/1973 [#2017]: https://github.com/tokio-rs/tracing/pull/2017 [#2031]: https://github.com/tokio-rs/tracing/pull/2031 [#1959]: https://github.com/tokio-rs/tracing/pull/1959 [#2034]: https://github.com/tokio-rs/tracing/pull/2034 [#2027]: https://github.com/tokio-rs/tracing/pull/2027 [#2026]: https://github.com/tokio-rs/tracing/pull/2026 [#2035]: https://github.com/tokio-rs/tracing/pull/2035 [#1972]: https://github.com/tokio-rs/tracing/pull/1972 [#1971]: https://github.com/tokio-rs/tracing/pull/1971 [#2023]: https://github.com/tokio-rs/tracing/pull/2023 [#2025]: https://github.com/tokio-rs/tracing/pull/2025 [#2029]: https://github.com/tokio-rs/tracing/pull/2029 # 0.3.9 (Feb 17, 2022) This release updates the minimum supported Rust version (MSRV) to 1.49.0, and updates the (optional) dependency on `parking_lot` to v0.12. ### Changed - Updated minimum supported Rust version (MSRV) to 1.49.0 ([#1913]) - `parking_lot`: updated to v0.12 ([008339d]) ### Added - **fmt**: Documentation improvements ([#1926], [#1927]) [#1913]: https://github.com/tokio-rs/tracing/pull/1913 [#1926]: https://github.com/tokio-rs/tracing/pull/1926 [#1927]: https://github.com/tokio-rs/tracing/pull/1927 [008339d]: https://github.com/tokio-rs/tracing/commit/008339d1e8750ffe7b4634fc7789bda0c522424f # 0.3.8 (Feb 4, 2022) This release adds *experimental* support for recording structured field values using the [`valuable`] crate to the `format::Json` formatter. In particular, user-defined types which are recorded using their [`valuable::Valuable`] implementations will be serialized as JSON objects, rather than using their `fmt::Debug` representation. See [this blog post][post] for details on `valuable`. Note that `valuable` support currently requires `--cfg tracing_unstable`. See the documentation for details. Additionally, this release includes a number of other smaller API improvements. ### Added - **json**: Experimental support for recording [`valuable`] values as structured JSON ([#1862], [#1901]) - **filter**: `Targets::would_enable` method for testing if a `Targets` filter would enable a given target ([#1903]) - **fmt**: `map_event_format`, `map_fmt_fields`, and `map_writer` methods to `fmt::Layer` and `fmt::SubscriberBuilder` ([#1871]) ### Changed - `tracing-core`: updated to [0.1.22][core-0.1.22] ### Fixed - Set `smallvec` minimal version to 1.2.0, to fix compilation errors with `-Z minimal-versions` ([#1890]) - Minor documentation fixes ([#1902], [#1893]) Thanks to @guswynn, @glts, and @lilyball for contributing to this release! [`valuable`]: https://crates.io/crates/valuable [`valuable::Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html [post]: https://tokio.rs/blog/2021-05-valuable [core-0.1.22]: https://github.com/tokio-rs/tracing/releases/tag/tracing-core-0.1.22 [#1862]: https://github.com/tokio-rs/tracing/pull/1862 [#1901]: https://github.com/tokio-rs/tracing/pull/1901 [#1903]: https://github.com/tokio-rs/tracing/pull/1903 [#1871]: https://github.com/tokio-rs/tracing/pull/1871 [#1890]: https://github.com/tokio-rs/tracing/pull/1890 [#1902]: https://github.com/tokio-rs/tracing/pull/1902 [#1893]: https://github.com/tokio-rs/tracing/pull/1893 # 0.3.7 (Jan 25, 2022) This release adds combinators for combining filters. Additionally, this release also updates the `thread-local` crate to v1.1.4, fixing warnings for the security advisory [RUSTSEC-2022-0006]. Note that previous versions of `tracing-subscriber` did not use any of the `thread-local` crate's APIs effected by the vulnerability. However, updating the version fixes warnings emitted by `cargo audit` and similar tools. ### Added - **filter**: Added combinators for combining filters ([#1578]) ### Fixed - **registry**: Updated `thread-local` to v1.1.4 ([#1858]) Thanks to new contributor @matze for contributing to this release! [RUSTSEC-2022-0006]: https://rustsec.org/advisories/RUSTSEC-2022-0006 [#1578]: https://github.com/tokio-rs/tracing/pull/1578 [#1858]: https://github.com/tokio-rs/tracing/pull/1858 # 0.3.6 (Jan 14, 2022) This release adds configuration options to `tracing_subscriber::fmt` to log source code locations for events. ### Added - **fmt**: Added `with_file` and `with_line_number` configuration methods to `fmt::Format`, `fmt::SubscriberBuilder`, and `fmt::Layer` ([#1773]) ### Fixed - **fmt**: Removed incorrect leading comma from span fields with the `Pretty` formatter ([#1833]) ### Deprecated - **fmt**: Deprecated `Pretty::with_source_location`, as it can now be replaced by the more general `Format`, `SubscriberBuilder`, and `Layer` methods ([#1773]) Thanks to new contributor @renecouto for contributing to this release! [#1773]: https://github.com/tokio-rs/tracing/pull/1773 [#1833]: https://github.com/tokio-rs/tracing/pull/1833 # 0.3.5 (Dec 29, 2021) This release re-enables `RUST_LOG` filtering in `tracing_subscriber::fmt`'s default initialization methods, and adds an `OffsetLocalTime` formatter for using local timestamps with the `time` crate. ### Added - **fmt**: Added `OffsetLocalTime` formatter to `fmt::time` for formatting local timestamps with a fixed offset ([#1772]) ### Fixed - **fmt**: Added a `Targets` filter to `fmt::init()` and `fmt::try_init()` when the "env-filter" feature is disabled, so that `RUST_LOG` is still honored ([#1781]) Thanks to @marienz and @ishitatsuyuki for contributing to this release! [#1772]: https://github.com/tokio-rs/tracing/pull/1772 [#1781]: https://github.com/tokio-rs/tracing/pull/1781 # 0.3.4 (Dec 23, 2021) This release contains bugfixes for the `fmt` module, as well as documentation improvements. ### Fixed - **fmt**: Fixed `fmt` not emitting log lines when timestamp formatting fails ([#1689]) - **fmt**: Fixed double space before thread IDs with `Pretty` formatter ([#1778]) - Several documentation improvements ([#1608], [#1699], [#1701]) [#1689]: https://github.com/tokio-rs/tracing/pull/1689 [#1778]: https://github.com/tokio-rs/tracing/pull/1778 [#1608]: https://github.com/tokio-rs/tracing/pull/1608 [#1699]: https://github.com/tokio-rs/tracing/pull/1699 [#1701]: https://github.com/tokio-rs/tracing/pull/1701 Thanks to new contributors @Swatinem and @rukai for contributing to this release! # 0.3.3 (Nov 29, 2021) This release fixes a pair of regressions in `tracing-subscriber`'s `fmt` module. ### Fixed - **fmt**: Fixed missing event fields with `Compact` formatter ([#1755]) - **fmt**: Fixed `PrettyFields` formatter (and thus `format::Pretty` event formatter) ignoring the `fmt::Layer`'s ANSI color code configuration ([#1747]) [#1755]: https://github.com/tokio-rs/tracing/pull/1755 [#1747]: https://github.com/tokio-rs/tracing/pull/1747 # 0.3.2 (Nov 19, 2021) ### Fixed - **fmt**: Fixed `MakeWriter` filtering not working with `BoxMakeWriter` ([#1694]) ### Added - **fmt**: `Writer::has_ansi_escapes` method to check if an output supports ANSI terminal formatting escape codes ([#1696]) - **fmt**: Added additional ANSI terminal formatting to field formatters when supported ([#1702]) - **fmt**: Added `FmtContext::span_scope`, `FmtContext::event_scope`, and `FmtContext::parent_span` methods for accessing the current span and its scope when formatting an event ([#1728]) - **fmt**: Improved documentation on implementing event formatters ([#1727]) [#1694]: https://github.com/tokio-rs/tracing/pull/1694 [#1696]: https://github.com/tokio-rs/tracing/pull/1696 [#1702]: https://github.com/tokio-rs/tracing/pull/1702 [#1728]: https://github.com/tokio-rs/tracing/pull/1728 [#1727]: https://github.com/tokio-rs/tracing/pull/1727 # 0.3.1 (Oct 25, 2021) This release fixes a few issues related to feature flagging. ### Fixed - **time**: Compilation error when enabling the "time" feature flag without also enabling the "local-time" feature flag ([#1685]) - **registry**: Unused method warnings when the "std" feature is enabled but the "registry" feature is disabled ([#1686]) [#1685]: https://github.com/tokio-rs/tracing/pull/1685 [#1686]: https://github.com/tokio-rs/tracing/pull/1686 # 0.3.0 (Oct 22, 2021) This is a breaking release of `tracing-subscriber`. The primary breaking change in this release is the removal of the dependency on the [`chrono` crate], due to [RUSTSEC-2020-0159]. To replace `chrono`, support is added for formatting timestamps using the [`time` crate] instead. In addition, this release includes a number of other breaking API changes, such as adding (limited) support for `#![no_std]` targets, removing previously deprecated APIs, and more. ### Breaking Changes - Removed APIs deprecated in the v0.2.x release series. - Renamed `Layer::new_span` to `Layer::on_new_span` ([#1674]) - Removed `Layer` impl for `Arc>` and `Arc + ...>` ([#1649]) - Replaced the [`chrono` crate] with the [`time` crate] for timestamp formatting, to resolve [RUSTSEC-2020-0159] ([#1646]) - Removed `json` and `env-filter` from default features. They must now be enabled explictly ([#1647]). This means that `RUST_LOG`-based filters _will not_ work unless the `env-filter` feature is enabled. - Changed `FormatEvent::format_event` and `FormatFields::format_fields` trait methods to take a `Writer` type, rather than a `&mut dyn fmt::Write` trait object ([#1661]) - Changed the signature of the `MakeWriter` trait by adding a lifetime parameter ([#781]) ### Changed - **layer**: Renamed `Layer::new_span` to `Layer::on_new_span` ([#1674]) - **fmt**: Changed `FormatEvent::format_event` and `FormatFields::format_fields` trait methods to take a `Writer` type, rather than a `&mut dyn fmt::Write` trait object ([#1661]) - **json**, **env-filter**: `json` and `env-filter` feature flags are no longer enabled by default ([#1647]) ### Removed - Removed deprecated `CurrentSpan` type ([#1320]) - **registry**: Removed deprecated `SpanRef::parents` iterator, replaced by `SpanRef::scope` in [#1431] ([#1648)]) - **layer**: Removed deprecated `Context::scope` iterator, replaced by `Context::span_scope` and `Context::event_scope` in [#1431] and [#1434] ([#1648)]) - **layer**: Removed `Layer` impl for `Arc>` and `Arc + ...>`. These interfere with per-layer filtering. ([#1649]) - **fmt**: Removed deprecated `LayerBuilder` type ([#1673]) - **fmt**: Removed `fmt::Layer::on_event` (renamed to `fmt::Layer::fmt_event`) ([#1673]) - **fmt**, **chrono**: Removed the `chrono` feature flag and APIs for using the [`chrono` crate] for timestamp formatting ([#1646]) ### Added - **fmt**, **time**: `LocalTime` and `UtcTime` types for formatting timestamps using the [`time` crate] ([#1646]) - **fmt**: Added a lifetime parameter to the `MakeWriter` trait, allowing it to return a borrowed writer. This enables implementations of `MakeWriter` for types such as `Mutex` and `std::fs::File`. ([#781]) - **env-filter**: Documentation improvements ([#1637]) - Support for some APIs on `#![no_std]` targets, by disabling the `std` feature flag ([#1660]) Thanks to @Folyd and @nmathewson for contributing to this release! [#1320]: https://github.com/tokio-rs/tracing/pull/1320 [#1673]: https://github.com/tokio-rs/tracing/pull/1673 [#1674]: https://github.com/tokio-rs/tracing/pull/1674 [#1646]: https://github.com/tokio-rs/tracing/pull/1646 [#1647]: https://github.com/tokio-rs/tracing/pull/1647 [#1648]: https://github.com/tokio-rs/tracing/pull/1648 [#1649]: https://github.com/tokio-rs/tracing/pull/1649 [#1660]: https://github.com/tokio-rs/tracing/pull/1660 [#1661]: https://github.com/tokio-rs/tracing/pull/1661 [#1431]: https://github.com/tokio-rs/tracing/pull/1431 [#1434]: https://github.com/tokio-rs/tracing/pull/1434 [#781]: https://github.com/tokio-rs/tracing/pull/781 [`chrono` crate]: https://crates.io/crates/chrono [`time` crate]: https://crates.io/crates/time [RUSTSEC-2020-0159]: https://rustsec.org/advisories/RUSTSEC-2020-0159.html # 0.2.25 (October 5, 2021) This release fixes an issue where a `Layer` implementation's custom `downcast_raw` implementation was lost when wrapping that layer with a per-layer filter. ### Fixed - **registry**: Forward `Filtered::downcast_raw` to wrapped `Layer` ([#1619]) ### Added - Documentation improvements ([#1596], [#1601]) Thanks to @bryanburgers for contributing to this release! [#1619]: https://github.com/tokio-rs/tracing/pull/1619 [#1601]: https://github.com/tokio-rs/tracing/pull/1601 [#1596]: https://github.com/tokio-rs/tracing/pull/1596 # 0.2.24 (September 19, 2021) This release contains a number of bug fixes, including a fix for `tracing-subscriber` failing to compile on the minimum supported Rust version of 1.42.0. It also adds `IntoIterator` implementations for the `Targets` type. ### Fixed - Fixed compilation on Rust 1.42.0 ([#1580], [#1581]) - **registry**: Ensure per-layer filter `enabled` state is cleared when a global filter short-circuits filter evaluation ([#1575]) - **layer**: Fixed `Layer::on_layer` not being called for `Box`ed `Layer`s, which broke per-layer filtering ([#1576]) ### Added - **filter**: Added `Targets::iter`, returning an iterator over the set of target-level pairs enabled by a `Targets` filter ([#1574]) - **filter**: Added `IntoIterator` implementations for `Targets` and `&Targets` ([#1574]) Thanks to new contributor @connec for contributing to this release! [#1580]: https://github.com/tokio-rs/tracing/pull/1580 [#1581]: https://github.com/tokio-rs/tracing/pull/1581 [#1575]: https://github.com/tokio-rs/tracing/pull/1575 [#1576]: https://github.com/tokio-rs/tracing/pull/1576 [#1574]: https://github.com/tokio-rs/tracing/pull/1574 # 0.2.23 (September 16, 2021) This release fixes a few bugs in the per-layer filtering API added in v0.2.21. ### Fixed - **env-filter**: Fixed excessive `EnvFilter` memory use ([#1568]) - **filter**: Fixed a panic that may occur in debug mode when using per-layer filters together with global filters ([#1569]) - Fixed incorrect documentation formatting ([#1572]) [#1568]: https://github.com/tokio-rs/tracing/pull/1568 [#1569]: https://github.com/tokio-rs/tracing/pull/1569 [#1572]: https://github.com/tokio-rs/tracing/pull/1572 # 0.2.22 (September 13, 2021) This fixes a regression where the `filter::ParseError` type was accidentally renamed. ### Fixed - **filter**: Fix `filter::ParseError` accidentally being renamed to `filter::DirectiveParseError` ([#1558]) [#1558]: https://github.com/tokio-rs/tracing/pull/1558 # 0.2.21 (September 12, 2021) This release introduces the [`Filter`] trait, a new API for [per-layer filtering][plf]. This allows controlling which spans and events are recorded by various layers individually, rather than globally. In addition, it adds a new [`Targets`] filter, which provides a lighter-weight version of the filtering provided by [`EnvFilter`], as well as other smaller API improvements and fixes. ### Deprecated - **registry**: `SpanRef::parent_id`, which cannot properly support per-layer filtering. Use `.parent().map(SpanRef::id)` instead. ([#1523]) ### Fixed - **layer** `Context` methods that are provided when the `Subscriber` implements `LookupSpan` no longer require the "registry" feature flag ([#1525]) - **layer** `fmt::Debug` implementation for `Layered` no longer requires the `S` type parameter to implement `Debug` ([#1528]) ### Added - **registry**: `Filter` trait, `Filtered` type, `Layer::with_filter` method, and other APIs for per-layer filtering ([#1523]) - **filter**: `FilterFn` and `DynFilterFn` types that implement global (`Layer`) and per-layer (`Filter`) filtering for closures and function pointers ([#1523]) - **filter**: `Targets` filter, which implements a lighter-weight form of `EnvFilter`-like filtering ([#1550]) - **env-filter**: Added support for filtering on floating-point values ([#1507]) - **layer**: `Layer::on_layer` callback, called when layering the `Layer` onto a `Subscriber` ([#1523]) - **layer**: `Layer` implementations for `Box` and `Arc` where `L: Layer` ([#1536]) - **layer**: `Layer` implementations for `Box + Send + Sync + 'static>` and `Arc + Send + Sync + 'static>` ([#1536]) - A number of small documentation fixes and improvements ([#1553], [#1544], [#1539], [#1524]) Special thanks to new contributors @jsgf and @maxburke for contributing to this release! [`Filter`]: https://docs.rs/tracing-subscriber/0.2.21/tracing_subscriber/layer/trait.Filter.html [plf]: https://docs.rs/tracing-subscriber/0.2.21/tracing_subscriber/layer/index.html#per-layer-filtering [`Targets`]: https://docs.rs/tracing-subscriber/0.2.21/tracing_subscriber/filter/struct.Targets.html [`EnvFilter`]: https://docs.rs/tracing-subscriber/0.2.21/tracing_subscriber/filter/struct.EnvFilter.html [#1507]: https://github.com/tokio-rs/tracing/pull/1507 [#1523]: https://github.com/tokio-rs/tracing/pull/1523 [#1524]: https://github.com/tokio-rs/tracing/pull/1524 [#1525]: https://github.com/tokio-rs/tracing/pull/1525 [#1528]: https://github.com/tokio-rs/tracing/pull/1528 [#1539]: https://github.com/tokio-rs/tracing/pull/1539 [#1544]: https://github.com/tokio-rs/tracing/pull/1544 [#1550]: https://github.com/tokio-rs/tracing/pull/1550 [#1553]: https://github.com/tokio-rs/tracing/pull/1553 # 0.2.20 (August 17, 2021) ### Fixed - **fmt**: Fixed `fmt` printing only the first `source` for errors with a chain of sources ([#1460]) - **fmt**: Fixed missing space between level and event in the `Pretty` formatter ([#1498]) - **json**: Fixed `Json` formatter not honoring `without_time` and `with_level` configurations ([#1463]) ### Added - **registry**: Improved panic message when cloning a span whose ID doesn't exist, to aid in debugging issues with multiple subscribers ([#1483]) - **registry**: Improved documentation on span ID generation ([#1453]) [#1460]: https://github.com/tokio-rs/tracing/pull/1460 [#1483]: https://github.com/tokio-rs/tracing/pull/1483 [#1463]: https://github.com/tokio-rs/tracing/pull/1463 [#1453]: https://github.com/tokio-rs/tracing/pull/1453 [#1498]: https://github.com/tokio-rs/tracing/pull/1498 Thanks to new contributors @joshtriplett and @lerouxrgd, and returning contributor @teozkr, for contributing to this release! # 0.2.19 (June 25, 2021) ### Deprecated - **registry**: `SpanRef::parents`, `SpanRef::from_root`, and `Context::scope` iterators, which are replaced by new `SpanRef::scope` and `Scope::from_root` iterators ([#1413]) ### Added - **registry**: `SpanRef::scope` method, which returns a leaf-to-root `Iterator` including the leaf span ([#1413]) - **registry**: `Scope::from_root` method, which reverses the `scope` iterator to iterate root-to-leaf ([#1413]) - **registry**: `Context::event_span` method, which looks up the parent span of an event ([#1434]) - **registry**: `Context::event_scope` method, returning a `Scope` iterator over the span scope of an event ([#1434]) - **fmt**: `MakeWriter::make_writer_for` method, which allows returning a different writer based on a span or event's metadata ([#1141]) - **fmt**: `MakeWriterExt` trait, with `with_max_level`, `with_min_level`, `with_filter`, `and`, and `or_else` combinators ([#1274]) - **fmt**: `MakeWriter` implementation for `Arc where &W: io::Write` ([#1274]) Thanks to @teozkr and @Folyd for contributing to this release! [#1413]: https://github.com/tokio-rs/tracing/pull/1413 [#1434]: https://github.com/tokio-rs/tracing/pull/1434 [#1141]: https://github.com/tokio-rs/tracing/pull/1141 [#1274]: https://github.com/tokio-rs/tracing/pull/1274 # 0.2.18 (April 30, 2021) ### Deprecated - Deprecated the `CurrentSpan` type, which is inefficient and largely superseded by the `registry` API ([#1321]) ### Fixed - **json**: Invalid JSON emitted for events in spans with no fields ([#1333]) - **json**: Missing span data for synthesized new span, exit, and close events ([#1334]) - **fmt**: Extra space before log lines when timestamps are disabled ([#1355]) ### Added - **env-filter**: Support for filters on spans whose names contain any characters other than `{` and `]` ([#1368]) Thanks to @Folyd, and new contributors @akinnane and @aym-v for contributing to this release! [#1321]: https://github.com/tokio-rs/tracing/pull/1321 [#1333]: https://github.com/tokio-rs/tracing/pull/1333 [#1334]: https://github.com/tokio-rs/tracing/pull/1334 [#1355]: https://github.com/tokio-rs/tracing/pull/1355 [#1368]: https://github.com/tokio-rs/tracing/pull/1368 # 0.2.17 (March 12, 2021) ### Fixed - **fmt**: `Pretty` formatter now honors `with_ansi(false)` to disable ANSI terminal formatting ([#1240]) - **fmt**: Fixed extra padding when using `Pretty` formatter ([#1275]) - **chrono**: Removed extra trailing space with `ChronoLocal` time formatter ([#1103]) ### Added - **fmt**: Added `FmtContext::current_span()` method, returning the current span ([#1290]) - **fmt**: `FmtSpan` variants may now be combined using the `|` operator for more granular control over what span events are generated ([#1277]) Thanks to new contributors @cratelyn, @dignati, and @zicklag, as well as @Folyd, @matklad, and @najamelan, for contributing to this release! [#1240]: https://github.com/tokio-rs/tracing/pull/1240 [#1275]: https://github.com/tokio-rs/tracing/pull/1275 [#1103]: https://github.com/tokio-rs/tracing/pull/1103 [#1290]: https://github.com/tokio-rs/tracing/pull/1290 [#1277]: https://github.com/tokio-rs/tracing/pull/1277 # 0.2.16 (February 19, 2021) ### Fixed - **env-filter**: Fixed directives where the level is in mixed case (such as `Info`) failing to parse ([#1126]) - **fmt**: Fixed `fmt::Subscriber` not providing a max-level hint ([#1251]) - `tracing-subscriber` no longer enables `tracing` and `tracing-core`'s default features ([#1144]) ### Changed - **chrono**: Updated `chrono` dependency to 0.4.16 ([#1189]) - **log**: Updated `tracing-log` dependency to 0.1.2 Thanks to @salewski, @taiki-e, @davidpdrsn and @markdingram for contributing to this release! [#1126]: https://github.com/tokio-rs/tracing/pull/1126 [#1251]: https://github.com/tokio-rs/tracing/pull/1251 [#1144]: https://github.com/tokio-rs/tracing/pull/1144 [#1189]: https://github.com/tokio-rs/tracing/pull/1189 # 0.2.15 (November 2, 2020) ### Fixed - **fmt**: Fixed wrong lifetime parameters on `FormatFields` impl for `FmtContext` ([#1082]) ### Added - **fmt**: `format::Pretty`, an aesthetically pleasing, human-readable event formatter for local development and user-facing CLIs ([#1080]) - **fmt**: `FmtContext::field_format`, which returns the subscriber's field formatter ([#1082]) [#1082]: https://github.com/tokio-rs/tracing/pull/1082 [#1080]: https://github.com/tokio-rs/tracing/pull/1080 # 0.2.14 (October 22, 2020) ### Fixed - **registry**: Fixed `Registry::new` allocating an excessively large amount of memory, most of which would never be used ([#1064]) ### Changed - **registry**: Improved `new_span` performance by reusing `HashMap` allocations for `Extensions` ([#1064]) - **registry**: Significantly improved the performance of `Registry::enter` and `Registry::exit` ([#1058]) [#1064]: https://github.com/tokio-rs/tracing/pull/1064 [#1058]: https://github.com/tokio-rs/tracing/pull/1058 # 0.2.13 (October 7, 2020) ### Changed - Updated `tracing-core` to 0.1.17 ([#992]) ### Added - **env-filter**: Added support for filtering on targets which contain dashes ([#1014]) - **env-filter**: Added a warning when creating an `EnvFilter` that contains directives that would enable a level disabled by the `tracing` crate's `static_max_level` features ([#1021]) Thanks to @jyn514 and @bkchr for contributing to this release! [#992]: https://github.com/tokio-rs/tracing/pull/992 [#1014]: https://github.com/tokio-rs/tracing/pull/1014 [#1021]: https://github.com/tokio-rs/tracing/pull/1021 # 0.2.12 (September 11, 2020) ### Fixed - **env-filter**: Fixed a regression where `Option` lost its `Into` impl ([#966]) - **env-filter**: Fixed `EnvFilter` enabling spans that should not be enabled when multiple subscribers are in use ([#927]) ### Changed - **json**: `format::Json` now outputs fields in a more readable order ([#892]) - Updated `tracing-core` dependency to 0.1.16 ### Added - **fmt**: Add `BoxMakeWriter` for erasing the type of a `MakeWriter` implementation ([#958]) - **fmt**: Add `TestWriter` `MakeWriter` implementation to support libtest output capturing ([#938]) - **layer**: Add `Layer` impl for `Option where T: Layer` ([#910]) - **env-filter**: Add `From` impl for `Directive` ([#918]) - Multiple documentation fixes and improvements Thanks to @Pothulapati, @samrg472, @bryanburgers, @keetonian, and @SriRamanujam for contributing to this release! [#927]: https://github.com/tokio-rs/tracing/pull/927 [#966]: https://github.com/tokio-rs/tracing/pull/966 [#958]: https://github.com/tokio-rs/tracing/pull/958 [#892]: https://github.com/tokio-rs/tracing/pull/892 [#938]: https://github.com/tokio-rs/tracing/pull/938 [#910]: https://github.com/tokio-rs/tracing/pull/910 [#918]: https://github.com/tokio-rs/tracing/pull/918 # 0.2.11 (August 10, 2020) ### Fixed - **env-filter**: Incorrect max level hint when filters involving span field values are in use (#907) - **registry**: Fixed inconsistent span stacks when multiple registries are in use on the same thread (#901) ### Changed - **env-filter**: `regex` dependency enables fewer unused feature flags (#899) Thanks to @bdonlan and @jeromegn for contributing to this release! # 0.2.10 (July 31, 2020) ### Fixed - **docs**: Incorrect formatting (#862) ### Changed - **filter**: `LevelFilter` is now a re-export of the `tracing_core::LevelFilter` type, it can now be used interchangably with the versions in `tracing` and `tracing-core` (#853) - **filter**: Significant performance improvements when comparing `LevelFilter`s and `Level`s (#853) - Updated the minimum `tracing-core` dependency to 0.1.12 (#853) ### Added - **filter**: `LevelFilter` and `EnvFilter` now participate in `tracing-core`'s max level hinting, improving performance significantly in some use cases where levels are disabled globally (#853) # 0.2.9 (July 23, 2020) ### Fixed - **fmt**: Fixed compilation failure on MSRV when the `chrono` feature is disabled (#844) ### Added - **fmt**: Span lookup methods defined by `layer::Context` are now also provided by `FmtContext` (#834) # 0.2.8 (July 17, 2020) ### Changed - **fmt**: When the `chrono` dependency is enabled, the `SystemTime` timestamp formatter now emits human-readable timestamps rather than using `SystemTime`'s `fmt::Debug`implementation (`chrono` is still required for customized timestamp formatting) (#807) - **ansi**: Updated `ansi_term` dependency to 0.12 (#816) ### Added - **json**: `with_span_list` method to configure the JSON formatter to include a list of all spans in the current trace in formatting events (similarly to the text formatter) (#741) - **json**: `with_current_span` method to configure the JSON formatter to include a field for the _current_ span (the leaf of the trace) in formatted events (#741) - **fmt**: `with_thread_names` and `with_thread_ids` methods to configure `fmt::Subscriber`s and `fmt::Layer`s to include the thread name and/or thread ID of the current thread when formatting events (#818) Thanks to new contributors @mockersf, @keetonian, and @Pothulapati for contributing to this release! # 0.2.7 (July 1, 2020) ### Changed - **parking_lot**: Updated the optional `parking_lot` dependency to accept the latest `parking_lot` version (#774) ### Fixed - **fmt**: Fixed events with explicitly overridden parent spans being formatted as though they were children of the current span (#767) ### Added - **fmt**: Added the option to print synthesized events when spans are created, entered, exited, and closed, including span durations (#761) - Documentation clarification and improvement (#762, #769) Thanks to @rkuhn, @greenwoodcm, and @Ralith for contributing to this release! # 0.2.6 (June 19, 2020) ### Fixed - **fmt**: Fixed an issue in the JSON formatter where using `Span::record` would result in malformed spans (#709) # 0.2.5 (April 21, 2020) ### Changed - **fmt**: Bump sharded-slab dependency (#679) ### Fixed - **fmt**: remove trailing space in `ChronoUtc` `format_time` (#677) # 0.2.4 (April 6, 2020) This release includes several API ergonomics improvements, including shorthand constructors for many types, and an extension trait for initializing subscribers using method-chaining style. Additionally, several bugs in less commonly used `fmt` APIs were fixed. ### Added - **fmt**: Shorthand free functions for constructing most types in `fmt` (including `tracing_subscriber::fmt()` to return a `SubscriberBuilder`, `tracing_subscriber::fmt::layer()` to return a format `Layer`, etc) (#660) - **registry**: Shorthand free function `tracing_subscriber::registry()` to construct a new registry (#660) - Added `SubscriberInitExt` extension trait for more ergonomic subscriber initialization (#660) ### Changed - **fmt**: Moved `LayerBuilder` methods to `Layer` (#655) ### Deprecated - **fmt**: `LayerBuilder`, as `Layer` now implements all builder methods (#655) ### Fixed - **fmt**: Fixed `Compact` formatter not omitting levels with `with_level(false)` (#657) - **fmt**: Fixed `fmt::Layer` duplicating the fields for a new span if another layer has already formatted its fields (#634) - **fmt**: Added missing space when using `record` to add new fields to a span that already has fields (#659) - Updated outdated documentation (#647) # 0.2.3 (March 5, 2020) ### Fixed - **env-filter**: Regression where filter directives were selected in the order they were listed, rather than most specific first (#624) # 0.2.2 (February 27, 2020) ### Added - **fmt**: Added `flatten_event` to `SubscriberBuilder` (#599) - **fmt**: Added `with_level` to `SubscriberBuilder` (#594) # 0.2.1 (February 13, 2020) ### Changed - **filter**: `EnvFilter` directive selection now behaves correctly (i.e. like `env_logger`) (#583) ### Fixed - **filter**: Fixed `EnvFilter` incorrectly allowing less-specific filter directives to enable events that are disabled by more-specific filters (#583) - **filter**: Multiple significant `EnvFilter` performance improvements, especially when filtering events generated by `log` records (#578, #583) - **filter**: Replaced `BTreeMap` with `Vec` in `DirectiveSet`, improving iteration performance significantly with typical numbers of filter directives (#580) A big thank-you to @samschlegel for lots of help with `EnvFilter` performance tuning in this release! # 0.2.0 (February 4, 2020) ### Breaking Changes - **fmt**: Renamed `Context` to `FmtContext` (#420, #425) - **fmt**: Renamed `Builder` to `SubscriberBuilder` (#420) - **filter**: Removed `Filter`. Use `EnvFilter` instead (#434) ### Added - **registry**: `Registry`, a `Subscriber` implementation that `Layer`s can use as a high-performance, in-memory span store. (#420, #425, #432, #433, #435) - **registry**: Added `LookupSpan` trait, implemented by `Subscriber`s to expose stored span data to `Layer`s (#420) - **fmt**: Added `fmt::Layer`, to allow composing log formatting with other `Layer`s - **fmt**: Added support for JSON field and event formatting (#377, #415) - **filter**: Documentation for filtering directives (#554) ### Changed - **fmt**: Renamed `Context` to `FmtContext` (#420, #425) (BREAKING) - **fmt**: Renamed `Builder` to `SubscriberBuilder` (#420) (BREAKING) - **fmt**: Reimplemented `fmt::Subscriber` in terms of the `Registry` and `Layer`s (#420) ### Removed - **filter**: Removed `Filter`. Use `EnvFilter` instead (#434) (BREAKING) ### Fixed - **fmt**: Fixed memory leaks in the slab used to store per-span data (3c35048) - **fmt**: `fmt::SubscriberBuilder::init` not setting up `log` compatibility (#489) - **fmt**: Spans closed by a child span closing not also closing _their_ parents (#514) - **Layer**: Fixed `Layered` subscribers failing to downcast to their own type (#549) - **Layer**: Fixed `Layer::downcast_ref` returning invalid references (#454) # 0.2.0-alpha.6 (February 3, 2020) ### Fixed - **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) - Fixed warnings on nightly Rust compilers (#558) # 0.2.0-alpha.5 (January 31, 2020) ### Added - **env_filter**: Documentation for filtering directives (#554) - **registry**, **env_filter**: Updated `smallvec` dependency to 0.1 (#543) ### Fixed - **registry**: Fixed a memory leak in the slab used to store per-span data (3c35048) - **Layer**: Fixed `Layered` subscribers failing to downcast to their own type (#549) - **fmt**: Fixed a panic when multiple layers insert `FormattedFields` extensions from the same formatter type (1c3bb70) - **fmt**: Fixed `fmt::Layer::on_record` inserting a new `FormattedFields` when formatted fields for a span already exist (1c3bb70) # 0.2.0-alpha.4 (January 11, 2020) ### Fixed - **registry**: Removed inadvertently committed `dbg!` macros (#533) # 0.2.0-alpha.3 (January 10, 2020) ### Added - **fmt**: Public `FormattedFields::new` constructor (#478) - **fmt**: Added examples to `fmt::Layer` documentation (#510) - Documentation now shows what feature flags are required by each API item (#525) ### Fixed - **fmt**: Missing space between timestamp and level (#480) - **fmt**: Incorrect formatting with `with_target(false)` (#481) - **fmt**: `fmt::SubscriberBuilder::init` not setting up `log` compatibility (#489) - **registry**: Spans exited out of order not being closed properly on exit (#509) - **registry**: Memory leak when spans are closed by a child span closing (#514) - **registry**: Spans closed by a child span closing not also closing _their_ parents (#514) - Compilation errors with `no-default-features` (#499, #500) # 0.2.0-alpha.2 (December 8, 2019) ### Added - `LookupSpans` implementation for `Layered` (#448) - `SpanRef::from_root` to iterate over a span's parents from the root (#460) - `Context::scope`, to iterate over the current context from the root (#460) - `Context::lookup_current`, which returns a `SpanRef` to the current span's data (#460) ### Changed - Lifetimes on some new `Context` methods to be less restrictive (#460) ### Fixed - `Layer::downcast_ref` returning invalid references (#454) - Compilation failure on 32-bit platforms (#462) - Compilation failure with ANSI formatters (#438) # 0.2.0-alpha.1 (November 18, 2019) ### Added - `Registry`, a reusable span store that `Layer`s can use a high-performance, in-memory store. (#420, #425, #432, #433, #435) - Reimplemented `fmt::Subscriber` in terms of the `Registry` and `Layer`s (#420) - Add benchmarks for fmt subscriber (#421) - Add support for JSON field and event formatting (#377, #415) ### Changed - **BREAKING**: Change `fmt::format::FormatFields` and `fmt::format::FormatEvent` to accept a mandatory `FmtContext`. These `FormatFields` and `FormatEvent` will likely see additional breaking changes in subsequent alpha. (#420, #425) - **BREAKING**: Removed `Filter`. Use `EnvFilter` instead (#434) ### Contributers Thanks to all the contributers to this release! - @pimeys for #377 and #415 # 0.1.6 (October 29, 2019) ### Added - Add `init` and `try_init` functions to `FmtSubscriber` (#385) - Add `ChronoUtc` and `ChronoLocal` timers, RFC 3339 support (#387) - Add `tracing::subscriber::set_default` which sets the default subscriber and returns a drop guard. This drop guard will reset the dispatch on drop (#388). ### Fixed - Fix default level for `EnvFilter`. Setting `RUST_LOG=target` previously only the `ERROR` level, while it should enable everything. `tracing-subscriber` now defaults to `TRACE` if no level is specified (#401) - Fix `tracing-log` feature flag for init + try_init. The feature flag `tracing_log` was used instead of the correct `tracing-log`. As a result, both `tracing-log` and `tracing_log` needed to be specified in order to initialize the global logger. Only `tracing-log` needs to be specified now (#400). ### Contributers Thanks to all the contributers to this release! - @emschwartz for #385, #387, #400 and #401 - @bIgBV for #388 # 0.1.5 (October 7, 2019) ### Fixed - Spans not being closed properly when `FmtSubscriber::current_span` is used (#371) # 0.1.4 (September 26, 2019) ### Fixed - Spans entered twice on the same thread sometimes being completely exited when the more deeply-nested entry is exited (#361) - Setting `with_ansi(false)` on `FmtSubscriber` not disabling ANSI color formatting for timestamps (#354) - Incorrect reference counting in `FmtSubscriber` that could cause spans to not be closed when all references are dropped (#366) # 0.1.3 (September 16, 2019) ### Fixed - `Layered` subscribers not properly forwarding calls to `current_span` (#350) # 0.1.2 (September 12, 2019) ### Fixed - `EnvFilter` ignoring directives with targets that are the same number of characters (#333) - `EnvFilter` failing to properly apply filter directives to events generated from `log` records by`tracing-log` (#344) ### Changed - Renamed `Filter` to `EnvFilter`, deprecated `Filter` (#339) - Renamed "filter" feature flag to "env-filter", deprecated "filter" (#339) - `FmtSubscriber` now defaults to enabling only the `INFO` level and above when a max level filter or `EnvFilter` is not set (#336) - Made `parking_lot` dependency an opt-in feature flag (#348) ### Added - `EnvFilter::add_directive` to add new directives to filters after they are constructed (#334) - `fmt::Builder::with_max_level` to set a global level filter for a `FmtSubscriber` without requiring the use of `EnvFilter` (#336) - `Layer` implementation for `LevelFilter` (#336) - `EnvFilter` now implements `fmt::Display` (#329) ### Removed - Removed dependency on `crossbeam-util` (#348) # 0.1.1 (September 4, 2019) ### Fixed - Potential double panic in `CurrentSpan` (#325) # 0.1.0 (September 3, 2019) - Initial release tracing-subscriber-0.3.18/Cargo.toml0000644000000100650000000000100127150ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.63.0" name = "tracing-subscriber" version = "0.3.18" authors = [ "Eliza Weisman ", "David Barsky ", "Tokio Contributors ", ] description = """ Utilities for implementing and composing `tracing` subscribers. """ homepage = "https://tokio.rs" readme = "README.md" keywords = [ "logging", "tracing", "metrics", "subscriber", ] categories = [ "development-tools::debugging", "development-tools::profiling", "asynchronous", ] license = "MIT" repository = "https://github.com/tokio-rs/tracing" resolver = "2" [package.metadata.docs.rs] all-features = true rustdoc-args = [ "--cfg", "docsrs", ] [[bench]] name = "filter" harness = false [[bench]] name = "filter_log" harness = false [[bench]] name = "fmt" harness = false [[bench]] name = "enter" harness = false [dependencies.chrono] version = "0.4.26" features = [ "clock", "std", ] optional = true default-features = false [dependencies.matchers] version = "0.1.0" optional = true [dependencies.nu-ansi-term] version = "0.46.0" optional = true [dependencies.once_cell] version = "1.13.0" optional = true [dependencies.parking_lot] version = "0.12.1" optional = true [dependencies.regex] version = "1.6.0" features = [ "std", "unicode-case", "unicode-perl", ] optional = true default-features = false [dependencies.serde] version = "1.0.140" optional = true [dependencies.serde_json] version = "1.0.82" optional = true [dependencies.sharded-slab] version = "0.1.4" optional = true [dependencies.smallvec] version = "1.9.0" optional = true [dependencies.thread_local] version = "1.1.4" optional = true [dependencies.time] version = "0.3.2" features = ["formatting"] optional = true [dependencies.tracing] version = "0.1.35" optional = true default-features = false [dependencies.tracing-core] version = "0.1.30" default-features = false [dependencies.tracing-log] version = "0.2.0" features = [ "log-tracer", "std", ] optional = true default-features = false [dependencies.tracing-serde] version = "0.1.3" optional = true [dev-dependencies.criterion] version = "0.3.6" default-features = false [dev-dependencies.log] version = "0.4.17" [dev-dependencies.regex] version = "1" features = ["std"] default-features = false [dev-dependencies.time] version = "0.3.2" features = [ "formatting", "macros", ] [dev-dependencies.tokio] version = "1" features = [ "rt", "macros", ] [dev-dependencies.tracing] version = "0.1.35" [dev-dependencies.tracing-futures] version = "0.2.0" features = [ "std-future", "std", ] default-features = false [dev-dependencies.tracing-log] version = "0.2.0" [features] alloc = [] ansi = [ "fmt", "nu-ansi-term", ] default = [ "smallvec", "fmt", "ansi", "tracing-log", "std", ] env-filter = [ "matchers", "regex", "once_cell", "tracing", "std", "thread_local", ] fmt = [ "registry", "std", ] json = [ "tracing-serde", "serde", "serde_json", ] local-time = ["time/local-offset"] registry = [ "sharded-slab", "thread_local", "std", ] std = [ "alloc", "tracing-core/std", ] valuable = [ "tracing-core/valuable", "valuable_crate", "valuable-serde", "tracing-serde/valuable", ] [target."cfg(tracing_unstable)".dependencies.valuable-serde] version = "0.1.0" optional = true default-features = false [target."cfg(tracing_unstable)".dependencies.valuable_crate] version = "0.1.0" optional = true default-features = false package = "valuable" [badges.maintenance] status = "experimental" tracing-subscriber-0.3.18/Cargo.toml.orig000064400000000000000000000071651046102023000164050ustar 00000000000000[package] name = "tracing-subscriber" version = "0.3.18" authors = [ "Eliza Weisman ", "David Barsky ", "Tokio Contributors ", ] edition = "2018" license = "MIT" readme = "README.md" repository = "https://github.com/tokio-rs/tracing" homepage = "https://tokio.rs" description = """ Utilities for implementing and composing `tracing` subscribers. """ categories = [ "development-tools::debugging", "development-tools::profiling", "asynchronous", ] keywords = ["logging", "tracing", "metrics", "subscriber"] rust-version = "1.63.0" [features] default = ["smallvec", "fmt", "ansi", "tracing-log", "std"] alloc = [] std = ["alloc", "tracing-core/std"] env-filter = ["matchers", "regex", "once_cell", "tracing", "std", "thread_local"] fmt = ["registry", "std"] ansi = ["fmt", "nu-ansi-term"] registry = ["sharded-slab", "thread_local", "std"] json = ["tracing-serde", "serde", "serde_json"] valuable = ["tracing-core/valuable", "valuable_crate", "valuable-serde", "tracing-serde/valuable"] # Enables support for local time when using the `time` crate timestamp # formatters. local-time = ["time/local-offset"] [dependencies] tracing-core = { path = "../tracing-core", version = "0.1.30", default-features = false } # only required by the filter feature tracing = { optional = true, path = "../tracing", version = "0.1.35", default-features = false } matchers = { optional = true, version = "0.1.0" } regex = { optional = true, version = "1.6.0", default-features = false, features = ["std", "unicode-case", "unicode-perl"] } smallvec = { optional = true, version = "1.9.0" } once_cell = { optional = true, version = "1.13.0" } # fmt tracing-log = { path = "../tracing-log", version = "0.2.0", optional = true, default-features = false, features = ["log-tracer", "std"] } nu-ansi-term = { version = "0.46.0", optional = true } time = { version = "0.3.2", features = ["formatting"], optional = true } # only required by the json feature serde_json = { version = "1.0.82", optional = true } serde = { version = "1.0.140", optional = true } tracing-serde = { path = "../tracing-serde", version = "0.1.3", 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 } thread_local = { version = "1.1.4", optional = true } [target.'cfg(tracing_unstable)'.dependencies] valuable_crate = { package = "valuable", version = "0.1.0", optional = true, default-features = false } valuable-serde = { version = "0.1.0", optional = true, default-features = false } [dev-dependencies] tracing = { path = "../tracing", version = "0.1.35" } tracing-mock = { path = "../tracing-mock", features = ["tracing-subscriber"] } log = "0.4.17" tracing-log = { path = "../tracing-log", version = "0.2.0" } criterion = { version = "0.3.6", default-features = false } regex = { version = "1", default-features = false, features = ["std"] } tracing-futures = { path = "../tracing-futures", version = "0.2.0", default-features = false, features = ["std-future", "std"] } tokio = { version = "1", features = ["rt", "macros"] } # Enable the `time` crate's `macros` feature, for examples. time = { version = "0.3.2", features = ["formatting", "macros"] } [badges] maintenance = { status = "experimental" } [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [[bench]] name = "filter" harness = false [[bench]] name = "filter_log" harness = false [[bench]] name = "fmt" harness = false [[bench]] name = "enter" harness = false tracing-subscriber-0.3.18/LICENSE000064400000000000000000000020461046102023000145140ustar 00000000000000Copyright (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. tracing-subscriber-0.3.18/README.md000064400000000000000000000052011046102023000147620ustar 00000000000000![Tracing — Structured, application-level diagnostics][splash] [splash]: https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/splash.svg # tracing-subscriber Utilities for implementing and composing [`tracing`][tracing] subscribers. [![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] [tracing]: https://github.com/tokio-rs/tracing/tree/master/tracing [tracing-fmt]: https://github.com/tokio-rs/tracing/tree/master/tracing-subscriber [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/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 [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-experimental-blue.svg *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.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 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.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. ## 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. tracing-subscriber-0.3.18/benches/enter.rs000064400000000000000000000042221046102023000165770ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; use tracing_subscriber::prelude::*; fn enter(c: &mut Criterion) { let mut group = c.benchmark_group("enter"); let _subscriber = tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .finish() .set_default(); group.bench_function("enabled", |b| { let span = tracing::info_span!("foo"); b.iter_with_large_drop(|| span.enter()) }); group.bench_function("disabled", |b| { let span = tracing::debug_span!("foo"); b.iter_with_large_drop(|| span.enter()) }); } fn enter_exit(c: &mut Criterion) { let mut group = c.benchmark_group("enter_exit"); let _subscriber = tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .finish() .set_default(); group.bench_function("enabled", |b| { let span = tracing::info_span!("foo"); b.iter(|| span.enter()) }); group.bench_function("disabled", |b| { let span = tracing::debug_span!("foo"); b.iter(|| span.enter()) }); } fn enter_many(c: &mut Criterion) { let mut group = c.benchmark_group("enter_many"); let _subscriber = tracing_subscriber::fmt() .with_max_level(tracing::Level::INFO) .finish() .set_default(); group.bench_function("enabled", |b| { let span1 = tracing::info_span!("span1"); let _e1 = span1.enter(); let span2 = tracing::info_span!("span2"); let _e2 = span2.enter(); let span3 = tracing::info_span!("span3"); let _e3 = span3.enter(); let span = tracing::info_span!("foo"); b.iter_with_large_drop(|| span.enter()) }); group.bench_function("disabled", |b| { let span1 = tracing::info_span!("span1"); let _e1 = span1.enter(); let span2 = tracing::info_span!("span2"); let _e2 = span2.enter(); let span3 = tracing::info_span!("span3"); let _e3 = span3.enter(); let span = tracing::debug_span!("foo"); b.iter_with_large_drop(|| span.enter()) }); } criterion_group!(benches, enter, enter_exit, enter_many); criterion_main!(benches); tracing-subscriber-0.3.18/benches/filter.rs000064400000000000000000000254111046102023000167520ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; use std::time::Duration; use tracing::{dispatcher::Dispatch, span, Event, Id, Metadata}; use tracing_subscriber::{prelude::*, EnvFilter}; mod support; use support::MultithreadedBench; /// A subscriber that is enabled but otherwise does nothing. struct EnabledSubscriber; impl tracing::Subscriber for EnabledSubscriber { fn new_span(&self, span: &span::Attributes<'_>) -> Id { let _ = span; Id::from_u64(0xDEAD_FACE) } fn event(&self, event: &Event<'_>) { let _ = event; } fn record(&self, span: &Id, values: &span::Record<'_>) { let _ = (span, values); } fn record_follows_from(&self, span: &Id, follows: &Id) { let _ = (span, follows); } fn enabled(&self, metadata: &Metadata<'_>) -> bool { let _ = metadata; true } fn enter(&self, span: &Id) { let _ = span; } fn exit(&self, span: &Id) { let _ = span; } } fn bench_static(c: &mut Criterion) { let mut group = c.benchmark_group("static"); group.bench_function("baseline_single_threaded", |b| { tracing::subscriber::with_default(EnabledSubscriber, || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); tracing::debug!(target: "static_filter", "hi"); tracing::warn!(target: "static_filter", "hi"); tracing::trace!(target: "foo", "hi"); }) }); }); group.bench_function("single_threaded", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); tracing::debug!(target: "static_filter", "hi"); tracing::warn!(target: "static_filter", "hi"); tracing::trace!(target: "foo", "hi"); }) }); }); group.bench_function("enabled_one", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("enabled_many", |b| { let filter = "foo=debug,bar=trace,baz=error,quux=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_level_one", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::debug!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_level_many", |b| { let filter = "foo=debug,bar=info,baz=error,quux=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::trace!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_one", |b| { let filter = "foo=info".parse::().expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_many", |b| { let filter = "foo=debug,bar=trace,baz=error,quux=warn,whibble=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("baseline_multithreaded", |b| { let dispatch = Dispatch::new(EnabledSubscriber); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { tracing::info!(target: "static_filter", "hi"); }) .thread(|| { tracing::debug!(target: "static_filter", "hi"); }) .thread(|| { tracing::warn!(target: "static_filter", "hi"); }) .thread(|| { tracing::warn!(target: "foo", "hi"); }) .run(); total += elapsed; } total }) }); group.bench_function("multithreaded", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); let dispatch = Dispatch::new(EnabledSubscriber.with(filter)); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { tracing::info!(target: "static_filter", "hi"); }) .thread(|| { tracing::debug!(target: "static_filter", "hi"); }) .thread(|| { tracing::warn!(target: "static_filter", "hi"); }) .thread(|| { tracing::warn!(target: "foo", "hi"); }) .run(); total += elapsed; } total }); }); group.finish(); } fn bench_dynamic(c: &mut Criterion) { let mut group = c.benchmark_group("dynamic"); group.bench_function("baseline_single_threaded", |b| { tracing::subscriber::with_default(EnabledSubscriber, || { b.iter(|| { tracing::info_span!("foo").in_scope(|| { tracing::info!("hi"); tracing::debug!("hi"); }); tracing::info_span!("bar").in_scope(|| { tracing::warn!("hi"); }); tracing::trace!("hi"); }) }); }); group.bench_function("single_threaded", |b| { let filter = "[foo]=trace".parse::().expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info_span!("foo").in_scope(|| { tracing::info!("hi"); tracing::debug!("hi"); }); tracing::info_span!("bar").in_scope(|| { tracing::warn!("hi"); }); tracing::trace!("hi"); }) }); }); group.bench_function("baseline_multithreaded", |b| { let dispatch = Dispatch::new(EnabledSubscriber); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); tracing::info!("hi"); }) .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); tracing::debug!("hi"); }) .thread(|| { let span = tracing::info_span!("bar"); let _ = span.enter(); tracing::debug!("hi"); }) .thread(|| { tracing::trace!("hi"); }) .run(); total += elapsed; } total }) }); group.bench_function("multithreaded", |b| { let filter = "[foo]=trace".parse::().expect("should parse"); let dispatch = Dispatch::new(EnabledSubscriber.with(filter)); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); tracing::info!("hi"); }) .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); tracing::debug!("hi"); }) .thread(|| { let span = tracing::info_span!("bar"); let _ = span.enter(); tracing::debug!("hi"); }) .thread(|| { tracing::trace!("hi"); }) .run(); total += elapsed; } total }) }); group.finish(); } fn bench_mixed(c: &mut Criterion) { let mut group = c.benchmark_group("mixed"); group.bench_function("disabled", |b| { let filter = "[foo]=trace,bar[quux]=debug,[{baz}]=debug,asdf=warn,wibble=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_by_level", |b| { let filter = "[foo]=info,bar[quux]=debug,asdf=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::trace!(target: "static_filter", "hi"); }) }); }); } criterion_group!(benches, bench_static, bench_dynamic, bench_mixed); criterion_main!(benches); tracing-subscriber-0.3.18/benches/filter_log.rs000064400000000000000000000253751046102023000176240ustar 00000000000000use criterion::{criterion_group, criterion_main, Criterion}; use std::time::Duration; use tracing::{dispatcher::Dispatch, span, Event, Id, Metadata}; use tracing_subscriber::{prelude::*, EnvFilter}; mod support; use support::MultithreadedBench; /// A subscriber that is enabled but otherwise does nothing. struct EnabledSubscriber; impl tracing::Subscriber for EnabledSubscriber { fn new_span(&self, span: &span::Attributes<'_>) -> Id { let _ = span; Id::from_u64(0xDEAD_FACE) } fn event(&self, event: &Event<'_>) { let _ = event; } fn record(&self, span: &Id, values: &span::Record<'_>) { let _ = (span, values); } fn record_follows_from(&self, span: &Id, follows: &Id) { let _ = (span, follows); } fn enabled(&self, metadata: &Metadata<'_>) -> bool { let _ = metadata; true } fn enter(&self, span: &Id) { let _ = span; } fn exit(&self, span: &Id) { let _ = span; } } fn bench_static(c: &mut Criterion) { let _ = tracing_log::LogTracer::init(); let mut group = c.benchmark_group("log/static"); group.bench_function("baseline_single_threaded", |b| { tracing::subscriber::with_default(EnabledSubscriber, || { b.iter(|| { log::info!(target: "static_filter", "hi"); log::debug!(target: "static_filter", "hi"); log::warn!(target: "static_filter", "hi"); log::trace!(target: "foo", "hi"); }) }); }); group.bench_function("single_threaded", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); log::debug!(target: "static_filter", "hi"); log::warn!(target: "static_filter", "hi"); log::trace!(target: "foo", "hi"); }) }); }); group.bench_function("enabled_one", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("enabled_many", |b| { let filter = "foo=debug,bar=trace,baz=error,quux=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_level_one", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::debug!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_level_many", |b| { let filter = "foo=debug,bar=info,baz=error,quux=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::trace!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_one", |b| { let filter = "foo=info".parse::().expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_many", |b| { let filter = "foo=debug,bar=trace,baz=error,quux=warn,whibble=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("baseline_multithreaded", |b| { let dispatch = Dispatch::new(EnabledSubscriber); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { log::info!(target: "static_filter", "hi"); }) .thread(|| { log::debug!(target: "static_filter", "hi"); }) .thread(|| { log::warn!(target: "static_filter", "hi"); }) .thread(|| { log::warn!(target: "foo", "hi"); }) .run(); total += elapsed; } total }) }); group.bench_function("multithreaded", |b| { let filter = "static_filter=info" .parse::() .expect("should parse"); let dispatch = Dispatch::new(EnabledSubscriber.with(filter)); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { log::info!(target: "static_filter", "hi"); }) .thread(|| { log::debug!(target: "static_filter", "hi"); }) .thread(|| { log::warn!(target: "static_filter", "hi"); }) .thread(|| { log::warn!(target: "foo", "hi"); }) .run(); total += elapsed; } total }); }); group.finish(); } fn bench_dynamic(c: &mut Criterion) { let _ = tracing_log::LogTracer::init(); let mut group = c.benchmark_group("log/dynamic"); group.bench_function("baseline_single_threaded", |b| { tracing::subscriber::with_default(EnabledSubscriber, || { b.iter(|| { tracing::info_span!("foo").in_scope(|| { log::info!("hi"); log::debug!("hi"); }); tracing::info_span!("bar").in_scope(|| { log::warn!("hi"); }); log::trace!("hi"); }) }); }); group.bench_function("single_threaded", |b| { let filter = "[foo]=trace".parse::().expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { tracing::info_span!("foo").in_scope(|| { log::info!("hi"); log::debug!("hi"); }); tracing::info_span!("bar").in_scope(|| { log::warn!("hi"); }); log::trace!("hi"); }) }); }); group.bench_function("baseline_multithreaded", |b| { let dispatch = Dispatch::new(EnabledSubscriber); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); log::info!("hi"); }) .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); log::debug!("hi"); }) .thread(|| { let span = tracing::info_span!("bar"); let _ = span.enter(); log::debug!("hi"); }) .thread(|| { log::trace!("hi"); }) .run(); total += elapsed; } total }) }); group.bench_function("multithreaded", |b| { let filter = "[foo]=trace".parse::().expect("should parse"); let dispatch = Dispatch::new(EnabledSubscriber.with(filter)); b.iter_custom(|iters| { let mut total = Duration::from_secs(0); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); log::info!("hi"); }) .thread(|| { let span = tracing::info_span!("foo"); let _ = span.enter(); log::debug!("hi"); }) .thread(|| { let span = tracing::info_span!("bar"); let _ = span.enter(); log::debug!("hi"); }) .thread(|| { log::trace!("hi"); }) .run(); total += elapsed; } total }) }); group.finish(); } fn bench_mixed(c: &mut Criterion) { let _ = tracing_log::LogTracer::init(); let mut group = c.benchmark_group("log/mixed"); group.bench_function("disabled", |b| { let filter = "[foo]=trace,bar[quux]=debug,[{baz}]=debug,asdf=warn,wibble=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::info!(target: "static_filter", "hi"); }) }); }); group.bench_function("disabled_by_level", |b| { let filter = "[foo]=info,bar[quux]=debug,asdf=warn,static_filter=info" .parse::() .expect("should parse"); tracing::subscriber::with_default(EnabledSubscriber.with(filter), || { b.iter(|| { log::trace!(target: "static_filter", "hi"); }) }); }); } criterion_group!(benches, bench_static, bench_dynamic, bench_mixed); criterion_main!(benches); tracing-subscriber-0.3.18/benches/fmt.rs000064400000000000000000000323661046102023000162620ustar 00000000000000use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::{io, time::Duration}; mod support; use support::MultithreadedBench; /// A fake writer that doesn't actually do anything. /// /// We want to measure the subscriber's overhead, *not* the performance of /// stdout/file writers. Using a no-op Write implementation lets us only measure /// the subscriber's overhead. struct NoWriter; impl io::Write for NoWriter { fn write(&mut self, buf: &[u8]) -> io::Result { Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl NoWriter { fn new() -> Self { Self } } fn bench_new_span(c: &mut Criterion) { bench_thrpt(c, "new_span", |group, i| { group.bench_with_input(BenchmarkId::new("single_thread", i), i, |b, &i| { tracing::dispatcher::with_default(&mk_dispatch(), || { b.iter(|| { for n in 0..i { let _span = tracing::info_span!("span", n); } }) }); }); group.bench_with_input(BenchmarkId::new("multithreaded", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); let dispatch = mk_dispatch(); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(move || { for n in 0..i { let _span = tracing::info_span!("span", n); } }) .thread(move || { for n in 0..i { let _span = tracing::info_span!("span", n); } }) .thread(move || { for n in 0..i { let _span = tracing::info_span!("span", n); } }) .thread(move || { for n in 0..i { let _span = tracing::info_span!("span", n); } }) .run(); total += elapsed; } total }) }); }); } type Group<'a> = criterion::BenchmarkGroup<'a, criterion::measurement::WallTime>; fn bench_thrpt(c: &mut Criterion, name: &'static str, mut f: impl FnMut(&mut Group<'_>, &usize)) { const N_SPANS: &[usize] = &[1, 10, 50]; let mut group = c.benchmark_group(name); for spans in N_SPANS { group.throughput(Throughput::Elements(*spans as u64)); f(&mut group, spans); } group.finish(); } fn mk_dispatch() -> tracing::Dispatch { let subscriber = tracing_subscriber::FmtSubscriber::builder() .with_writer(NoWriter::new) .finish(); tracing::Dispatch::new(subscriber) } fn bench_event(c: &mut Criterion) { bench_thrpt(c, "event", |group, i| { group.bench_with_input(BenchmarkId::new("root/single_threaded", i), i, |b, &i| { let dispatch = mk_dispatch(); tracing::dispatcher::with_default(&dispatch, || { b.iter(|| { for n in 0..i { tracing::info!(n); } }) }); }); group.bench_with_input(BenchmarkId::new("root/multithreaded", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); let dispatch = mk_dispatch(); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread(move || { for n in 0..i { tracing::info!(n); } }) .thread(move || { for n in 0..i { tracing::info!(n); } }) .thread(move || { for n in 0..i { tracing::info!(n); } }) .thread(move || { for n in 0..i { tracing::info!(n); } }) .run(); total += elapsed; } total }) }); group.bench_with_input( BenchmarkId::new("unique_parent/single_threaded", i), i, |b, &i| { tracing::dispatcher::with_default(&mk_dispatch(), || { let span = tracing::info_span!("unique_parent", foo = false); let _guard = span.enter(); b.iter(|| { for n in 0..i { tracing::info!(n); } }) }); }, ); group.bench_with_input( BenchmarkId::new("unique_parent/multithreaded", i), i, |b, &i| { b.iter_custom(|iters| { let mut total = Duration::from_secs(0); let dispatch = mk_dispatch(); for _ in 0..iters { let bench = MultithreadedBench::new(dispatch.clone()); let elapsed = bench .thread_with_setup(move |start| { let span = tracing::info_span!("unique_parent", foo = false); let _guard = span.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }) .thread_with_setup(move |start| { let span = tracing::info_span!("unique_parent", foo = false); let _guard = span.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }) .thread_with_setup(move |start| { let span = tracing::info_span!("unique_parent", foo = false); let _guard = span.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }) .thread_with_setup(move |start| { let span = tracing::info_span!("unique_parent", foo = false); let _guard = span.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }) .run(); total += elapsed; } total }) }, ); group.bench_with_input( BenchmarkId::new("shared_parent/multithreaded", i), i, |b, &i| { b.iter_custom(|iters| { let dispatch = mk_dispatch(); let mut total = Duration::from_secs(0); for _ in 0..iters { let parent = tracing::dispatcher::with_default(&dispatch, || { tracing::info_span!("shared_parent", foo = "hello world") }); let bench = MultithreadedBench::new(dispatch.clone()); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); for n in 0..i { tracing::info!(n); } }); let elapsed = bench.run(); total += elapsed; } total }) }, ); group.bench_with_input( BenchmarkId::new("multi-parent/multithreaded", i), i, |b, &i| { b.iter_custom(|iters| { let dispatch = mk_dispatch(); let mut total = Duration::from_secs(0); for _ in 0..iters { let parent = tracing::dispatcher::with_default(&dispatch, || { tracing::info_span!("multiparent", foo = "hello world") }); let bench = MultithreadedBench::new(dispatch.clone()); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); let mut span = tracing::info_span!("parent"); for n in 0..i { let s = tracing::info_span!(parent: &span, "parent2", n, i); s.in_scope(|| { tracing::info!(n); }); span = s; } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); let mut span = tracing::info_span!("parent"); for n in 0..i { let s = tracing::info_span!(parent: &span, "parent2", n, i); s.in_scope(|| { tracing::info!(n); }); span = s; } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); let mut span = tracing::info_span!("parent"); for n in 0..i { let s = tracing::info_span!(parent: &span, "parent2", n, i); s.in_scope(|| { tracing::info!(n); }); span = s; } }); let parent2 = parent.clone(); bench.thread_with_setup(move |start| { let _guard = parent2.enter(); start.wait(); let mut span = tracing::info_span!("parent"); for n in 0..i { let s = tracing::info_span!(parent: &span, "parent2", n, i); s.in_scope(|| { tracing::info!(n); }); span = s; } }); let elapsed = bench.run(); total += elapsed; } total }) }, ); }); } criterion_group!(benches, bench_new_span, bench_event); criterion_main!(benches); tracing-subscriber-0.3.18/benches/support/mod.rs000064400000000000000000000022571046102023000177630ustar 00000000000000use std::{ sync::{Arc, Barrier}, thread, time::{Duration, Instant}, }; use tracing::dispatcher::Dispatch; #[derive(Clone)] pub(super) struct MultithreadedBench { start: Arc, end: Arc, dispatch: Dispatch, } impl MultithreadedBench { pub(super) fn new(dispatch: Dispatch) -> Self { Self { start: Arc::new(Barrier::new(5)), end: Arc::new(Barrier::new(5)), dispatch, } } pub(super) fn thread(&self, f: impl FnOnce() + Send + 'static) -> &Self { self.thread_with_setup(|start| { start.wait(); f() }) } pub(super) fn thread_with_setup(&self, f: impl FnOnce(&Barrier) + Send + 'static) -> &Self { let this = self.clone(); thread::spawn(move || { let dispatch = this.dispatch.clone(); tracing::dispatcher::with_default(&dispatch, move || { f(&this.start); this.end.wait(); }) }); self } pub(super) fn run(&self) -> Duration { self.start.wait(); let t0 = Instant::now(); self.end.wait(); t0.elapsed() } } tracing-subscriber-0.3.18/src/field/debug.rs000064400000000000000000000046721046102023000170240ustar 00000000000000//! `MakeVisitor` wrappers for working with `fmt::Debug` fields. use super::{MakeVisitor, VisitFmt, VisitOutput}; use tracing_core::field::{Field, Visit}; use core::fmt; /// A visitor wrapper that ensures any `fmt::Debug` fields are formatted using /// the alternate (`:#`) formatter. #[derive(Debug, Clone)] pub struct Alt(V); // TODO(eliza): When `error` as a primitive type is stable, add a // `DisplayErrors` wrapper... // === impl Alt === // impl Alt { /// Wraps the provided visitor so that any `fmt::Debug` fields are formatted /// using the alternative (`:#`) formatter. pub fn new(inner: V) -> Self { Alt(inner) } } impl MakeVisitor for Alt where V: MakeVisitor, { type Visitor = Alt; #[inline] fn make_visitor(&self, target: T) -> Self::Visitor { Alt(self.0.make_visitor(target)) } } impl Visit for Alt where V: Visit, { #[inline] fn record_f64(&mut self, field: &Field, value: f64) { self.0.record_f64(field, value) } #[inline] fn record_i64(&mut self, field: &Field, value: i64) { self.0.record_i64(field, value) } #[inline] fn record_u64(&mut self, field: &Field, value: u64) { self.0.record_u64(field, value) } #[inline] fn record_bool(&mut self, field: &Field, value: bool) { self.0.record_bool(field, value) } /// Visit a string value. fn record_str(&mut self, field: &Field, value: &str) { self.0.record_str(field, value) } // TODO(eliza): add RecordError when stable // fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { // self.record_debug(field, &format_args!("{}", value)) // } #[inline] fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { self.0.record_debug(field, &format_args!("{:#?}", value)) } } impl VisitOutput for Alt where V: VisitOutput, { #[inline] fn finish(self) -> O { self.0.finish() } } feature! { #![feature = "std"] use super::VisitWrite; use std::io; impl VisitWrite for Alt where V: VisitWrite, { #[inline] fn writer(&mut self) -> &mut dyn io::Write { self.0.writer() } } } impl VisitFmt for Alt where V: VisitFmt, { #[inline] fn writer(&mut self) -> &mut dyn fmt::Write { self.0.writer() } } tracing-subscriber-0.3.18/src/field/delimited.rs000064400000000000000000000110621046102023000176650ustar 00000000000000//! A `MakeVisitor` wrapper that separates formatted fields with a delimiter. use super::{MakeVisitor, VisitFmt, VisitOutput}; use core::fmt; use tracing_core::field::{Field, Visit}; /// A `MakeVisitor` wrapper that wraps a visitor that writes formatted output so /// that a delimiter is inserted between writing formatted field values. #[derive(Debug, Clone)] pub struct Delimited { delimiter: D, inner: V, } /// A visitor wrapper that inserts a delimiter after the wrapped visitor formats /// a field value. #[derive(Debug)] pub struct VisitDelimited { delimiter: D, seen: bool, inner: V, err: fmt::Result, } // === impl Delimited === impl MakeVisitor for Delimited where D: AsRef + Clone, V: MakeVisitor, V::Visitor: VisitFmt, { type Visitor = VisitDelimited; fn make_visitor(&self, target: T) -> Self::Visitor { let inner = self.inner.make_visitor(target); VisitDelimited::new(self.delimiter.clone(), inner) } } impl Delimited { /// Returns a new [`MakeVisitor`] implementation that wraps `inner` so that /// it will format each visited field separated by the provided `delimiter`. /// /// [`MakeVisitor`]: super::MakeVisitor pub fn new(delimiter: D, inner: V) -> Self { Self { delimiter, inner } } } // === impl VisitDelimited === impl VisitDelimited { /// Returns a new [`Visit`] implementation that wraps `inner` so that /// each formatted field is separated by the provided `delimiter`. /// /// [`Visit`]: tracing_core::field::Visit pub fn new(delimiter: D, inner: V) -> Self { Self { delimiter, inner, seen: false, err: Ok(()), } } fn delimit(&mut self) where V: VisitFmt, D: AsRef, { if self.err.is_err() { return; } if self.seen { self.err = self.inner.writer().write_str(self.delimiter.as_ref()); } self.seen = true; } } impl Visit for VisitDelimited where V: VisitFmt, D: AsRef, { fn record_i64(&mut self, field: &Field, value: i64) { self.delimit(); self.inner.record_i64(field, value); } fn record_u64(&mut self, field: &Field, value: u64) { self.delimit(); self.inner.record_u64(field, value); } fn record_bool(&mut self, field: &Field, value: bool) { self.delimit(); self.inner.record_bool(field, value); } fn record_str(&mut self, field: &Field, value: &str) { self.delimit(); self.inner.record_str(field, value); } fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { self.delimit(); self.inner.record_debug(field, value); } } impl VisitOutput for VisitDelimited where V: VisitFmt, D: AsRef, { fn finish(self) -> fmt::Result { self.err?; self.inner.finish() } } impl VisitFmt for VisitDelimited where V: VisitFmt, D: AsRef, { fn writer(&mut self) -> &mut dyn fmt::Write { self.inner.writer() } } #[cfg(test)] #[cfg(all(test, feature = "alloc"))] mod test { use super::*; use crate::field::test_util::*; #[test] fn delimited_visitor() { let mut s = String::new(); let visitor = DebugVisitor::new(&mut s); let mut visitor = VisitDelimited::new(", ", visitor); TestAttrs1::with(|attrs| attrs.record(&mut visitor)); visitor.finish().unwrap(); assert_eq!( s.as_str(), "question=\"life, the universe, and everything\", tricky=true, can_you_do_it=true" ); } #[test] fn delimited_new_visitor() { let make = Delimited::new("; ", MakeDebug); TestAttrs1::with(|attrs| { let mut s = String::new(); { let mut v = make.make_visitor(&mut s); attrs.record(&mut v); } assert_eq!( s.as_str(), "question=\"life, the universe, and everything\"; tricky=true; can_you_do_it=true" ); }); TestAttrs2::with(|attrs| { let mut s = String::new(); { let mut v = make.make_visitor(&mut s); attrs.record(&mut v); } assert_eq!( s.as_str(), "question=None; question.answer=42; tricky=true; can_you_do_it=false" ); }); } } tracing-subscriber-0.3.18/src/field/display.rs000064400000000000000000000052461046102023000174010ustar 00000000000000//! `MakeVisitor` wrappers for working with `fmt::Display` fields. use super::{MakeVisitor, VisitFmt, VisitOutput}; use tracing_core::field::{Field, Visit}; use core::fmt; /// A visitor wrapper that ensures any strings named "message" are formatted /// using `fmt::Display` #[derive(Debug, Clone)] pub struct Messages(V); // TODO(eliza): When `error` as a primitive type is stable, add a // `DisplayErrors` wrapper... // === impl Messages === // impl Messages { /// Returns a new [`MakeVisitor`] implementation that will wrap `inner` so /// that any strings named `message` are formatted using `fmt::Display`. /// /// [`MakeVisitor`]: super::MakeVisitor pub fn new(inner: V) -> Self { Messages(inner) } } impl MakeVisitor for Messages where V: MakeVisitor, { type Visitor = Messages; #[inline] fn make_visitor(&self, target: T) -> Self::Visitor { Messages(self.0.make_visitor(target)) } } impl Visit for Messages where V: Visit, { #[inline] fn record_f64(&mut self, field: &Field, value: f64) { self.0.record_f64(field, value) } #[inline] fn record_i64(&mut self, field: &Field, value: i64) { self.0.record_i64(field, value) } #[inline] fn record_u64(&mut self, field: &Field, value: u64) { self.0.record_u64(field, value) } #[inline] fn record_bool(&mut self, field: &Field, value: bool) { self.0.record_bool(field, value) } /// Visit a string value. fn record_str(&mut self, field: &Field, value: &str) { if field.name() == "message" { self.0.record_debug(field, &format_args!("{}", value)) } else { self.0.record_str(field, value) } } // TODO(eliza): add RecordError when stable // fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { // self.record_debug(field, &format_args!("{}", value)) // } #[inline] fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { self.0.record_debug(field, value) } } impl VisitOutput for Messages where V: VisitOutput, { #[inline] fn finish(self) -> O { self.0.finish() } } feature! { #![feature = "std"] use super::VisitWrite; use std::io; impl VisitWrite for Messages where V: VisitWrite, { #[inline] fn writer(&mut self) -> &mut dyn io::Write { self.0.writer() } } } impl VisitFmt for Messages where V: VisitFmt, { #[inline] fn writer(&mut self) -> &mut dyn fmt::Write { self.0.writer() } } tracing-subscriber-0.3.18/src/field/mod.rs000064400000000000000000000251451046102023000165130ustar 00000000000000//! Utilities for working with [fields] and [field visitors]. //! //! [fields]: tracing_core::field //! [field visitors]: tracing_core::field::Visit use core::{fmt, marker::PhantomData}; pub use tracing_core::field::Visit; use tracing_core::{ span::{Attributes, Record}, Event, }; pub mod debug; pub mod delimited; pub mod display; /// Creates new [visitors]. /// /// A type implementing `MakeVisitor` represents a composable factory for types /// implementing the [`Visit` trait][visitors]. The `MakeVisitor` trait defines /// a single function, `make_visitor`, which takes in a `T`-typed `target` and /// returns a type implementing `Visit` configured for that target. A target may /// be a string, output stream, or data structure that the visitor will record /// data to, configuration variables that determine the visitor's behavior, or /// `()` when no input is required to produce a visitor. /// /// [visitors]: tracing_core::field::Visit pub trait MakeVisitor { /// The visitor type produced by this `MakeVisitor`. type Visitor: Visit; /// Make a new visitor for the provided `target`. fn make_visitor(&self, target: T) -> Self::Visitor; } /// A [visitor] that produces output once it has visited a set of fields. /// /// [visitor]: tracing_core::field::Visit pub trait VisitOutput: Visit { /// Completes the visitor, returning any output. /// /// This is called once a full set of fields has been visited. fn finish(self) -> Out; /// Visit a set of fields, and return the output of finishing the visitor /// once the fields have been visited. fn visit(mut self, fields: &R) -> Out where R: RecordFields, Self: Sized, { fields.record(&mut self); self.finish() } } /// 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`] /// types. These types all provide inherent `record` methods that allow a /// visitor to record their fields, but there is no common trait representing this. /// /// With `RecordFields`, we can write code like this: /// ``` /// use tracing_core::field::Visit; /// # use tracing_core::field::Field; /// use tracing_subscriber::field::RecordFields; /// /// struct MyVisitor { /// // ... /// } /// # impl MyVisitor { fn new() -> Self { Self{} } } /// impl Visit for MyVisitor { /// // ... /// # fn record_debug(&mut self, _: &Field, _: &dyn std::fmt::Debug) {} /// } /// /// fn record_with_my_visitor(r: R) /// where /// R: RecordFields, /// { /// let mut visitor = MyVisitor::new(); /// r.record(&mut visitor); /// } /// ``` /// [visitor]: tracing_core::field::Visit /// [attr]: tracing_core::span::Attributes /// [rec]: tracing_core::span::Record pub trait RecordFields: crate::sealed::Sealed { /// Record all the fields in `self` with the provided `visitor`. fn record(&self, visitor: &mut dyn Visit); } /// Extension trait implemented for all `MakeVisitor` implementations that /// produce a visitor implementing `VisitOutput`. pub trait MakeOutput where Self: MakeVisitor + crate::sealed::Sealed<(T, Out)>, Self::Visitor: VisitOutput, { /// Visits all fields in `fields` with a new visitor constructed from /// `target`. fn visit_with(&self, target: T, fields: &F) -> Out where F: RecordFields, { self.make_visitor(target).visit(fields) } } feature! { #![feature = "std"] use std::io; /// Extension trait implemented by visitors to indicate that they write to an /// `io::Write` instance, and allow access to that writer. pub trait VisitWrite: VisitOutput> { /// Returns the writer that this visitor writes to. fn writer(&mut self) -> &mut dyn io::Write; } } /// Extension trait implemented by visitors to indicate that they write to a /// `fmt::Write` instance, and allow access to that writer. pub trait VisitFmt: VisitOutput { /// Returns the formatter that this visitor writes to. fn writer(&mut self) -> &mut dyn fmt::Write; } /// Extension trait providing `MakeVisitor` combinators. pub trait MakeExt where Self: MakeVisitor + Sized, Self: crate::sealed::Sealed>, { /// Wraps `self` so that any `fmt::Debug` fields are recorded using the /// alternate formatter (`{:#?}`). fn debug_alt(self) -> debug::Alt { debug::Alt::new(self) } /// Wraps `self` so that any string fields named "message" are recorded /// using `fmt::Display`. fn display_messages(self) -> display::Messages { display::Messages::new(self) } /// Wraps `self` so that when fields are formatted to a writer, they are /// separated by the provided `delimiter`. fn delimited(self, delimiter: D) -> delimited::Delimited where D: AsRef + Clone, Self::Visitor: VisitFmt, { delimited::Delimited::new(delimiter, self) } } // === impl RecordFields === impl<'a> crate::sealed::Sealed for Event<'a> {} impl<'a> RecordFields for Event<'a> { fn record(&self, visitor: &mut dyn Visit) { Event::record(self, visitor) } } impl<'a> crate::sealed::Sealed for Attributes<'a> {} impl<'a> RecordFields for Attributes<'a> { fn record(&self, visitor: &mut dyn Visit) { Attributes::record(self, visitor) } } impl<'a> crate::sealed::Sealed for Record<'a> {} impl<'a> RecordFields for Record<'a> { fn record(&self, visitor: &mut dyn Visit) { Record::record(self, visitor) } } impl<'a, F> crate::sealed::Sealed for &'a F where F: RecordFields {} impl<'a, F> RecordFields for &'a F where F: RecordFields, { fn record(&self, visitor: &mut dyn Visit) { F::record(*self, visitor) } } // === blanket impls === impl MakeVisitor for F where F: Fn(T) -> V, V: Visit, { type Visitor = V; fn make_visitor(&self, target: T) -> Self::Visitor { (self)(target) } } impl crate::sealed::Sealed<(T, Out)> for M where M: MakeVisitor, M::Visitor: VisitOutput, { } impl MakeOutput for M where M: MakeVisitor, M::Visitor: VisitOutput, { } impl crate::sealed::Sealed> for M where M: MakeVisitor + Sized {} impl MakeExt for M where M: MakeVisitor + Sized, M: crate::sealed::Sealed>, { } #[derive(Debug)] #[doc(hidden)] pub struct MakeExtMarker { _p: PhantomData, } #[derive(Debug)] #[doc(hidden)] pub struct RecordFieldsMarker { _p: (), } #[cfg(all(test, feature = "alloc"))] #[macro_use] pub(in crate::field) mod test_util { use super::*; pub(in crate::field) use alloc::string::String; use tracing_core::{ callsite::Callsite, field::{Field, Value}, metadata::{Kind, Level, Metadata}, }; pub(crate) struct TestAttrs1; pub(crate) struct TestAttrs2; impl TestAttrs1 { pub(crate) fn with(f: impl FnOnce(Attributes<'_>) -> T) -> T { let fieldset = TEST_META_1.fields(); let values = &[ ( &fieldset.field("question").unwrap(), Some(&"life, the universe, and everything" as &dyn Value), ), (&fieldset.field("question.answer").unwrap(), None), ( &fieldset.field("tricky").unwrap(), Some(&true as &dyn Value), ), ( &fieldset.field("can_you_do_it").unwrap(), Some(&true as &dyn Value), ), ]; let valueset = fieldset.value_set(values); let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); f(attrs) } } impl TestAttrs2 { pub(crate) fn with(f: impl FnOnce(Attributes<'_>) -> T) -> T { let fieldset = TEST_META_1.fields(); let none = tracing_core::field::debug(&Option::<&str>::None); let values = &[ ( &fieldset.field("question").unwrap(), Some(&none as &dyn Value), ), ( &fieldset.field("question.answer").unwrap(), Some(&42 as &dyn Value), ), ( &fieldset.field("tricky").unwrap(), Some(&true as &dyn Value), ), ( &fieldset.field("can_you_do_it").unwrap(), Some(&false as &dyn Value), ), ]; let valueset = fieldset.value_set(values); let attrs = tracing_core::span::Attributes::new(&TEST_META_1, &valueset); f(attrs) } } struct TestCallsite1; static TEST_CALLSITE_1: &'static dyn Callsite = &TestCallsite1; static TEST_META_1: Metadata<'static> = tracing_core::metadata! { name: "field_test1", target: module_path!(), level: Level::INFO, fields: &["question", "question.answer", "tricky", "can_you_do_it"], callsite: TEST_CALLSITE_1, kind: Kind::SPAN, }; impl Callsite for TestCallsite1 { fn set_interest(&self, _: tracing_core::subscriber::Interest) { unimplemented!() } fn metadata(&self) -> &Metadata<'_> { &TEST_META_1 } } pub(crate) struct MakeDebug; pub(crate) struct DebugVisitor<'a> { writer: &'a mut dyn fmt::Write, err: fmt::Result, } impl<'a> DebugVisitor<'a> { pub(crate) fn new(writer: &'a mut dyn fmt::Write) -> Self { Self { writer, err: Ok(()), } } } impl<'a> Visit for DebugVisitor<'a> { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { write!(self.writer, "{}={:?}", field, value).unwrap(); } } impl<'a> VisitOutput for DebugVisitor<'a> { fn finish(self) -> fmt::Result { self.err } } impl<'a> VisitFmt for DebugVisitor<'a> { fn writer(&mut self) -> &mut dyn fmt::Write { self.writer } } impl<'a> MakeVisitor<&'a mut dyn fmt::Write> for MakeDebug { type Visitor = DebugVisitor<'a>; fn make_visitor(&self, w: &'a mut dyn fmt::Write) -> DebugVisitor<'a> { DebugVisitor::new(w) } } } tracing-subscriber-0.3.18/src/filter/directive.rs000064400000000000000000000324411046102023000201110ustar 00000000000000use crate::filter::level::{self, LevelFilter}; #[cfg(not(feature = "smallvec"))] use alloc::vec; #[cfg(not(feature = "std"))] use alloc::{string::String, vec::Vec}; use core::{cmp::Ordering, fmt, iter::FromIterator, slice, str::FromStr}; use tracing_core::{Level, Metadata}; /// Indicates that a string could not be parsed as a filtering directive. #[derive(Debug)] pub struct ParseError { kind: ParseErrorKind, } /// A directive which will statically enable or disable a given callsite. /// /// Unlike a dynamic directive, this can be cached by the callsite. #[derive(Debug, PartialEq, Eq, Clone)] pub(crate) struct StaticDirective { pub(in crate::filter) target: Option, pub(in crate::filter) field_names: Vec, pub(in crate::filter) level: LevelFilter, } #[cfg(feature = "smallvec")] pub(crate) type FilterVec = smallvec::SmallVec<[T; 8]>; #[cfg(not(feature = "smallvec"))] pub(crate) type FilterVec = Vec; #[derive(Debug, PartialEq, Clone)] pub(in crate::filter) struct DirectiveSet { directives: FilterVec, pub(in crate::filter) max_level: LevelFilter, } pub(in crate::filter) trait Match { fn cares_about(&self, meta: &Metadata<'_>) -> bool; fn level(&self) -> &LevelFilter; } #[derive(Debug)] enum ParseErrorKind { #[cfg(feature = "std")] Field(Box), Level(level::ParseError), Other(Option<&'static str>), } // === impl DirectiveSet === impl DirectiveSet { // 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() } pub(crate) fn iter(&self) -> slice::Iter<'_, T> { self.directives.iter() } } impl Default for DirectiveSet { fn default() -> Self { Self { directives: FilterVec::new(), max_level: LevelFilter::OFF, } } } impl DirectiveSet { pub(crate) fn directives(&self) -> impl Iterator { self.directives.iter() } pub(crate) fn directives_for<'a>( &'a self, metadata: &'a Metadata<'a>, ) -> impl Iterator + 'a { self.directives().filter(move |d| d.cares_about(metadata)) } pub(crate) fn add(&mut self, directive: T) { // does this directive enable a more verbose level than the current // max? if so, update the max level. let level = *directive.level(); if level > self.max_level { self.max_level = level; } // insert the directive into the vec of directives, ordered by // specificity (length of target + number of field filters). this // ensures that, when finding a directive to match a span or event, we // search the directive set in most specific first order. match self.directives.binary_search(&directive) { Ok(i) => self.directives[i] = directive, Err(i) => self.directives.insert(i, directive), } } #[cfg(test)] pub(in crate::filter) fn into_vec(self) -> FilterVec { self.directives } } impl FromIterator for DirectiveSet { fn from_iter>(iter: I) -> Self { let mut this = Self::default(); this.extend(iter); this } } impl Extend for DirectiveSet { fn extend>(&mut self, iter: I) { for directive in iter.into_iter() { self.add(directive); } } } impl IntoIterator for DirectiveSet { type Item = T; #[cfg(feature = "smallvec")] type IntoIter = smallvec::IntoIter<[T; 8]>; #[cfg(not(feature = "smallvec"))] type IntoIter = vec::IntoIter; fn into_iter(self) -> Self::IntoIter { self.directives.into_iter() } } // === impl Statics === impl DirectiveSet { pub(crate) fn enabled(&self, meta: &Metadata<'_>) -> bool { let level = meta.level(); match self.directives_for(meta).next() { Some(d) => d.level >= *level, None => false, } } /// Same as `enabled` above, but skips `Directive`'s with fields. pub(crate) fn target_enabled(&self, target: &str, level: &Level) -> bool { match self.directives_for_target(target).next() { Some(d) => d.level >= *level, None => false, } } pub(crate) fn directives_for_target<'a>( &'a self, target: &'a str, ) -> impl Iterator + 'a { self.directives() .filter(move |d| d.cares_about_target(target)) } } // === impl StaticDirective === impl StaticDirective { pub(in crate::filter) fn new( target: Option, field_names: Vec, level: LevelFilter, ) -> Self { Self { target, field_names, level, } } pub(in crate::filter) fn cares_about_target(&self, to_check: &str) -> bool { // Does this directive have a target filter, and does it match the // metadata's target? if let Some(ref target) = self.target { if !to_check.starts_with(&target[..]) { return false; } } if !self.field_names.is_empty() { return false; } true } } impl Ord for StaticDirective { fn cmp(&self, other: &StaticDirective) -> Ordering { // We attempt to order directives by how "specific" they are. This // ensures that we try the most specific directives first when // attempting to match a piece of metadata. // First, we compare based on whether a target is specified, and the // lengths of those targets if both have targets. let ordering = self .target .as_ref() .map(String::len) .cmp(&other.target.as_ref().map(String::len)) // Then we compare how many field names are matched by each directive. .then_with(|| self.field_names.len().cmp(&other.field_names.len())) // Finally, we fall back to lexicographical ordering if the directives are // equally specific. Although this is no longer semantically important, // we need to define a total ordering to determine the directive's place // in the BTreeMap. .then_with(|| { self.target .cmp(&other.target) .then_with(|| self.field_names[..].cmp(&other.field_names[..])) }) .reverse(); #[cfg(debug_assertions)] { if ordering == Ordering::Equal { debug_assert_eq!( self.target, other.target, "invariant violated: Ordering::Equal must imply a.target == b.target" ); debug_assert_eq!( self.field_names, other.field_names, "invariant violated: Ordering::Equal must imply a.field_names == b.field_names" ); } } ordering } } impl PartialOrd for StaticDirective { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Match for StaticDirective { fn cares_about(&self, meta: &Metadata<'_>) -> bool { // Does this directive have a target filter, and does it match the // metadata's target? if let Some(ref target) = self.target { if !meta.target().starts_with(&target[..]) { return false; } } if meta.is_event() && !self.field_names.is_empty() { let fields = meta.fields(); for name in &self.field_names { if fields.field(name).is_none() { return false; } } } true } fn level(&self) -> &LevelFilter { &self.level } } impl Default for StaticDirective { fn default() -> Self { StaticDirective { target: None, field_names: Vec::new(), level: LevelFilter::ERROR, } } } impl fmt::Display for StaticDirective { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut wrote_any = false; if let Some(ref target) = self.target { fmt::Display::fmt(target, f)?; wrote_any = true; } if !self.field_names.is_empty() { f.write_str("[")?; let mut fields = self.field_names.iter(); if let Some(field) = fields.next() { write!(f, "{{{}", field)?; for field in fields { write!(f, ",{}", field)?; } f.write_str("}")?; } f.write_str("]")?; wrote_any = true; } if wrote_any { f.write_str("=")?; } fmt::Display::fmt(&self.level, f) } } impl FromStr for StaticDirective { type Err = ParseError; fn from_str(s: &str) -> Result { // This method parses a filtering directive in one of the following // forms: // // * `foo=trace` (TARGET=LEVEL) // * `foo[{bar,baz}]=info` (TARGET[{FIELD,+}]=LEVEL) // * `trace` (bare LEVEL) // * `foo` (bare TARGET) let mut split = s.split('='); let part0 = split .next() .ok_or_else(|| ParseError::msg("string must not be empty"))?; // Directive includes an `=`: // * `foo=trace` // * `foo[{bar}]=trace` // * `foo[{bar,baz}]=trace` if let Some(part1) = split.next() { if split.next().is_some() { return Err(ParseError::msg( "too many '=' in filter directive, expected 0 or 1", )); } let mut split = part0.split("[{"); let target = split.next().map(String::from); let mut field_names = Vec::new(); // Directive includes fields: // * `foo[{bar}]=trace` // * `foo[{bar,baz}]=trace` if let Some(maybe_fields) = split.next() { if split.next().is_some() { return Err(ParseError::msg( "too many '[{' in filter directive, expected 0 or 1", )); } if !maybe_fields.ends_with("}]") { return Err(ParseError::msg("expected fields list to end with '}]'")); } let fields = maybe_fields .trim_end_matches("}]") .split(',') .filter_map(|s| { if s.is_empty() { None } else { Some(String::from(s)) } }); field_names.extend(fields); }; let level = part1.parse()?; return Ok(Self { level, field_names, target, }); } // Okay, the part after the `=` was empty, the directive is either a // bare level or a bare target. // * `foo` // * `info` Ok(match part0.parse::() { Ok(level) => Self { level, target: None, field_names: Vec::new(), }, Err(_) => Self { target: Some(String::from(part0)), level: LevelFilter::TRACE, field_names: Vec::new(), }, }) } } // === impl ParseError === impl ParseError { #[cfg(all(feature = "std", feature = "env-filter"))] pub(crate) fn new() -> Self { ParseError { kind: ParseErrorKind::Other(None), } } pub(crate) fn msg(s: &'static str) -> Self { ParseError { kind: ParseErrorKind::Other(Some(s)), } } } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { ParseErrorKind::Other(None) => f.pad("invalid filter directive"), ParseErrorKind::Other(Some(msg)) => write!(f, "invalid filter directive: {}", msg), ParseErrorKind::Level(ref l) => l.fmt(f), #[cfg(feature = "std")] ParseErrorKind::Field(ref e) => write!(f, "invalid field filter: {}", e), } } } #[cfg(feature = "std")] impl std::error::Error for ParseError { fn description(&self) -> &str { "invalid filter directive" } fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self.kind { ParseErrorKind::Other(_) => None, ParseErrorKind::Level(ref l) => Some(l), ParseErrorKind::Field(ref n) => Some(n.as_ref()), } } } #[cfg(feature = "std")] impl From> for ParseError { fn from(e: Box) -> Self { Self { kind: ParseErrorKind::Field(e), } } } impl From for ParseError { fn from(l: level::ParseError) -> Self { Self { kind: ParseErrorKind::Level(l), } } } tracing-subscriber-0.3.18/src/filter/env/builder.rs000064400000000000000000000302231046102023000203450ustar 00000000000000use super::{ directive::{self, Directive}, EnvFilter, FromEnvError, }; use crate::sync::RwLock; use std::env; use thread_local::ThreadLocal; use tracing::level_filters::STATIC_MAX_LEVEL; /// A [builder] for constructing new [`EnvFilter`]s. /// /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html #[derive(Debug, Clone)] #[must_use] pub struct Builder { regex: bool, env: Option, default_directive: Option, } impl Builder { /// Sets whether span field values can be matched with regular expressions. /// /// If this is `true`, field filter directives will be interpreted as /// regular expressions if they are not able to be interpreted as a `bool`, /// `i64`, `u64`, or `f64` literal. If this is `false,` those field values /// will be interpreted as literal [`std::fmt::Debug`] output instead. /// /// By default, regular expressions are enabled. /// /// **Note**: when [`EnvFilter`]s are constructed from untrusted inputs, /// disabling regular expressions is strongly encouraged. pub fn with_regex(self, regex: bool) -> Self { Self { regex, ..self } } /// Sets a default [filtering directive] that will be added to the filter if /// the parsed string or environment variable contains no filter directives. /// /// By default, there is no default directive. /// /// # Examples /// /// If [`parse`], [`parse_lossy`], [`from_env`], or [`from_env_lossy`] are /// called with an empty string or environment variable, the default /// directive is used instead: /// /// ```rust /// # fn main() -> Result<(), Box> { /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// let filter = EnvFilter::builder() /// .with_default_directive(LevelFilter::INFO.into()) /// .parse("")?; /// /// assert_eq!(format!("{}", filter), "info"); /// # Ok(()) } /// ``` /// /// Note that the `lossy` variants ([`parse_lossy`] and [`from_env_lossy`]) /// will ignore any invalid directives. If all directives in a filter /// string or environment variable are invalid, those methods will also use /// the default directive: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// let filter = EnvFilter::builder() /// .with_default_directive(LevelFilter::INFO.into()) /// .parse_lossy("some_target=fake level,foo::bar=lolwut"); /// /// assert_eq!(format!("{}", filter), "info"); /// ``` /// /// /// If the string or environment variable contains valid filtering /// directives, the default directive is not used: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// let filter = EnvFilter::builder() /// .with_default_directive(LevelFilter::INFO.into()) /// .parse_lossy("foo=trace"); /// /// // The default directive is *not* used: /// assert_eq!(format!("{}", filter), "foo=trace"); /// ``` /// /// Parsing a more complex default directive from a string: /// /// ```rust /// # fn main() -> Result<(), Box> { /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// let default = "myapp=debug".parse() /// .expect("hard-coded default directive should be valid"); /// /// let filter = EnvFilter::builder() /// .with_default_directive(default) /// .parse("")?; /// /// assert_eq!(format!("{}", filter), "myapp=debug"); /// # Ok(()) } /// ``` /// /// [`parse_lossy`]: Self::parse_lossy /// [`from_env_lossy`]: Self::from_env_lossy /// [`parse`]: Self::parse /// [`from_env`]: Self::from_env pub fn with_default_directive(self, default_directive: Directive) -> Self { Self { default_directive: Some(default_directive), ..self } } /// Sets the name of the environment variable used by the [`from_env`], /// [`from_env_lossy`], and [`try_from_env`] methods. /// /// By default, this is the value of [`EnvFilter::DEFAULT_ENV`] /// (`RUST_LOG`). /// /// [`from_env`]: Self::from_env /// [`from_env_lossy`]: Self::from_env_lossy /// [`try_from_env`]: Self::try_from_env pub fn with_env_var(self, var: impl ToString) -> Self { Self { env: Some(var.to_string()), ..self } } /// Returns a new [`EnvFilter`] from the directives in the given string, /// *ignoring* any that are invalid. pub fn parse_lossy>(&self, dirs: S) -> EnvFilter { let directives = dirs .as_ref() .split(',') .filter(|s| !s.is_empty()) .filter_map(|s| match Directive::parse(s, self.regex) { Ok(d) => Some(d), Err(err) => { eprintln!("ignoring `{}`: {}", s, err); None } }); self.from_directives(directives) } /// Returns a new [`EnvFilter`] from the directives in the given string, /// or an error if any are invalid. pub fn parse>(&self, dirs: S) -> Result { let dirs = dirs.as_ref(); if dirs.is_empty() { return Ok(self.from_directives(std::iter::empty())); } let directives = dirs .split(',') .filter(|s| !s.is_empty()) .map(|s| Directive::parse(s, self.regex)) .collect::, _>>()?; Ok(self.from_directives(directives)) } /// Returns a new [`EnvFilter`] from the directives in the configured /// environment variable, ignoring any directives that are invalid. pub fn from_env_lossy(&self) -> EnvFilter { let var = env::var(self.env_var_name()).unwrap_or_default(); self.parse_lossy(var) } /// 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 configured /// environment variable, or an error if the environment variable is not set /// or contains invalid directives. pub fn try_from_env(&self) -> Result { let var = env::var(self.env_var_name())?; self.parse(var).map_err(Into::into) } // TODO(eliza): consider making this a public API? // Clippy doesn't love this naming, because it suggests that `from_` methods // should not take a `Self`...but in this case, it's the `EnvFilter` that is // being constructed "from" the directives, rather than the builder itself. #[allow(clippy::wrong_self_convention)] pub(super) fn from_directives( &self, directives: impl IntoIterator, ) -> EnvFilter { use tracing::Level; let mut directives: Vec<_> = directives.into_iter().collect(); let mut disabled = Vec::new(); for directive in &mut directives { if directive.level > STATIC_MAX_LEVEL { disabled.push(directive.clone()); } if !self.regex { directive.deregexify(); } } if !disabled.is_empty() { #[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 subscriber, which // may or may not use a `MakeWriter`. let warn = |msg: &str| { #[cfg(not(feature = "nu_ansi_term"))] let msg = format!("warning: {}", msg); #[cfg(feature = "nu_ansi_term")] let msg = { let bold = Style::new().bold(); let mut warning = Color::Yellow.paint("warning"); warning.style_ref_mut().is_bold = true; format!("{}{} {}", warning, bold.paint(":"), bold.paint(msg)) }; eprintln!("{}", msg); }; let ctx_prefixed = |prefix: &str, msg: &str| { #[cfg(not(feature = "nu_ansi_term"))] let msg = format!("{} {}", prefix, msg); #[cfg(feature = "nu_ansi_term")] let msg = { let mut equal = Color::Fixed(21).paint("="); // dark blue equal.style_ref_mut().is_bold = true; format!(" {} {} {}", equal, Style::new().bold().paint(prefix), msg) }; eprintln!("{}", msg); }; 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"))] let msg = format!("note: {}", msg); #[cfg(feature = "nu_ansi_term")] let msg = { let mut pipe = Color::Fixed(21).paint("|"); pipe.style_ref_mut().is_bold = true; format!(" {} {}", pipe, msg) }; eprintln!("{}", msg); }; warn("some trace filter directives would enable traces that are disabled statically"); for directive in disabled { let target = if let Some(target) = &directive.target { format!("the `{}` target", target) } else { "all targets".into() }; let level = directive .level .into_level() .expect("=off would not have enabled any filters"); ctx(&format!( "`{}` would enable the {} level for {}", directive, level, target )); } ctx_note(&format!("the static max level is `{}`", STATIC_MAX_LEVEL)); let help_msg = || { let (feature, filter) = match STATIC_MAX_LEVEL.into_level() { Some(Level::TRACE) => unreachable!( "if the max level is trace, no static filtering features are enabled" ), Some(Level::DEBUG) => ("max_level_debug", Level::TRACE), Some(Level::INFO) => ("max_level_info", Level::DEBUG), Some(Level::WARN) => ("max_level_warn", Level::INFO), Some(Level::ERROR) => ("max_level_error", Level::WARN), None => return ("max_level_off", String::new()), }; (feature, format!("{} ", filter)) }; let (feature, earlier_level) = help_msg(); ctx_help(&format!( "to enable {}logging, remove the `{}` feature from the `tracing` crate", earlier_level, feature )); } let (dynamics, statics) = Directive::make_tables(directives); let has_dynamics = !dynamics.is_empty(); let mut filter = EnvFilter { statics, dynamics, has_dynamics, by_id: RwLock::new(Default::default()), by_cs: RwLock::new(Default::default()), scope: ThreadLocal::new(), regex: self.regex, }; if !has_dynamics && filter.statics.is_empty() { if let Some(ref default) = self.default_directive { filter = filter.add_directive(default.clone()); } } filter } fn env_var_name(&self) -> &str { self.env.as_deref().unwrap_or(EnvFilter::DEFAULT_ENV) } } impl Default for Builder { fn default() -> Self { Self { regex: true, env: None, default_directive: None, } } } tracing-subscriber-0.3.18/src/filter/env/directive.rs000064400000000000000000000715051046102023000207050ustar 00000000000000pub(crate) use crate::filter::directive::{FilterVec, ParseError, StaticDirective}; use crate::filter::{ directive::{DirectiveSet, Match}, env::{field, FieldMap}, level::LevelFilter, }; use once_cell::sync::Lazy; use regex::Regex; use std::{cmp::Ordering, fmt, iter::FromIterator, str::FromStr}; use tracing_core::{span, Level, Metadata}; /// A single filtering directive. // TODO(eliza): add a builder for programmatically constructing directives? #[derive(Clone, Debug, Eq, PartialEq)] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] pub struct Directive { in_span: Option, fields: Vec, pub(crate) target: Option, pub(crate) level: LevelFilter, } /// A set of dynamic filtering directives. pub(super) type Dynamics = DirectiveSet; /// A set of static filtering directives. pub(super) type Statics = DirectiveSet; pub(crate) type CallsiteMatcher = MatchSet; pub(crate) type SpanMatcher = MatchSet; #[derive(Debug, PartialEq, Eq)] pub(crate) struct MatchSet { field_matches: FilterVec, base_level: LevelFilter, } impl Directive { pub(super) fn has_name(&self) -> bool { self.in_span.is_some() } pub(super) fn has_fields(&self) -> bool { !self.fields.is_empty() } pub(super) fn to_static(&self) -> Option { if !self.is_static() { return None; } // TODO(eliza): these strings are all immutable; we should consider // `Arc`ing them to make this more efficient... let field_names = self.fields.iter().map(field::Match::name).collect(); Some(StaticDirective::new( self.target.clone(), field_names, self.level, )) } fn is_static(&self) -> bool { !self.has_name() && !self.fields.iter().any(field::Match::has_value) } pub(super) fn is_dynamic(&self) -> bool { self.has_name() || self.has_fields() } pub(crate) fn field_matcher(&self, meta: &Metadata<'_>) -> Option { let fieldset = meta.fields(); let fields = self .fields .iter() .filter_map( |field::Match { ref name, ref value, }| { if let Some(field) = fieldset.field(name) { let value = value.as_ref().cloned()?; Some(Ok((field, value))) } else { Some(Err(())) } }, ) .collect::, ()>>() .ok()?; Some(field::CallsiteMatch { fields, level: self.level, }) } pub(super) fn make_tables( directives: impl IntoIterator, ) -> (Dynamics, Statics) { // TODO(eliza): this could be made more efficient... let (dyns, stats): (Vec, Vec) = directives.into_iter().partition(Directive::is_dynamic); let statics = stats .into_iter() .filter_map(|d| d.to_static()) .chain(dyns.iter().filter_map(Directive::to_static)) .collect(); (Dynamics::from_iter(dyns), statics) } pub(super) fn deregexify(&mut self) { for field in &mut self.fields { field.value = match field.value.take() { Some(field::ValueMatch::Pat(pat)) => { Some(field::ValueMatch::Debug(pat.into_debug_match())) } x => x, } } } pub(super) fn parse(from: &str, regex: bool) -> Result { static DIRECTIVE_RE: Lazy = Lazy::new(|| { Regex::new( r"(?x) ^(?P(?i:trace|debug|info|warn|error|off|[0-5]))$ | # ^^^. # `note: we match log level names case-insensitively ^ (?: # target name or span name (?P[\w:-]+)|(?P\[[^\]]*\]) ){1,2} (?: # level or nothing =(?P(?i:trace|debug|info|warn|error|off|[0-5]))? # ^^^. # `note: we match log level names case-insensitively )? $ ", ) .unwrap() }); static SPAN_PART_RE: Lazy = Lazy::new(|| Regex::new(r"(?P[^\]\{]+)?(?:\{(?P[^\}]*)\})?").unwrap()); static FIELD_FILTER_RE: Lazy = // TODO(eliza): this doesn't _currently_ handle value matchers that include comma // characters. We should fix that. Lazy::new(|| { Regex::new( r"(?x) ( # field name [[:word:]][[[:word:]]\.]* # value part (optional) (?:=[^,]+)? ) # trailing comma or EOS (?:,\s?|$) ", ) .unwrap() }); let caps = DIRECTIVE_RE.captures(from).ok_or_else(ParseError::new)?; if let Some(level) = caps .name("global_level") .and_then(|s| s.as_str().parse().ok()) { return Ok(Directive { level, ..Default::default() }); } let target = caps.name("target").and_then(|c| { let s = c.as_str(); if s.parse::().is_ok() { None } else { Some(s.to_owned()) } }); let (in_span, fields) = caps .name("span") .and_then(|cap| { let cap = cap.as_str().trim_matches(|c| c == '[' || c == ']'); let caps = SPAN_PART_RE.captures(cap)?; let span = caps.name("name").map(|c| c.as_str().to_owned()); let fields = caps .name("fields") .map(|c| { FIELD_FILTER_RE .find_iter(c.as_str()) .map(|c| field::Match::parse(c.as_str(), regex)) .collect::, _>>() }) .unwrap_or_else(|| Ok(Vec::new())); Some((span, fields)) }) .unwrap_or_else(|| (None, Ok(Vec::new()))); let level = caps .name("level") .and_then(|l| l.as_str().parse().ok()) // Setting the target without the level enables every level for that target .unwrap_or(LevelFilter::TRACE); Ok(Self { level, target, in_span, fields: fields?, }) } } impl Match for Directive { fn cares_about(&self, meta: &Metadata<'_>) -> bool { // Does this directive have a target filter, and does it match the // metadata's target? if let Some(ref target) = self.target { if !meta.target().starts_with(&target[..]) { return false; } } // Do we have a name filter, and does it match the metadata's name? // TODO(eliza): put name globbing here? if let Some(ref name) = self.in_span { if name != meta.name() { return false; } } // Does the metadata define all the fields that this directive cares about? let actual_fields = meta.fields(); for expected_field in &self.fields { // Does the actual field set (from the metadata) contain this field? if actual_fields.field(&expected_field.name).is_none() { return false; } } true } fn level(&self) -> &LevelFilter { &self.level } } impl FromStr for Directive { type Err = ParseError; fn from_str(from: &str) -> Result { Directive::parse(from, true) } } impl Default for Directive { fn default() -> Self { Directive { level: LevelFilter::OFF, target: None, in_span: None, fields: Vec::new(), } } } impl PartialOrd for Directive { fn partial_cmp(&self, other: &Directive) -> Option { Some(self.cmp(other)) } } impl Ord for Directive { fn cmp(&self, other: &Directive) -> Ordering { // We attempt to order directives by how "specific" they are. This // ensures that we try the most specific directives first when // attempting to match a piece of metadata. // First, we compare based on whether a target is specified, and the // lengths of those targets if both have targets. let ordering = self .target .as_ref() .map(String::len) .cmp(&other.target.as_ref().map(String::len)) // Next compare based on the presence of span names. .then_with(|| self.in_span.is_some().cmp(&other.in_span.is_some())) // Then we compare how many fields are defined by each // directive. .then_with(|| self.fields.len().cmp(&other.fields.len())) // Finally, we fall back to lexicographical ordering if the directives are // equally specific. Although this is no longer semantically important, // we need to define a total ordering to determine the directive's place // in the BTreeMap. .then_with(|| { self.target .cmp(&other.target) .then_with(|| self.in_span.cmp(&other.in_span)) .then_with(|| self.fields[..].cmp(&other.fields[..])) }) .reverse(); #[cfg(debug_assertions)] { if ordering == Ordering::Equal { debug_assert_eq!( self.target, other.target, "invariant violated: Ordering::Equal must imply a.target == b.target" ); debug_assert_eq!( self.in_span, other.in_span, "invariant violated: Ordering::Equal must imply a.in_span == b.in_span" ); debug_assert_eq!( self.fields, other.fields, "invariant violated: Ordering::Equal must imply a.fields == b.fields" ); } } ordering } } impl fmt::Display for Directive { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut wrote_any = false; if let Some(ref target) = self.target { fmt::Display::fmt(target, f)?; wrote_any = true; } if self.in_span.is_some() || !self.fields.is_empty() { f.write_str("[")?; if let Some(ref span) = self.in_span { fmt::Display::fmt(span, f)?; } let mut fields = self.fields.iter(); if let Some(field) = fields.next() { write!(f, "{{{}", field)?; for field in fields { write!(f, ",{}", field)?; } f.write_str("}")?; } f.write_str("]")?; wrote_any = true; } if wrote_any { f.write_str("=")?; } fmt::Display::fmt(&self.level, f) } } impl From for Directive { fn from(level: LevelFilter) -> Self { Self { level, ..Self::default() } } } impl From for Directive { fn from(level: Level) -> Self { LevelFilter::from_level(level).into() } } // === impl Dynamics === impl Dynamics { pub(crate) fn matcher(&self, metadata: &Metadata<'_>) -> Option { let mut base_level = None; let field_matches = self .directives_for(metadata) .filter_map(|d| { if let Some(f) = d.field_matcher(metadata) { return Some(f); } match base_level { Some(ref b) if d.level > *b => base_level = Some(d.level), None => base_level = Some(d.level), _ => {} } None }) .collect(); if let Some(base_level) = base_level { Some(CallsiteMatcher { field_matches, base_level, }) } else if !field_matches.is_empty() { Some(CallsiteMatcher { field_matches, base_level: base_level.unwrap_or(LevelFilter::OFF), }) } else { None } } pub(crate) fn has_value_filters(&self) -> bool { self.directives() .any(|d| d.fields.iter().any(|f| f.value.is_some())) } } // ===== impl DynamicMatch ===== impl CallsiteMatcher { /// Create a new `SpanMatch` for a given instance of the matched callsite. pub(crate) fn to_span_match(&self, attrs: &span::Attributes<'_>) -> SpanMatcher { let field_matches = self .field_matches .iter() .map(|m| { let m = m.to_span_match(); attrs.record(&mut m.visitor()); m }) .collect(); SpanMatcher { field_matches, base_level: self.base_level, } } } impl SpanMatcher { /// Returns the level currently enabled for this callsite. pub(crate) fn level(&self) -> LevelFilter { self.field_matches .iter() .filter_map(field::SpanMatch::filter) .max() .unwrap_or(self.base_level) } pub(crate) fn record_update(&self, record: &span::Record<'_>) { for m in &self.field_matches { record.record(&mut m.visitor()) } } } #[cfg(test)] mod test { use super::*; fn parse_directives(dirs: impl AsRef) -> Vec { dirs.as_ref() .split(',') .filter_map(|s| s.parse().ok()) .collect() } fn expect_parse(dirs: impl AsRef) -> Vec { dirs.as_ref() .split(',') .map(|s| { s.parse() .unwrap_or_else(|err| panic!("directive '{:?}' should parse: {}", s, err)) }) .collect() } #[test] fn directive_ordering_by_target_len() { // TODO(eliza): it would be nice to have a property-based test for this // instead. let mut dirs = expect_parse( "foo::bar=debug,foo::bar::baz=trace,foo=info,a_really_long_name_with_no_colons=warn", ); dirs.sort_unstable(); let expected = vec![ "a_really_long_name_with_no_colons", "foo::bar::baz", "foo::bar", "foo", ]; let sorted = dirs .iter() .map(|d| d.target.as_ref().unwrap()) .collect::>(); assert_eq!(expected, sorted); } #[test] fn directive_ordering_by_span() { // TODO(eliza): it would be nice to have a property-based test for this // instead. let mut dirs = expect_parse("bar[span]=trace,foo=debug,baz::quux=info,a[span]=warn"); dirs.sort_unstable(); let expected = vec!["baz::quux", "bar", "foo", "a"]; let sorted = dirs .iter() .map(|d| d.target.as_ref().unwrap()) .collect::>(); assert_eq!(expected, sorted); } #[test] fn directive_ordering_uses_lexicographic_when_equal() { // TODO(eliza): it would be nice to have a property-based test for this // instead. let mut dirs = expect_parse("span[b]=debug,b=debug,a=trace,c=info,span[a]=info"); dirs.sort_unstable(); let expected = vec![ ("span", Some("b")), ("span", Some("a")), ("c", None), ("b", None), ("a", None), ]; let sorted = dirs .iter() .map(|d| { ( d.target.as_ref().unwrap().as_ref(), d.in_span.as_ref().map(String::as_ref), ) }) .collect::>(); assert_eq!(expected, sorted); } // TODO: this test requires the parser to support directives with multiple // fields, which it currently can't handle. We should enable this test when // that's implemented. #[test] #[ignore] fn directive_ordering_by_field_num() { // TODO(eliza): it would be nice to have a property-based test for this // instead. let mut dirs = expect_parse( "b[{foo,bar}]=info,c[{baz,quuux,quuux}]=debug,a[{foo}]=warn,bar[{field}]=trace,foo=debug,baz::quux=info" ); dirs.sort_unstable(); let expected = vec!["baz::quux", "bar", "foo", "c", "b", "a"]; let sorted = dirs .iter() .map(|d| d.target.as_ref().unwrap()) .collect::>(); assert_eq!(expected, sorted); } #[test] fn parse_directives_ralith() { let dirs = parse_directives("common=trace,server=trace"); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("common".to_string())); assert_eq!(dirs[0].level, LevelFilter::TRACE); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("server".to_string())); assert_eq!(dirs[1].level, LevelFilter::TRACE); assert_eq!(dirs[1].in_span, None); } #[test] fn parse_directives_ralith_uc() { let dirs = parse_directives("common=INFO,server=DEBUG"); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("common".to_string())); assert_eq!(dirs[0].level, LevelFilter::INFO); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("server".to_string())); assert_eq!(dirs[1].level, LevelFilter::DEBUG); assert_eq!(dirs[1].in_span, None); } #[test] fn parse_directives_ralith_mixed() { let dirs = parse_directives("common=iNfo,server=dEbUg"); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("common".to_string())); assert_eq!(dirs[0].level, LevelFilter::INFO); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("server".to_string())); assert_eq!(dirs[1].level, LevelFilter::DEBUG); assert_eq!(dirs[1].in_span, None); } #[test] fn parse_directives_valid() { let dirs = parse_directives("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::ERROR); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::TRACE); assert_eq!(dirs[1].in_span, None); assert_eq!(dirs[2].target, Some("crate2".to_string())); assert_eq!(dirs[2].level, LevelFilter::DEBUG); assert_eq!(dirs[2].in_span, None); assert_eq!(dirs[3].target, Some("crate3".to_string())); assert_eq!(dirs[3].level, LevelFilter::OFF); assert_eq!(dirs[3].in_span, None); } #[test] fn parse_level_directives() { let dirs = parse_directives( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ); assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::ERROR); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::WARN); assert_eq!(dirs[1].in_span, None); assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); assert_eq!(dirs[2].level, LevelFilter::INFO); assert_eq!(dirs[2].in_span, None); assert_eq!(dirs[3].target, Some("crate2".to_string())); assert_eq!(dirs[3].level, LevelFilter::DEBUG); assert_eq!(dirs[3].in_span, None); assert_eq!(dirs[4].target, Some("crate3".to_string())); assert_eq!(dirs[4].level, LevelFilter::TRACE); assert_eq!(dirs[4].in_span, None); assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); assert_eq!(dirs[5].level, LevelFilter::OFF); assert_eq!(dirs[5].in_span, None); } #[test] fn parse_uppercase_level_directives() { let dirs = parse_directives( "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", ); assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::ERROR); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::WARN); assert_eq!(dirs[1].in_span, None); assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); assert_eq!(dirs[2].level, LevelFilter::INFO); assert_eq!(dirs[2].in_span, None); assert_eq!(dirs[3].target, Some("crate2".to_string())); assert_eq!(dirs[3].level, LevelFilter::DEBUG); assert_eq!(dirs[3].in_span, None); assert_eq!(dirs[4].target, Some("crate3".to_string())); assert_eq!(dirs[4].level, LevelFilter::TRACE); assert_eq!(dirs[4].in_span, None); assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); assert_eq!(dirs[5].level, LevelFilter::OFF); assert_eq!(dirs[5].in_span, None); } #[test] fn parse_numeric_level_directives() { let dirs = parse_directives( "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ crate3=5,crate3::mod2::mod1=0", ); assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::ERROR); assert_eq!(dirs[0].in_span, None); assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::WARN); assert_eq!(dirs[1].in_span, None); assert_eq!(dirs[2].target, Some("crate1::mod2::mod3".to_string())); assert_eq!(dirs[2].level, LevelFilter::INFO); assert_eq!(dirs[2].in_span, None); assert_eq!(dirs[3].target, Some("crate2".to_string())); assert_eq!(dirs[3].level, LevelFilter::DEBUG); assert_eq!(dirs[3].in_span, None); assert_eq!(dirs[4].target, Some("crate3".to_string())); assert_eq!(dirs[4].level, LevelFilter::TRACE); assert_eq!(dirs[4].in_span, None); assert_eq!(dirs[5].target, Some("crate3::mod2::mod1".to_string())); assert_eq!(dirs[5].level, LevelFilter::OFF); assert_eq!(dirs[5].in_span, None); } #[test] fn parse_directives_invalid_crate() { // test parse_directives with multiple = in specification let dirs = parse_directives("crate1::mod1=warn=info,crate2=debug"); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::DEBUG); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_invalid_level() { // test parse_directives with 'noNumber' as log level let dirs = parse_directives("crate1::mod1=noNumber,crate2=debug"); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::DEBUG); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_string_level() { // test parse_directives with 'warn' as log level let dirs = parse_directives("crate1::mod1=wrong,crate2=warn"); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::WARN); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_empty_level() { // test parse_directives with '' as log level let dirs = parse_directives("crate1::mod1=wrong,crate2="); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate2".to_string())); assert_eq!(dirs[0].level, LevelFilter::TRACE); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_global() { // test parse_directives with no crate let dirs = parse_directives("warn,crate2=debug"); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, None); assert_eq!(dirs[0].level, LevelFilter::WARN); assert_eq!(dirs[1].in_span, None); assert_eq!(dirs[1].target, Some("crate2".to_string())); assert_eq!(dirs[1].level, LevelFilter::DEBUG); assert_eq!(dirs[1].in_span, None); } // helper function for tests below fn test_parse_bare_level(directive_to_test: &str, level_expected: LevelFilter) { let dirs = parse_directives(directive_to_test); assert_eq!( dirs.len(), 1, "\ninput: \"{}\"; parsed: {:#?}", directive_to_test, dirs ); assert_eq!(dirs[0].target, None); assert_eq!(dirs[0].level, level_expected); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_global_bare_warn_lc() { // test parse_directives with no crate, in isolation, all lowercase test_parse_bare_level("warn", LevelFilter::WARN); } #[test] fn parse_directives_global_bare_warn_uc() { // test parse_directives with no crate, in isolation, all uppercase test_parse_bare_level("WARN", LevelFilter::WARN); } #[test] fn parse_directives_global_bare_warn_mixed() { // test parse_directives with no crate, in isolation, mixed case test_parse_bare_level("wArN", LevelFilter::WARN); } #[test] fn parse_directives_valid_with_spans() { let dirs = parse_directives("crate1::mod1[foo]=error,crate1::mod2[bar],crate2[baz]=debug"); assert_eq!(dirs.len(), 3, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::ERROR); assert_eq!(dirs[0].in_span, Some("foo".to_string())); assert_eq!(dirs[1].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[1].level, LevelFilter::TRACE); assert_eq!(dirs[1].in_span, Some("bar".to_string())); assert_eq!(dirs[2].target, Some("crate2".to_string())); assert_eq!(dirs[2].level, LevelFilter::DEBUG); assert_eq!(dirs[2].in_span, Some("baz".to_string())); } #[test] fn parse_directives_with_dash_in_target_name() { let dirs = parse_directives("target-name=info"); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("target-name".to_string())); assert_eq!(dirs[0].level, LevelFilter::INFO); assert_eq!(dirs[0].in_span, None); } #[test] fn parse_directives_with_dash_in_span_name() { // Reproduces https://github.com/tokio-rs/tracing/issues/1367 let dirs = parse_directives("target[span-name]=info"); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("target".to_string())); assert_eq!(dirs[0].level, LevelFilter::INFO); assert_eq!(dirs[0].in_span, Some("span-name".to_string())); } #[test] fn parse_directives_with_special_characters_in_span_name() { let span_name = "!\"#$%&'()*+-./:;<=>?@^_`|~[}"; let dirs = parse_directives(format!("target[{}]=info", span_name)); assert_eq!(dirs.len(), 1, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("target".to_string())); assert_eq!(dirs[0].level, LevelFilter::INFO); assert_eq!(dirs[0].in_span, Some(span_name.to_string())); } #[test] fn parse_directives_with_invalid_span_chars() { let invalid_span_name = "]{"; let dirs = parse_directives(format!("target[{}]=info", invalid_span_name)); assert_eq!(dirs.len(), 0, "\nparsed: {:#?}", dirs); } } tracing-subscriber-0.3.18/src/filter/env/field.rs000064400000000000000000000436101046102023000200060ustar 00000000000000use matchers::Pattern; use std::{ cmp::Ordering, error::Error, fmt::{self, Write}, str::FromStr, sync::{ atomic::{AtomicBool, Ordering::*}, Arc, }, }; use super::{FieldMap, LevelFilter}; use tracing_core::field::{Field, Visit}; #[derive(Clone, Debug, Eq, PartialEq)] pub(crate) struct Match { pub(crate) name: String, // TODO: allow match patterns for names? pub(crate) value: Option, } #[derive(Debug, Eq, PartialEq)] pub(crate) struct CallsiteMatch { pub(crate) fields: FieldMap, pub(crate) level: LevelFilter, } #[derive(Debug)] pub(crate) struct SpanMatch { fields: FieldMap<(ValueMatch, AtomicBool)>, level: LevelFilter, has_matched: AtomicBool, } pub(crate) struct MatchVisitor<'a> { inner: &'a SpanMatch, } #[derive(Debug, Clone)] pub(crate) enum ValueMatch { /// Matches a specific `bool` value. Bool(bool), /// Matches a specific `f64` value. F64(f64), /// Matches a specific `u64` value. U64(u64), /// Matches a specific `i64` value. I64(i64), /// Matches any `NaN` `f64` value. NaN, /// Matches any field whose `fmt::Debug` output is equal to a fixed string. Debug(MatchDebug), /// Matches any field whose `fmt::Debug` output matches a regular expression /// pattern. Pat(Box), } impl Eq for ValueMatch {} impl PartialEq for ValueMatch { fn eq(&self, other: &Self) -> bool { use ValueMatch::*; match (self, other) { (Bool(a), Bool(b)) => a.eq(b), (F64(a), F64(b)) => { debug_assert!(!a.is_nan()); debug_assert!(!b.is_nan()); a.eq(b) } (U64(a), U64(b)) => a.eq(b), (I64(a), I64(b)) => a.eq(b), (NaN, NaN) => true, (Pat(a), Pat(b)) => a.eq(b), _ => false, } } } impl Ord for ValueMatch { fn cmp(&self, other: &Self) -> Ordering { use ValueMatch::*; match (self, other) { (Bool(this), Bool(that)) => this.cmp(that), (Bool(_), _) => Ordering::Less, (F64(this), F64(that)) => this .partial_cmp(that) .expect("`ValueMatch::F64` may not contain `NaN` values"), (F64(_), Bool(_)) => Ordering::Greater, (F64(_), _) => Ordering::Less, (NaN, NaN) => Ordering::Equal, (NaN, Bool(_)) | (NaN, F64(_)) => Ordering::Greater, (NaN, _) => Ordering::Less, (U64(this), U64(that)) => this.cmp(that), (U64(_), Bool(_)) | (U64(_), F64(_)) | (U64(_), NaN) => Ordering::Greater, (U64(_), _) => Ordering::Less, (I64(this), I64(that)) => this.cmp(that), (I64(_), Bool(_)) | (I64(_), F64(_)) | (I64(_), NaN) | (I64(_), U64(_)) => { Ordering::Greater } (I64(_), _) => Ordering::Less, (Pat(this), Pat(that)) => this.cmp(that), (Pat(_), _) => Ordering::Greater, (Debug(this), Debug(that)) => this.cmp(that), (Debug(_), _) => Ordering::Greater, } } } impl PartialOrd for ValueMatch { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } /// Matches a field's `fmt::Debug` output against a regular expression pattern. /// /// This is used for matching all non-literal field value filters when regular /// expressions are enabled. #[derive(Debug, Clone)] pub(crate) struct MatchPattern { pub(crate) matcher: Pattern, pattern: Arc, } /// Matches a field's `fmt::Debug` output against a fixed string pattern. /// /// This is used for matching all non-literal field value filters when regular /// expressions are disabled. #[derive(Debug, Clone)] pub(crate) struct MatchDebug { pattern: Arc, } /// Indicates that a field name specified in a filter directive was invalid. #[derive(Clone, Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] pub struct BadName { name: String, } // === impl Match === impl Match { pub(crate) fn has_value(&self) -> bool { self.value.is_some() } // TODO: reference count these strings? pub(crate) fn name(&self) -> String { self.name.clone() } pub(crate) fn parse(s: &str, regex: bool) -> Result> { let mut parts = s.split('='); let name = parts .next() .ok_or_else(|| BadName { name: "".to_string(), })? // TODO: validate field name .to_string(); let value = parts .next() .map(|part| match regex { true => ValueMatch::parse_regex(part), false => Ok(ValueMatch::parse_non_regex(part)), }) .transpose()?; Ok(Match { name, value }) } } impl fmt::Display for Match { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.name, f)?; if let Some(ref value) = self.value { write!(f, "={}", value)?; } Ok(()) } } impl Ord for Match { fn cmp(&self, other: &Self) -> Ordering { // Ordering for `Match` directives is based first on _whether_ a value // is matched or not. This is semantically meaningful --- we would // prefer to check directives that match values first as they are more // specific. let has_value = match (self.value.as_ref(), other.value.as_ref()) { (Some(_), None) => Ordering::Greater, (None, Some(_)) => Ordering::Less, _ => Ordering::Equal, }; // If both directives match a value, we fall back to the field names in // length + lexicographic ordering, and if these are equal as well, we // compare the match directives. // // This ordering is no longer semantically meaningful but is necessary // so that the directives can be stored in the `BTreeMap` in a defined // order. has_value .then_with(|| self.name.cmp(&other.name)) .then_with(|| self.value.cmp(&other.value)) } } impl PartialOrd for Match { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } // === impl ValueMatch === fn value_match_f64(v: f64) -> ValueMatch { if v.is_nan() { ValueMatch::NaN } else { ValueMatch::F64(v) } } impl ValueMatch { /// Parse a `ValueMatch` that will match `fmt::Debug` fields using regular /// expressions. /// /// This returns an error if the string didn't contain a valid `bool`, /// `u64`, `i64`, or `f64` literal, and couldn't be parsed as a regular /// expression. fn parse_regex(s: &str) -> Result { s.parse::() .map(ValueMatch::Bool) .or_else(|_| s.parse::().map(ValueMatch::U64)) .or_else(|_| s.parse::().map(ValueMatch::I64)) .or_else(|_| s.parse::().map(value_match_f64)) .or_else(|_| { s.parse::() .map(|p| ValueMatch::Pat(Box::new(p))) }) } /// Parse a `ValueMatch` that will match `fmt::Debug` against a fixed /// string. /// /// This does *not* return an error, because any string that isn't a valid /// `bool`, `u64`, `i64`, or `f64` literal is treated as expected /// `fmt::Debug` output. fn parse_non_regex(s: &str) -> Self { s.parse::() .map(ValueMatch::Bool) .or_else(|_| s.parse::().map(ValueMatch::U64)) .or_else(|_| s.parse::().map(ValueMatch::I64)) .or_else(|_| s.parse::().map(value_match_f64)) .unwrap_or_else(|_| ValueMatch::Debug(MatchDebug::new(s))) } } impl fmt::Display for ValueMatch { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 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::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), ValueMatch::Pat(ref inner) => fmt::Display::fmt(inner, f), } } } // === impl MatchPattern === impl FromStr for MatchPattern { type Err = matchers::Error; fn from_str(s: &str) -> Result { let matcher = s.parse::()?; Ok(Self { matcher, pattern: s.to_owned().into(), }) } } impl fmt::Display for MatchPattern { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&*self.pattern, f) } } impl AsRef for MatchPattern { #[inline] fn as_ref(&self) -> &str { self.pattern.as_ref() } } impl MatchPattern { #[inline] fn str_matches(&self, s: &impl AsRef) -> bool { self.matcher.matches(s) } #[inline] fn debug_matches(&self, d: &impl fmt::Debug) -> bool { self.matcher.debug_matches(d) } pub(super) fn into_debug_match(self) -> MatchDebug { MatchDebug { pattern: self.pattern, } } } impl PartialEq for MatchPattern { #[inline] fn eq(&self, other: &Self) -> bool { self.pattern == other.pattern } } impl Eq for MatchPattern {} impl PartialOrd for MatchPattern { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.pattern.cmp(&other.pattern)) } } impl Ord for MatchPattern { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.pattern.cmp(&other.pattern) } } // === impl MatchDebug === impl MatchDebug { fn new(s: &str) -> Self { Self { pattern: s.to_owned().into(), } } #[inline] fn debug_matches(&self, d: &impl fmt::Debug) -> bool { // Naively, we would probably match a value's `fmt::Debug` output by // formatting it to a string, and then checking if the string is equal // to the expected pattern. However, this would require allocating every // time we want to match a field value against a `Debug` matcher, which // can be avoided. // // Instead, we implement `fmt::Write` for a type that, rather than // actually _writing_ the strings to something, matches them against the // expected pattern, and returns an error if the pattern does not match. struct Matcher<'a> { pattern: &'a str, } impl fmt::Write for Matcher<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { // If the string is longer than the remaining expected string, // we know it won't match, so bail. if s.len() > self.pattern.len() { return Err(fmt::Error); } // If the expected string begins with the string that was // written, we are still potentially a match. Advance the // position in the expected pattern to chop off the matched // output, and continue. if self.pattern.starts_with(s) { self.pattern = &self.pattern[s.len()..]; return Ok(()); } // Otherwise, the expected string doesn't include the string // that was written at the current position, so the `fmt::Debug` // output doesn't match! Return an error signalling that this // doesn't match. Err(fmt::Error) } } let mut matcher = Matcher { pattern: &self.pattern, }; // Try to "write" the value's `fmt::Debug` output to a `Matcher`. This // returns an error if the `fmt::Debug` implementation wrote any // characters that did not match the expected pattern. write!(matcher, "{:?}", d).is_ok() } } impl fmt::Display for MatchDebug { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&*self.pattern, f) } } impl AsRef for MatchDebug { #[inline] fn as_ref(&self) -> &str { self.pattern.as_ref() } } impl PartialEq for MatchDebug { #[inline] fn eq(&self, other: &Self) -> bool { self.pattern == other.pattern } } impl Eq for MatchDebug {} impl PartialOrd for MatchDebug { #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.pattern.cmp(&other.pattern)) } } impl Ord for MatchDebug { #[inline] fn cmp(&self, other: &Self) -> Ordering { self.pattern.cmp(&other.pattern) } } // === impl BadName === impl Error for BadName {} impl fmt::Display for BadName { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "invalid field name `{}`", self.name) } } impl CallsiteMatch { pub(crate) fn to_span_match(&self) -> SpanMatch { let fields = self .fields .iter() .map(|(k, v)| (k.clone(), (v.clone(), AtomicBool::new(false)))) .collect(); SpanMatch { fields, level: self.level, has_matched: AtomicBool::new(false), } } } impl SpanMatch { pub(crate) fn visitor(&self) -> MatchVisitor<'_> { MatchVisitor { inner: self } } #[inline] pub(crate) fn is_matched(&self) -> bool { if self.has_matched.load(Acquire) { return true; } self.is_matched_slow() } #[inline(never)] fn is_matched_slow(&self) -> bool { let matched = self .fields .values() .all(|(_, matched)| matched.load(Acquire)); if matched { self.has_matched.store(true, Release); } matched } #[inline] pub(crate) fn filter(&self) -> Option { if self.is_matched() { Some(self.level) } else { None } } } impl<'a> Visit for MatchVisitor<'a> { fn record_f64(&mut self, field: &Field, value: f64) { match self.inner.fields.get(field) { Some((ValueMatch::NaN, ref matched)) if value.is_nan() => { matched.store(true, Release); } Some((ValueMatch::F64(ref e), ref matched)) if (value - *e).abs() < std::f64::EPSILON => { matched.store(true, Release); } _ => {} } } fn record_i64(&mut self, field: &Field, value: i64) { use std::convert::TryInto; match self.inner.fields.get(field) { Some((ValueMatch::I64(ref e), ref matched)) if value == *e => { matched.store(true, Release); } Some((ValueMatch::U64(ref e), ref matched)) if Ok(value) == (*e).try_into() => { matched.store(true, Release); } _ => {} } } fn record_u64(&mut self, field: &Field, value: u64) { match self.inner.fields.get(field) { Some((ValueMatch::U64(ref e), ref matched)) if value == *e => { matched.store(true, Release); } _ => {} } } fn record_bool(&mut self, field: &Field, value: bool) { match self.inner.fields.get(field) { Some((ValueMatch::Bool(ref e), ref matched)) if value == *e => { matched.store(true, Release); } _ => {} } } fn record_str(&mut self, field: &Field, value: &str) { match self.inner.fields.get(field) { Some((ValueMatch::Pat(ref e), ref matched)) if e.str_matches(&value) => { matched.store(true, Release); } Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { matched.store(true, Release) } _ => {} } } fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { match self.inner.fields.get(field) { Some((ValueMatch::Pat(ref e), ref matched)) if e.debug_matches(&value) => { matched.store(true, Release); } Some((ValueMatch::Debug(ref e), ref matched)) if e.debug_matches(&value) => { matched.store(true, Release) } _ => {} } } } #[cfg(test)] mod tests { use super::*; #[derive(Debug)] #[allow(dead_code)] struct MyStruct { answer: usize, question: &'static str, } #[test] fn debug_struct_match() { let my_struct = MyStruct { answer: 42, question: "life, the universe, and everything", }; let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; assert_eq!( format!("{:?}", my_struct), pattern, "`MyStruct`'s `Debug` impl doesn't output the expected string" ); let matcher = MatchDebug { pattern: pattern.into(), }; assert!(matcher.debug_matches(&my_struct)) } #[test] fn debug_struct_not_match() { let my_struct = MyStruct { answer: 42, question: "what shall we have for lunch?", }; let pattern = "MyStruct { answer: 42, question: \"life, the universe, and everything\" }"; assert_eq!( format!("{:?}", my_struct), "MyStruct { answer: 42, question: \"what shall we have for lunch?\" }", "`MyStruct`'s `Debug` impl doesn't output the expected string" ); let matcher = MatchDebug { pattern: pattern.into(), }; assert!(!matcher.debug_matches(&my_struct)) } } tracing-subscriber-0.3.18/src/filter/env/mod.rs000064400000000000000000001040731046102023000175030ustar 00000000000000//! A `Layer` that enables or disables spans and events based on a set of //! filtering directives. // these are publicly re-exported, but the compiler doesn't realize // that for some reason. #[allow(unreachable_pub)] pub use self::{builder::Builder, directive::Directive, field::BadName as BadFieldName}; mod builder; mod directive; mod field; use crate::{ filter::LevelFilter, layer::{Context, Layer}, sync::RwLock, }; use directive::ParseError; use std::{cell::RefCell, collections::HashMap, env, error::Error, fmt, str::FromStr}; use thread_local::ThreadLocal; use tracing_core::{ callsite, field::Field, span, subscriber::{Interest, Subscriber}, Metadata, }; /// A [`Layer`] which filters spans and events based on a set of filter /// directives. /// /// `EnvFilter` implements both the [`Layer`](#impl-Layer) and [`Filter`] traits, so it may /// be used for both [global filtering][global] and [per-layer filtering][plf], /// respectively. See [the documentation on filtering with `Layer`s][filtering] /// for details. /// /// The [`Targets`] type implements a similar form of filtering, but without the /// ability to dynamically enable events based on the current span context, and /// without filtering on field values. When these features are not required, /// [`Targets`] provides a lighter-weight alternative to [`EnvFilter`]. /// /// # Directives /// /// A filter consists of one or more comma-separated directives which match on [`Span`]s and [`Event`]s. /// Each directive may have a corresponding maximum verbosity [`level`] which /// enables (e.g., _selects for_) spans and events that match. Like `log`, /// `tracing` considers less exclusive levels (like `trace` or `info`) to be more /// verbose than more exclusive levels (like `error` or `warn`). /// /// The directive syntax is similar to that of [`env_logger`]'s. At a high level, the syntax for directives /// consists of several parts: /// /// ```text /// target[span{field=value}]=level /// ``` /// /// Each component (`target`, `span`, `field`, `value`, and `level`) will be covered in turn. /// /// - `target` matches the event or span's target. In general, this is the module path and/or crate name. /// Examples of targets `h2`, `tokio::net`, or `tide::server`. For more information on targets, /// please refer to [`Metadata`]'s documentation. /// - `span` matches on the span's name. If a `span` directive is provided alongside a `target`, /// the `span` directive will match on spans _within_ the `target`. /// - `field` matches on [fields] within spans. Field names can also be supplied without a `value` /// and will match on any [`Span`] or [`Event`] that has a field with that name. /// For example: `[span{field=\"value\"}]=debug`, `[{field}]=trace`. /// - `value` matches on the value of a span's field. If a value is a numeric literal or a bool, /// it will match _only_ on that value. Otherwise, this filter matches the /// [`std::fmt::Debug`] output from the value. /// - `level` sets a maximum verbosity level accepted by this directive. /// /// When a field value directive (`[{=}]=...`) matches a /// value's [`std::fmt::Debug`] output (i.e., the field value in the directive /// is not a `bool`, `i64`, `u64`, or `f64` literal), the matched pattern may be /// interpreted as either a regular expression or as the precise expected /// output of the field's [`std::fmt::Debug`] implementation. By default, these /// filters are interpreted as regular expressions, but this can be disabled /// using the [`Builder::with_regex`] builder method to use precise matching /// instead. /// /// When field value filters are interpreted as regular expressions, the /// [`regex-automata` crate's regular expression syntax][re-syntax] is /// supported. /// /// **Note**: When filters are constructed from potentially untrusted inputs, /// [disabling regular expression matching](Builder::with_regex) is strongly /// recommended. /// /// ## Usage Notes /// /// - The portion of the directive which is included within the square brackets is `tracing`-specific. /// - Any portion of the directive can be omitted. /// - The sole exception are the `field` and `value` directives. If a `value` is provided, /// a `field` must _also_ be provided. However, the converse does not hold, as fields can /// be matched without a value. /// - If only a level is provided, it will set the maximum level for all `Span`s and `Event`s /// that are not enabled by other filters. /// - A directive without a level will enable anything that it matches. This is equivalent to `=trace`. /// - When a crate has a dash in its name, the default target for events will be the /// crate's module path as it appears in Rust. This means every dash will be replaced /// with an underscore. /// - A dash in a target will only appear when being specified explicitly: /// `tracing::info!(target: "target-name", ...);` /// /// ## Example Syntax /// /// - `tokio::net=info` will enable all spans or events that: /// - have the `tokio::net` target, /// - at the level `info` or above. /// - `warn,tokio::net=info` will enable all spans and events that: /// - are at the level `warn` or above, *or* /// - have the `tokio::net` target at the level `info` or above. /// - `my_crate[span_a]=trace` will enable all spans and events that: /// - are within the `span_a` span or named `span_a` _if_ `span_a` has the target `my_crate`, /// - at the level `trace` or above. /// - `[span_b{name=\"bob\"}]` will enable all spans or event that: /// - have _any_ target, /// - are inside a span named `span_b`, /// - which has a field named `name` with value `bob`, /// - at _any_ level. /// /// # Examples /// /// Parsing an `EnvFilter` from the [default environment /// variable](EnvFilter::from_default_env) (`RUST_LOG`): /// /// ``` /// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; /// /// tracing_subscriber::registry() /// .with(fmt::layer()) /// .with(EnvFilter::from_default_env()) /// .init(); /// ``` /// /// Parsing an `EnvFilter` [from a user-provided environment /// variable](EnvFilter::from_env): /// /// ``` /// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; /// /// tracing_subscriber::registry() /// .with(fmt::layer()) /// .with(EnvFilter::from_env("MYAPP_LOG")) /// .init(); /// ``` /// /// Using `EnvFilter` as a [per-layer filter][plf] to filter only a single /// [`Layer`]: /// /// ``` /// use tracing_subscriber::{EnvFilter, fmt, prelude::*}; /// /// // Parse an `EnvFilter` configuration from the `RUST_LOG` /// // environment variable. /// let filter = EnvFilter::from_default_env(); /// /// // Apply the filter to this layer *only*. /// let filtered_layer = fmt::layer().with_filter(filter); /// /// // Some other layer, whose output we don't want to filter. /// let unfiltered_layer = // ... /// # fmt::layer(); /// /// tracing_subscriber::registry() /// .with(filtered_layer) /// .with(unfiltered_layer) /// .init(); /// ``` /// # Constructing `EnvFilter`s /// /// An `EnvFilter` is be constructed by parsing a string containing one or more /// directives. The [`EnvFilter::new`] constructor parses an `EnvFilter` from a /// string, ignoring any invalid directives, while [`EnvFilter::try_new`] /// returns an error if invalid directives are encountered. Similarly, the /// [`EnvFilter::from_env`] and [`EnvFilter::try_from_env`] constructors parse /// an `EnvFilter` from the value of the provided environment variable, with /// lossy and strict validation, respectively. /// /// A [builder](EnvFilter::builder) interface is available to set additional /// configuration options prior to parsing an `EnvFilter`. See the [`Builder` /// type's documentation](Builder) for details on the options that can be /// configured using the builder. /// /// [`Span`]: tracing_core::span /// [fields]: tracing_core::Field /// [`Event`]: tracing_core::Event /// [`level`]: tracing_core::Level /// [`Metadata`]: tracing_core::Metadata /// [`Targets`]: crate::filter::Targets /// [`env_logger`]: https://crates.io/crates/env_logger /// [`Filter`]: #impl-Filter /// [global]: crate::layer#global-filtering /// [plf]: crate::layer#per-layer-filtering /// [filtering]: crate::layer#filtering-with-layers #[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] #[derive(Debug)] pub struct EnvFilter { statics: directive::Statics, dynamics: directive::Dynamics, has_dynamics: bool, by_id: RwLock>, by_cs: RwLock>, scope: ThreadLocal>>, regex: bool, } type FieldMap = HashMap; /// Indicates that an error occurred while parsing a `EnvFilter` from an /// environment variable. #[cfg_attr(docsrs, doc(cfg(all(feature = "env-filter", feature = "std"))))] #[derive(Debug)] pub struct FromEnvError { kind: ErrorKind, } #[derive(Debug)] enum ErrorKind { Parse(ParseError), Env(env::VarError), } impl EnvFilter { /// `RUST_LOG` is the default environment variable used by /// [`EnvFilter::from_default_env`] and [`EnvFilter::try_from_default_env`]. /// /// [`EnvFilter::from_default_env`]: EnvFilter::from_default_env() /// [`EnvFilter::try_from_default_env`]: EnvFilter::try_from_default_env() pub const DEFAULT_ENV: &'static str = "RUST_LOG"; // === constructors, etc === /// Returns a [builder] that can be used to configure a new [`EnvFilter`] /// instance. /// /// The [`Builder`] type is used to set additional configurations, such as /// [whether regular expressions are enabled](Builder::with_regex) or [the /// default directive](Builder::with_default_directive) before parsing an /// [`EnvFilter`] from a string or environment variable. /// /// [builder]: https://rust-unofficial.github.io/patterns/patterns/creational/builder.html pub fn builder() -> Builder { Builder::default() } /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment /// variable, ignoring any invalid filter directives. /// /// If the environment variable is empty or not set, or if it contains only /// invalid directives, a default directive enabling the [`ERROR`] level is /// added. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// # fn docs() -> EnvFilter { /// EnvFilter::builder() /// .with_default_directive(LevelFilter::ERROR.into()) /// .from_env_lossy() /// # } /// ``` /// /// [`ERROR`]: tracing::Level::ERROR pub fn from_default_env() -> Self { Self::builder() .with_default_directive(LevelFilter::ERROR.into()) .from_env_lossy() } /// Returns a new `EnvFilter` from the value of the given environment /// variable, ignoring any invalid filter directives. /// /// If the environment variable is empty or not set, or if it contains only /// invalid directives, a default directive enabling the [`ERROR`] level is /// added. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// # fn docs() -> EnvFilter { /// # let env = ""; /// EnvFilter::builder() /// .with_default_directive(LevelFilter::ERROR.into()) /// .with_env_var(env) /// .from_env_lossy() /// # } /// ``` /// /// [`ERROR`]: tracing::Level::ERROR pub fn from_env>(env: A) -> Self { Self::builder() .with_default_directive(LevelFilter::ERROR.into()) .with_env_var(env.as_ref()) .from_env_lossy() } /// Returns a new `EnvFilter` from the directives in the given string, /// ignoring any that are invalid. /// /// If the string is empty or contains only invalid directives, a default /// directive enabling the [`ERROR`] level is added. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// # fn docs() -> EnvFilter { /// # let directives = ""; /// EnvFilter::builder() /// .with_default_directive(LevelFilter::ERROR.into()) /// .parse_lossy(directives) /// # } /// ``` /// /// [`ERROR`]: tracing::Level::ERROR pub fn new>(directives: S) -> Self { Self::builder() .with_default_directive(LevelFilter::ERROR.into()) .parse_lossy(directives) } /// Returns a new `EnvFilter` from the directives in the given string, /// or an error if any are invalid. /// /// If the string is empty, a default directive enabling the [`ERROR`] level /// is added. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// /// # fn docs() -> Result { /// # let directives = ""; /// EnvFilter::builder() /// .with_default_directive(LevelFilter::ERROR.into()) /// .parse(directives) /// # } /// ``` /// /// [`ERROR`]: tracing::Level::ERROR pub fn try_new>(dirs: S) -> Result { Self::builder().parse(dirs) } /// Returns a new `EnvFilter` from the value of the `RUST_LOG` environment /// variable, or an error if the environment variable is unset or contains /// any invalid filter directives. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::EnvFilter; /// /// # fn docs() -> Result { /// EnvFilter::builder().try_from_env() /// # } /// ``` pub fn try_from_default_env() -> Result { Self::builder().try_from_env() } /// Returns a new `EnvFilter` from the value of the given environment /// variable, or an error if the environment variable is unset or contains /// any invalid filter directives. /// /// To set additional configuration options prior to parsing the filter, use /// the [`Builder`] type instead. /// /// This function is equivalent to the following: /// /// ```rust /// use tracing_subscriber::EnvFilter; /// /// # fn docs() -> Result { /// # let env = ""; /// EnvFilter::builder().with_env_var(env).try_from_env() /// # } /// ``` pub fn try_from_env>(env: A) -> Result { Self::builder().with_env_var(env.as_ref()).try_from_env() } /// Add a filtering directive to this `EnvFilter`. /// /// The added directive will be used in addition to any previously set /// directives, either added using this method or provided when the filter /// is constructed. /// /// Filters may be created from [`LevelFilter`] or [`Level`], which will /// enable all traces at or below a certain verbosity level, or /// parsed from a string specifying a directive. /// /// If a filter directive is inserted that matches exactly the same spans /// and events as a previous filter, but sets a different level for those /// spans and events, the previous directive is overwritten. /// /// [`LevelFilter`]: super::LevelFilter /// [`Level`]: tracing_core::Level /// /// # Examples /// /// From [`LevelFilter`]: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// let mut filter = EnvFilter::from_default_env() /// .add_directive(LevelFilter::INFO.into()); /// ``` /// /// Or from [`Level`]: /// /// ```rust /// # use tracing_subscriber::filter::{EnvFilter, LevelFilter}; /// # use tracing::Level; /// let mut filter = EnvFilter::from_default_env() /// .add_directive(Level::INFO.into()); /// ``` /// /// Parsed from a string: /// /// ```rust /// use tracing_subscriber::filter::{EnvFilter, Directive}; /// /// # fn try_mk_filter() -> Result<(), Box> { /// let mut filter = EnvFilter::try_from_default_env()? /// .add_directive("my_crate::module=trace".parse()?) /// .add_directive("my_crate::my_other_module::something=info".parse()?); /// # Ok(()) /// # } /// ``` /// In the above example, substitute `my_crate`, `module`, etc. with the /// name your target crate/module is imported with. This might be /// different from the package name in Cargo.toml (`-` is replaced by `_`). /// Example, if the package name in your Cargo.toml is `MY-FANCY-LIB`, then /// the corresponding Rust identifier would be `MY_FANCY_LIB`: pub fn add_directive(mut self, mut directive: Directive) -> Self { if !self.regex { directive.deregexify(); } if let Some(stat) = directive.to_static() { self.statics.add(stat) } else { self.has_dynamics = true; self.dynamics.add(directive); } self } // === filtering methods === /// Returns `true` if this `EnvFilter` would enable the provided `metadata` /// in the current context. /// /// This is equivalent to calling the [`Layer::enabled`] or /// [`Filter::enabled`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. pub fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { 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 // spans in the current scope. if self.has_dynamics && self.dynamics.max_level >= *level { if metadata.is_span() { // If the metadata is a span, see if we care about its callsite. let enabled_by_cs = self .by_cs .read() .ok() .map(|by_cs| by_cs.contains_key(&metadata.callsite())) .unwrap_or(false); if enabled_by_cs { return true; } } let enabled_by_scope = { let scope = self.scope.get_or_default().borrow(); for filter in &*scope { if filter >= level { return true; } } false }; if enabled_by_scope { return true; } } // is it possible for a static filter directive to enable this event? if self.statics.max_level >= *level { // Otherwise, fall back to checking if the callsite is // statically enabled. return self.statics.enabled(metadata); } false } /// Returns an optional hint of the highest [verbosity level][level] that /// this `EnvFilter` will enable. /// /// This is equivalent to calling the [`Layer::max_level_hint`] or /// [`Filter::max_level_hint`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. /// /// [level]: tracing_core::metadata::Level pub fn max_level_hint(&self) -> Option { if self.dynamics.has_value_filters() { // If we perform any filtering on span field *values*, we will // enable *all* spans, because their field values are not known // until recording. return Some(LevelFilter::TRACE); } std::cmp::max( self.statics.max_level.into(), self.dynamics.max_level.into(), ) } /// Informs the filter that a new span was created. /// /// This is equivalent to calling the [`Layer::on_new_span`] or /// [`Filter::on_new_span`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. pub fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, _: Context<'_, S>) { let by_cs = try_lock!(self.by_cs.read()); if let Some(cs) = by_cs.get(&attrs.metadata().callsite()) { let span = cs.to_span_match(attrs); try_lock!(self.by_id.write()).insert(id.clone(), span); } } /// Informs the filter that the span with the provided `id` was entered. /// /// This is equivalent to calling the [`Layer::on_enter`] or /// [`Filter::on_enter`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. pub fn on_enter(&self, id: &span::Id, _: Context<'_, S>) { // XXX: This is where _we_ could push IDs to the stack instead, and use // that to allow changing the filter while a span is already entered. // But that might be much less efficient... if let Some(span) = try_lock!(self.by_id.read()).get(id) { self.scope.get_or_default().borrow_mut().push(span.level()); } } /// Informs the filter that the span with the provided `id` was exited. /// /// This is equivalent to calling the [`Layer::on_exit`] or /// [`Filter::on_exit`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. pub fn on_exit(&self, id: &span::Id, _: Context<'_, S>) { if self.cares_about_span(id) { self.scope.get_or_default().borrow_mut().pop(); } } /// Informs the filter that the span with the provided `id` was closed. /// /// This is equivalent to calling the [`Layer::on_close`] or /// [`Filter::on_close`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope. pub fn on_close(&self, id: span::Id, _: Context<'_, S>) { // If we don't need to acquire a write lock, avoid doing so. if !self.cares_about_span(&id) { return; } let mut spans = try_lock!(self.by_id.write()); spans.remove(&id); } /// Informs the filter that the span with the provided `id` recorded the /// provided field `values`. /// /// This is equivalent to calling the [`Layer::on_record`] or /// [`Filter::on_record`] methods on `EnvFilter`'s implementations of those /// traits, but it does not require the trait to be in scope pub fn on_record(&self, id: &span::Id, values: &span::Record<'_>, _: Context<'_, S>) { if let Some(span) = try_lock!(self.by_id.read()).get(id) { span.record_update(values); } } fn cares_about_span(&self, span: &span::Id) -> bool { let spans = try_lock!(self.by_id.read(), else return false); spans.contains_key(span) } fn base_interest(&self) -> Interest { if self.has_dynamics { Interest::sometimes() } else { Interest::never() } } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { if self.has_dynamics && metadata.is_span() { // If this metadata describes a span, first, check if there is a // dynamic filter that should be constructed for it. If so, it // should always be enabled, since it influences filtering. if let Some(matcher) = self.dynamics.matcher(metadata) { let mut by_cs = try_lock!(self.by_cs.write(), else return self.base_interest()); by_cs.insert(metadata.callsite(), matcher); return Interest::always(); } } // Otherwise, check if any of our static filters enable this metadata. if self.statics.enabled(metadata) { Interest::always() } else { self.base_interest() } } } impl Layer for EnvFilter { #[inline] fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { EnvFilter::register_callsite(self, metadata) } #[inline] fn max_level_hint(&self) -> Option { EnvFilter::max_level_hint(self) } #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { self.enabled(metadata, ctx) } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { self.on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { self.on_record(id, values, ctx); } #[inline] fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { self.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { self.on_exit(id, ctx); } #[inline] fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { self.on_close(id, ctx); } } feature! { #![all(feature = "registry", feature = "std")] use crate::layer::Filter; impl Filter for EnvFilter { #[inline] fn enabled(&self, meta: &Metadata<'_>, ctx: &Context<'_, S>) -> bool { self.enabled(meta, ctx.clone()) } #[inline] fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { self.register_callsite(meta) } #[inline] fn max_level_hint(&self) -> Option { EnvFilter::max_level_hint(self) } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { self.on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { self.on_record(id, values, ctx); } #[inline] fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { self.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { self.on_exit(id, ctx); } #[inline] fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { self.on_close(id, ctx); } } } impl FromStr for EnvFilter { type Err = directive::ParseError; fn from_str(spec: &str) -> Result { Self::try_new(spec) } } impl From for EnvFilter where S: AsRef, { fn from(s: S) -> Self { Self::new(s) } } impl Default for EnvFilter { fn default() -> Self { Builder::default().from_directives(std::iter::empty()) } } impl fmt::Display for EnvFilter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut statics = self.statics.iter(); let wrote_statics = if let Some(next) = statics.next() { fmt::Display::fmt(next, f)?; for directive in statics { write!(f, ",{}", directive)?; } true } else { false }; let mut dynamics = self.dynamics.iter(); if let Some(next) = dynamics.next() { if wrote_statics { f.write_str(",")?; } fmt::Display::fmt(next, f)?; for directive in dynamics { write!(f, ",{}", directive)?; } } Ok(()) } } // ===== impl FromEnvError ===== impl From for FromEnvError { fn from(p: directive::ParseError) -> Self { Self { kind: ErrorKind::Parse(p), } } } impl From for FromEnvError { fn from(v: env::VarError) -> Self { Self { kind: ErrorKind::Env(v), } } } impl fmt::Display for FromEnvError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.kind { ErrorKind::Parse(ref p) => p.fmt(f), ErrorKind::Env(ref e) => e.fmt(f), } } } impl Error for FromEnvError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self.kind { ErrorKind::Parse(ref p) => Some(p), ErrorKind::Env(ref e) => Some(e), } } } #[cfg(test)] mod tests { use super::*; use tracing_core::field::FieldSet; use tracing_core::*; struct NoSubscriber; impl Subscriber for NoSubscriber { #[inline] fn register_callsite(&self, _: &'static Metadata<'static>) -> subscriber::Interest { subscriber::Interest::always() } fn new_span(&self, _: &span::Attributes<'_>) -> span::Id { span::Id::from_u64(0xDEAD) } fn event(&self, _event: &Event<'_>) {} fn record(&self, _span: &span::Id, _values: &span::Record<'_>) {} fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} #[inline] fn enabled(&self, _metadata: &Metadata<'_>) -> bool { true } fn enter(&self, _span: &span::Id) {} fn exit(&self, _span: &span::Id) {} } struct Cs; impl Callsite for Cs { fn set_interest(&self, _interest: Interest) {} fn metadata(&self) -> &Metadata<'_> { unimplemented!() } } #[test] fn callsite_enabled_no_span_directive() { let filter = EnvFilter::new("app=debug").with_subscriber(NoSubscriber); static META: &Metadata<'static> = &Metadata::new( "mySpan", "app", Level::TRACE, None, None, None, FieldSet::new(&[], identify_callsite!(&Cs)), Kind::SPAN, ); let interest = filter.register_callsite(META); assert!(interest.is_never()); } #[test] fn callsite_off() { let filter = EnvFilter::new("app=off").with_subscriber(NoSubscriber); static META: &Metadata<'static> = &Metadata::new( "mySpan", "app", Level::ERROR, None, None, None, FieldSet::new(&[], identify_callsite!(&Cs)), Kind::SPAN, ); let interest = filter.register_callsite(META); assert!(interest.is_never()); } #[test] fn callsite_enabled_includes_span_directive() { let filter = EnvFilter::new("app[mySpan]=debug").with_subscriber(NoSubscriber); static META: &Metadata<'static> = &Metadata::new( "mySpan", "app", Level::TRACE, None, None, None, FieldSet::new(&[], identify_callsite!(&Cs)), Kind::SPAN, ); let interest = filter.register_callsite(META); assert!(interest.is_always()); } #[test] fn callsite_enabled_includes_span_directive_field() { let filter = EnvFilter::new("app[mySpan{field=\"value\"}]=debug").with_subscriber(NoSubscriber); static META: &Metadata<'static> = &Metadata::new( "mySpan", "app", Level::TRACE, None, None, None, FieldSet::new(&["field"], identify_callsite!(&Cs)), Kind::SPAN, ); let interest = filter.register_callsite(META); assert!(interest.is_always()); } #[test] fn callsite_enabled_includes_span_directive_multiple_fields() { let filter = EnvFilter::new("app[mySpan{field=\"value\",field2=2}]=debug") .with_subscriber(NoSubscriber); static META: &Metadata<'static> = &Metadata::new( "mySpan", "app", Level::TRACE, None, None, None, FieldSet::new(&["field"], identify_callsite!(&Cs)), Kind::SPAN, ); let interest = filter.register_callsite(META); assert!(interest.is_never()); } #[test] fn roundtrip() { let f1: EnvFilter = "[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug" .parse() .unwrap(); let f2: EnvFilter = format!("{}", f1).parse().unwrap(); assert_eq!(f1.statics, f2.statics); assert_eq!(f1.dynamics, f2.dynamics); } #[test] fn size_of_filters() { fn print_sz(s: &str) { let filter = s.parse::().expect("filter should parse"); println!( "size_of_val({:?})\n -> {}B", s, std::mem::size_of_val(&filter) ); } print_sz("info"); print_sz("foo=debug"); print_sz( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ); print_sz("[span1{foo=1}]=error,[span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug"); print_sz( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off,[span1{foo=1}]=error,\ [span2{bar=2 baz=false}],crate2[{quux=\"quuux\"}]=debug", ); } #[test] fn parse_empty_string() { // There is no corresponding test for [`Builder::parse_lossy`] as failed // parsing does not produce any observable side effects. If this test fails // check that [`Builder::parse_lossy`] is behaving correctly as well. assert!(EnvFilter::builder().parse("").is_ok()); } } tracing-subscriber-0.3.18/src/filter/filter_fn.rs000064400000000000000000000606271046102023000201120ustar 00000000000000use crate::{ filter::LevelFilter, layer::{Context, Layer}, }; use core::{any::type_name, fmt, marker::PhantomData}; use tracing_core::{Interest, Metadata, Subscriber}; /// A filter implemented by a closure or function pointer that /// determines whether a given span or event is enabled, based on its /// [`Metadata`]. /// /// This type can be used for both [per-layer filtering][plf] (using its /// [`Filter`] implementation) and [global filtering][global] (using its /// [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// [`Metadata`]: tracing_core::Metadata /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers #[derive(Clone)] pub struct FilterFn) -> bool> { enabled: F, max_level_hint: Option, } /// A filter implemented by a closure or function pointer that /// determines whether a given span or event is enabled _dynamically_, /// potentially based on the current [span context]. /// /// This type can be used for both [per-layer filtering][plf] (using its /// [`Filter`] implementation) and [global filtering][global] (using its /// [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// [span context]: crate::layer::Context /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers pub struct DynFilterFn< S, // TODO(eliza): should these just be boxed functions? F = fn(&Metadata<'_>, &Context<'_, S>) -> bool, R = fn(&'static Metadata<'static>) -> Interest, > { enabled: F, register_callsite: Option, max_level_hint: Option, _s: PhantomData, } // === impl FilterFn === /// Constructs a [`FilterFn`], from a function or closure that returns `true` if /// a span or event should be enabled, based on its [`Metadata`]. /// /// The returned [`FilterFn`] can be used for both [per-layer filtering][plf] /// (using its [`Filter`] implementation) and [global filtering][global] (using /// its [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// This is equivalent to calling [`FilterFn::new`]. /// /// [`Metadata`]: tracing_core::Metadata /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter, /// util::SubscriberInitExt, /// }; /// /// let my_filter = filter::filter_fn(|metadata| { /// // Only enable spans or events with the target "interesting_things" /// metadata.target() == "interesting_things" /// }); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// /// // This event will not be enabled. /// tracing::warn!("something important but uninteresting happened!"); /// /// // This event will be enabled. /// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); /// ``` pub fn filter_fn(f: F) -> FilterFn where F: Fn(&Metadata<'_>) -> bool, { FilterFn::new(f) } /// Constructs a [`DynFilterFn`] from a function or closure that returns `true` /// if a span or event should be enabled within a particular [span context][`Context`]. /// /// This is equivalent to calling [`DynFilterFn::new`]. /// /// Unlike [`filter_fn`], this function takes a closure or function pointer /// taking the [`Metadata`] for a span or event *and* the current [`Context`]. /// This means that a [`DynFilterFn`] can choose whether to enable spans or /// events based on information about the _current_ span (or its parents). /// /// If this is *not* necessary, use [`filter_fn`] instead. /// /// The returned [`DynFilterFn`] can be used for both [per-layer filtering][plf] /// (using its [`Filter`] implementation) and [global filtering][global] (using /// its [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter, /// util::SubscriberInitExt, /// }; /// /// // Only enable spans or events within a span named "interesting_span". /// let my_filter = filter::dynamic_filter_fn(|metadata, cx| { /// // If this *is* "interesting_span", make sure to enable it. /// if metadata.is_span() && metadata.name() == "interesting_span" { /// return true; /// } /// /// // Otherwise, are we in an interesting span? /// if let Some(current_span) = cx.lookup_current() { /// return current_span.name() == "interesting_span"; /// } /// /// false /// }); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// /// // This event will not be enabled. /// tracing::info!("something happened"); /// /// tracing::info_span!("interesting_span").in_scope(|| { /// // This event will be enabled. /// tracing::debug!("something else happened"); /// }); /// ``` /// /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers /// [`Context`]: crate::layer::Context /// [`Metadata`]: tracing_core::Metadata pub fn dynamic_filter_fn(f: F) -> DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, { DynFilterFn::new(f) } impl FilterFn where F: Fn(&Metadata<'_>) -> bool, { /// Constructs a [`FilterFn`] from a function or closure that returns `true` /// if a span or event should be enabled, based on its [`Metadata`]. /// /// If determining whether a span or event should be enabled also requires /// information about the current span context, use [`DynFilterFn`] instead. /// /// See the [documentation on per-layer filtering][plf] for details on using /// [`Filter`]s. /// /// [`Filter`]: crate::layer::Filter /// [plf]: crate::layer#per-layer-filtering /// [`Metadata`]: tracing_core::Metadata /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter::FilterFn, /// util::SubscriberInitExt, /// }; /// /// let my_filter = FilterFn::new(|metadata| { /// // Only enable spans or events with the target "interesting_things" /// metadata.target() == "interesting_things" /// }); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// /// // This event will not be enabled. /// tracing::warn!("something important but uninteresting happened!"); /// /// // This event will be enabled. /// tracing::debug!(target: "interesting_things", "an interesting minor detail..."); /// ``` pub fn new(enabled: F) -> Self { Self { enabled, max_level_hint: None, } } /// Sets the highest verbosity [`Level`] the filter function will enable. /// /// The value passed to this method will be returned by this `FilterFn`'s /// [`Filter::max_level_hint`] method. /// /// If the provided function will not enable all levels, it is recommended /// to call this method to configure it with the most verbose level it will /// enable. /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter::{filter_fn, LevelFilter}, /// util::SubscriberInitExt, /// }; /// use tracing_core::Level; /// /// let my_filter = filter_fn(|metadata| { /// // Only enable spans or events with targets starting with `my_crate` /// // and levels at or below `INFO`. /// metadata.level() <= &Level::INFO && metadata.target().starts_with("my_crate") /// }) /// // Since the filter closure will only enable the `INFO` level and /// // below, set the max level hint /// .with_max_level_hint(LevelFilter::INFO); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// ``` /// /// [`Level`]: tracing_core::Level /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint pub fn with_max_level_hint(self, max_level_hint: impl Into) -> Self { Self { max_level_hint: Some(max_level_hint.into()), ..self } } #[inline] pub(in crate::filter) fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { let enabled = (self.enabled)(metadata); debug_assert!( !enabled || self.is_below_max_level(metadata), "FilterFn<{}> claimed it would only enable {:?} and below, \ but it enabled metadata with the {:?} level\nmetadata={:#?}", type_name::(), self.max_level_hint.unwrap(), metadata.level(), metadata, ); enabled } #[inline] pub(in crate::filter) fn is_callsite_enabled( &self, 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 // just return `Interest::always`/`Interest::never`. if (self.enabled)(metadata) { debug_assert!( self.is_below_max_level(metadata), "FilterFn<{}> claimed it was only interested in {:?} and below, \ but it enabled metadata with the {:?} level\nmetadata={:#?}", type_name::(), self.max_level_hint.unwrap(), metadata.level(), metadata, ); return Interest::always(); } Interest::never() } fn is_below_max_level(&self, metadata: &Metadata<'_>) -> bool { self.max_level_hint .as_ref() .map(|hint| metadata.level() <= hint) .unwrap_or(true) } } impl Layer for FilterFn where F: Fn(&Metadata<'_>) -> bool + 'static, S: Subscriber, { fn enabled(&self, metadata: &Metadata<'_>, _: Context<'_, S>) -> bool { self.is_enabled(metadata) } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.is_callsite_enabled(metadata) } fn max_level_hint(&self) -> Option { self.max_level_hint } } impl From for FilterFn where F: Fn(&Metadata<'_>) -> bool, { fn from(enabled: F) -> Self { Self::new(enabled) } } impl fmt::Debug for FilterFn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FilterFn") .field("enabled", &format_args!("{}", type_name::())) .field("max_level_hint", &self.max_level_hint) .finish() } } // === impl DynFilterFn == impl DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, { /// Constructs a [`Filter`] from a function or closure that returns `true` /// if a span or event should be enabled in the current [span /// context][`Context`]. /// /// Unlike [`FilterFn`], a `DynFilterFn` is constructed from a closure or /// function pointer that takes both the [`Metadata`] for a span or event /// *and* the current [`Context`]. This means that a [`DynFilterFn`] can /// choose whether to enable spans or events based on information about the /// _current_ span (or its parents). /// /// If this is *not* necessary, use [`FilterFn`] instead. /// /// See the [documentation on per-layer filtering][plf] for details on using /// [`Filter`]s. /// /// [`Filter`]: crate::layer::Filter /// [plf]: crate::layer#per-layer-filtering /// [`Context`]: crate::layer::Context /// [`Metadata`]: tracing_core::Metadata /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter::DynFilterFn, /// util::SubscriberInitExt, /// }; /// /// // Only enable spans or events within a span named "interesting_span". /// let my_filter = DynFilterFn::new(|metadata, cx| { /// // If this *is* "interesting_span", make sure to enable it. /// if metadata.is_span() && metadata.name() == "interesting_span" { /// return true; /// } /// /// // Otherwise, are we in an interesting span? /// if let Some(current_span) = cx.lookup_current() { /// return current_span.name() == "interesting_span"; /// } /// /// false /// }); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// /// // This event will not be enabled. /// tracing::info!("something happened"); /// /// tracing::info_span!("interesting_span").in_scope(|| { /// // This event will be enabled. /// tracing::debug!("something else happened"); /// }); /// ``` pub fn new(enabled: F) -> Self { Self { enabled, register_callsite: None, max_level_hint: None, _s: PhantomData, } } } impl DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, { /// Sets the highest verbosity [`Level`] the filter function will enable. /// /// The value passed to this method will be returned by this `DynFilterFn`'s /// [`Filter::max_level_hint`] method. /// /// If the provided function will not enable all levels, it is recommended /// to call this method to configure it with the most verbose level it will /// enable. /// /// # Examples /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter::{DynFilterFn, LevelFilter}, /// util::SubscriberInitExt, /// }; /// use tracing_core::Level; /// /// // Only enable spans or events with levels at or below `INFO`, if /// // we are inside a span called "interesting_span". /// let my_filter = DynFilterFn::new(|metadata, cx| { /// // If the level is greater than INFO, disable it. /// if metadata.level() > &Level::INFO { /// return false; /// } /// /// // If any span in the current scope is named "interesting_span", /// // enable this span or event. /// for span in cx.lookup_current().iter().flat_map(|span| span.scope()) { /// if span.name() == "interesting_span" { /// return true; /// } /// } /// /// // Otherwise, disable it. /// false /// }) /// // Since the filter closure will only enable the `INFO` level and /// // below, set the max level hint /// .with_max_level_hint(LevelFilter::INFO); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// ``` /// /// [`Level`]: tracing_core::Level /// [`Filter::max_level_hint`]: crate::layer::Filter::max_level_hint pub fn with_max_level_hint(self, max_level_hint: impl Into) -> Self { Self { max_level_hint: Some(max_level_hint.into()), ..self } } /// Adds a function for filtering callsites to this filter. /// /// When this filter's [`Filter::callsite_enabled`][cse] method is called, /// the provided function will be used rather than the default. /// /// By default, `DynFilterFn` assumes that, because the filter _may_ depend /// dynamically on the current [span context], its result should never be /// cached. However, some filtering strategies may require dynamic information /// from the current span context in *some* cases, but are able to make /// static filtering decisions from [`Metadata`] alone in others. /// /// For example, consider the filter given in the example for /// [`DynFilterFn::new`]. That filter enables all spans named /// "interesting_span", and any events and spans that occur inside of an /// interesting span. Since the span's name is part of its static /// [`Metadata`], the "interesting_span" can be enabled in /// [`callsite_enabled`][cse]: /// /// ``` /// use tracing_subscriber::{ /// layer::{Layer, SubscriberExt}, /// filter::DynFilterFn, /// util::SubscriberInitExt, /// }; /// use tracing_core::subscriber::Interest; /// /// // Only enable spans or events within a span named "interesting_span". /// let my_filter = DynFilterFn::new(|metadata, cx| { /// // If this *is* "interesting_span", make sure to enable it. /// if metadata.is_span() && metadata.name() == "interesting_span" { /// return true; /// } /// /// // Otherwise, are we in an interesting span? /// if let Some(current_span) = cx.lookup_current() { /// return current_span.name() == "interesting_span"; /// } /// /// false /// }).with_callsite_filter(|metadata| { /// // If this is an "interesting_span", we know we will always /// // enable it. /// if metadata.is_span() && metadata.name() == "interesting_span" { /// return Interest::always(); /// } /// /// // Otherwise, it depends on whether or not we're in an interesting /// // span. You'll have to ask us again for each span/event! /// Interest::sometimes() /// }); /// /// let my_layer = tracing_subscriber::fmt::layer(); /// /// tracing_subscriber::registry() /// .with(my_layer.with_filter(my_filter)) /// .init(); /// ``` /// /// [cse]: crate::layer::Filter::callsite_enabled /// [`enabled`]: crate::layer::Filter::enabled /// [`Metadata`]: tracing_core::Metadata /// [span context]: crate::layer::Context pub fn with_callsite_filter(self, callsite_enabled: R2) -> DynFilterFn where R2: Fn(&'static Metadata<'static>) -> Interest, { let register_callsite = Some(callsite_enabled); let DynFilterFn { enabled, max_level_hint, _s, .. } = self; DynFilterFn { enabled, register_callsite, max_level_hint, _s, } } fn default_callsite_enabled(&self, metadata: &Metadata<'_>) -> Interest { // If it's below the configured max level, assume that `enabled` will // never enable it... if !is_below_max_level(&self.max_level_hint, metadata) { debug_assert!( !(self.enabled)(metadata, &Context::none()), "DynFilterFn<{}> claimed it would only enable {:?} and below, \ but it enabled metadata with the {:?} level\nmetadata={:#?}", type_name::(), self.max_level_hint.unwrap(), metadata.level(), metadata, ); return Interest::never(); } // Otherwise, since this `enabled` function is dynamic and depends on // the current context, we don't know whether this span or event will be // enabled or not. Ask again every time it's recorded! Interest::sometimes() } } impl DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, R: Fn(&'static Metadata<'static>) -> Interest, { #[inline] fn is_enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { let enabled = (self.enabled)(metadata, cx); debug_assert!( !enabled || is_below_max_level(&self.max_level_hint, metadata), "DynFilterFn<{}> claimed it would only enable {:?} and below, \ but it enabled metadata with the {:?} level\nmetadata={:#?}", type_name::(), self.max_level_hint.unwrap(), metadata.level(), metadata, ); enabled } #[inline] fn is_callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { let interest = self .register_callsite .as_ref() .map(|callsite_enabled| callsite_enabled(metadata)) .unwrap_or_else(|| self.default_callsite_enabled(metadata)); debug_assert!( interest.is_never() || is_below_max_level(&self.max_level_hint, metadata), "DynFilterFn<{}, {}> claimed it was only interested in {:?} and below, \ but it enabled metadata with the {:?} level\nmetadata={:#?}", type_name::(), type_name::(), self.max_level_hint.unwrap(), metadata.level(), metadata, ); interest } } impl Layer for DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool + 'static, R: Fn(&'static Metadata<'static>) -> Interest + 'static, S: Subscriber, { fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { self.is_enabled(metadata, &cx) } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.is_callsite_enabled(metadata) } fn max_level_hint(&self) -> Option { self.max_level_hint } } impl fmt::Debug for DynFilterFn { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut s = f.debug_struct("DynFilterFn"); s.field("enabled", &format_args!("{}", type_name::())); if self.register_callsite.is_some() { s.field( "register_callsite", &format_args!("Some({})", type_name::()), ); } else { s.field("register_callsite", &format_args!("None")); } s.field("max_level_hint", &self.max_level_hint).finish() } } impl Clone for DynFilterFn where F: Clone, R: Clone, { fn clone(&self) -> Self { Self { enabled: self.enabled.clone(), register_callsite: self.register_callsite.clone(), max_level_hint: self.max_level_hint, _s: PhantomData, } } } impl From for DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, { fn from(f: F) -> Self { Self::new(f) } } // === PLF impls === feature! { #![all(feature = "registry", feature = "std")] use crate::layer::Filter; impl Filter for FilterFn where F: Fn(&Metadata<'_>) -> bool, { fn enabled(&self, metadata: &Metadata<'_>, _: &Context<'_, S>) -> bool { self.is_enabled(metadata) } fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { self.is_callsite_enabled(metadata) } fn max_level_hint(&self) -> Option { self.max_level_hint } } impl Filter for DynFilterFn where F: Fn(&Metadata<'_>, &Context<'_, S>) -> bool, R: Fn(&'static Metadata<'static>) -> Interest, { fn enabled(&self, metadata: &Metadata<'_>, cx: &Context<'_, S>) -> bool { self.is_enabled(metadata, cx) } fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { self.is_callsite_enabled(metadata) } fn max_level_hint(&self) -> Option { self.max_level_hint } } } fn is_below_max_level(hint: &Option, metadata: &Metadata<'_>) -> bool { hint.as_ref() .map(|hint| metadata.level() <= hint) .unwrap_or(true) } tracing-subscriber-0.3.18/src/filter/layer_filters/combinator.rs000064400000000000000000000372261046102023000231420ustar 00000000000000//! Filter combinators use crate::layer::{Context, Filter}; use std::{cmp, fmt, marker::PhantomData}; use tracing_core::{ span::{Attributes, Id, Record}, subscriber::Interest, LevelFilter, Metadata, }; /// Combines two [`Filter`]s so that spans and events are enabled if and only if /// *both* filters return `true`. /// /// This type is typically returned by the [`FilterExt::and`] method. See that /// method's documentation for details. /// /// [`Filter`]: crate::layer::Filter /// [`FilterExt::and`]: crate::filter::FilterExt::and pub struct And { a: A, b: B, _s: PhantomData, } /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter /// returns `true`. /// /// This type is typically returned by the [`FilterExt::or`] method. See that /// method's documentation for details. /// /// [`Filter`]: crate::layer::Filter /// [`FilterExt::or`]: crate::filter::FilterExt::or pub struct Or { a: A, b: B, _s: PhantomData, } /// Inverts the result of a [`Filter`]. /// /// If the wrapped filter would enable a span or event, it will be disabled. If /// it would disable a span or event, that span or event will be enabled. /// /// This type is typically returned by the [`FilterExt::not`] method. See that /// method's documentation for details. /// /// [`Filter`]: crate::layer::Filter /// [`FilterExt::not`]: crate::filter::FilterExt::not pub struct Not { a: A, _s: PhantomData, } // === impl And === impl And where A: Filter, B: Filter, { /// Combines two [`Filter`]s so that spans and events are enabled if and only if /// *both* filters return `true`. /// /// # Examples /// /// Enabling spans or events if they have both a particular target *and* are /// above a certain level: /// /// ```ignore /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, combinator::And}, /// prelude::*, /// }; /// /// // Enables spans and events with targets starting with `interesting_target`: /// let target_filter = filter_fn(|meta| { /// meta.target().starts_with("interesting_target") /// }); /// /// // Enables spans and events with levels `INFO` and below: /// let level_filter = LevelFilter::INFO; /// /// // Combine the two filters together so that a span or event is only enabled /// // if *both* filters would enable it: /// let filter = And::new(level_filter, target_filter); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// /// // This event will *not* be enabled: /// tracing::info!("an event with an uninteresting target"); /// /// // This event *will* be enabled: /// tracing::info!(target: "interesting_target", "a very interesting event"); /// /// // This event will *not* be enabled: /// tracing::debug!(target: "interesting_target", "interesting debug event..."); /// ``` /// /// [`Filter`]: crate::layer::Filter pub(crate) fn new(a: A, b: B) -> Self { Self { a, b, _s: PhantomData, } } } impl Filter for And where A: Filter, B: Filter, { #[inline] fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { self.a.enabled(meta, cx) && self.b.enabled(meta, cx) } fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { let a = self.a.callsite_enabled(meta); if a.is_never() { return a; } let b = self.b.callsite_enabled(meta); if !b.is_always() { return b; } a } fn max_level_hint(&self) -> Option { // If either hint is `None`, return `None`. Otherwise, return the most restrictive. cmp::min(self.a.max_level_hint(), self.b.max_level_hint()) } #[inline] fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { self.a.event_enabled(event, cx) && self.b.event_enabled(event, cx) } #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx.clone()); self.b.on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { self.a.on_record(id, values, ctx.clone()); self.b.on_record(id, values, ctx); } #[inline] fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_enter(id, ctx.clone()); self.b.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_exit(id, ctx.clone()); self.b.on_exit(id, ctx); } #[inline] fn on_close(&self, id: Id, ctx: Context<'_, S>) { self.a.on_close(id.clone(), ctx.clone()); self.b.on_close(id, ctx); } } impl Clone for And where A: Clone, B: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), _s: PhantomData, } } } impl fmt::Debug for And where A: fmt::Debug, B: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("And") .field("a", &self.a) .field("b", &self.b) .finish() } } // === impl Or === impl Or where A: Filter, B: Filter, { /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter /// returns `true`. /// /// # Examples /// /// Enabling spans and events at the `INFO` level and above, and all spans /// and events with a particular target: /// /// ```ignore /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, combinator::Or}, /// prelude::*, /// }; /// /// // Enables spans and events with targets starting with `interesting_target`: /// let target_filter = filter_fn(|meta| { /// meta.target().starts_with("interesting_target") /// }); /// /// // Enables spans and events with levels `INFO` and below: /// let level_filter = LevelFilter::INFO; /// /// // Combine the two filters together so that a span or event is enabled /// // if it is at INFO or lower, or if it has a target starting with /// // `interesting_target`. /// let filter = Or::new(level_filter, target_filter); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// /// // This event will *not* be enabled: /// tracing::debug!("an uninteresting event"); /// /// // This event *will* be enabled: /// tracing::info!("an uninteresting INFO event"); /// /// // This event *will* be enabled: /// tracing::info!(target: "interesting_target", "a very interesting event"); /// /// // This event *will* be enabled: /// tracing::debug!(target: "interesting_target", "interesting debug event..."); /// ``` /// /// Enabling a higher level for a particular target by using `Or` in /// conjunction with the [`And`] combinator: /// /// ```ignore /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, combinator}, /// prelude::*, /// }; /// /// // This filter will enable spans and events with targets beginning with /// // `my_crate`: /// let my_crate = filter_fn(|meta| { /// meta.target().starts_with("my_crate") /// }); /// /// // Combine the `my_crate` filter with a `LevelFilter` to produce a filter /// // that will enable the `INFO` level and lower for spans and events with /// // `my_crate` targets: /// let filter = combinator::And::new(my_crate, LevelFilter::INFO); /// /// // If a span or event *doesn't* have a target beginning with /// // `my_crate`, enable it if it has the `WARN` level or lower: /// // let filter = combinator::Or::new(filter, LevelFilter::WARN); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// ``` /// /// [`Filter`]: crate::layer::Filter pub(crate) fn new(a: A, b: B) -> Self { Self { a, b, _s: PhantomData, } } } impl Filter for Or where A: Filter, B: Filter, { #[inline] fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { self.a.enabled(meta, cx) || self.b.enabled(meta, cx) } fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { let a = self.a.callsite_enabled(meta); let b = self.b.callsite_enabled(meta); // If either filter will always enable the span or event, return `always`. if a.is_always() || b.is_always() { return Interest::always(); } // Okay, if either filter will sometimes enable the span or event, // return `sometimes`. if a.is_sometimes() || b.is_sometimes() { return Interest::sometimes(); } debug_assert!( a.is_never() && b.is_never(), "if neither filter was `always` or `sometimes`, both must be `never` (a={:?}; b={:?})", a, b, ); Interest::never() } fn max_level_hint(&self) -> Option { // If either hint is `None`, return `None`. Otherwise, return the less restrictive. Some(cmp::max(self.a.max_level_hint()?, self.b.max_level_hint()?)) } #[inline] fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { self.a.event_enabled(event, cx) || self.b.event_enabled(event, cx) } #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx.clone()); self.b.on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { self.a.on_record(id, values, ctx.clone()); self.b.on_record(id, values, ctx); } #[inline] fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_enter(id, ctx.clone()); self.b.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_exit(id, ctx.clone()); self.b.on_exit(id, ctx); } #[inline] fn on_close(&self, id: Id, ctx: Context<'_, S>) { self.a.on_close(id.clone(), ctx.clone()); self.b.on_close(id, ctx); } } impl Clone for Or where A: Clone, B: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), b: self.b.clone(), _s: PhantomData, } } } impl fmt::Debug for Or where A: fmt::Debug, B: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Or") .field("a", &self.a) .field("b", &self.b) .finish() } } // === impl Not === impl Not where A: Filter, { /// Inverts the result of a [`Filter`]. /// /// If the wrapped filter would enable a span or event, it will be disabled. If /// it would disable a span or event, that span or event will be enabled. /// /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as /// filters which do not implement filtering on event field values will return /// the default `true` even for events that their [`enabled`] method disables. /// /// Consider a normal filter defined as: /// /// ```ignore (pseudo-code) /// // for spans /// match callsite_enabled() { /// ALWAYS => on_span(), /// SOMETIMES => if enabled() { on_span() }, /// NEVER => (), /// } /// // for events /// match callsite_enabled() { /// ALWAYS => on_event(), /// SOMETIMES => if enabled() && event_enabled() { on_event() }, /// NEVER => (), /// } /// ``` /// /// and an inverted filter defined as: /// /// ```ignore (pseudo-code) /// // for spans /// match callsite_enabled() { /// ALWAYS => (), /// SOMETIMES => if !enabled() { on_span() }, /// NEVER => on_span(), /// } /// // for events /// match callsite_enabled() { /// ALWAYS => (), /// SOMETIMES => if !enabled() { on_event() }, /// NEVER => on_event(), /// } /// ``` /// /// A proper inversion would do `!(enabled() && event_enabled())` (or /// `!enabled() || !event_enabled()`), but because of the implicit `&&` /// relation between `enabled` and `event_enabled`, it is difficult to /// short circuit and not call the wrapped `event_enabled`. /// /// A combinator which remembers the result of `enabled` in order to call /// `event_enabled` only when `enabled() == true` is possible, but requires /// additional thread-local mutable state to support a very niche use case. // // Also, it'd mean the wrapped layer's `enabled()` always gets called and // globally applied to events where it doesn't today, since we can't know // what `event_enabled` will say until we have the event to call it with. /// /// [`Filter`]: crate::layer::Filter /// [`enabled`]: crate::layer::Filter::enabled /// [`event_enabled`]: crate::layer::Filter::event_enabled /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled pub(crate) fn new(a: A) -> Self { Self { a, _s: PhantomData } } } impl Filter for Not where A: Filter, { #[inline] fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { !self.a.enabled(meta, cx) } fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { match self.a.callsite_enabled(meta) { i if i.is_always() => Interest::never(), i if i.is_never() => Interest::always(), _ => Interest::sometimes(), } } fn max_level_hint(&self) -> Option { // TODO(eliza): figure this out??? None } #[inline] fn event_enabled(&self, event: &tracing_core::Event<'_>, cx: &Context<'_, S>) -> bool { // Never disable based on event_enabled; we "disabled" it in `enabled`, // so the `not` has already been applied and filtered this not out. let _ = (event, cx); true } #[inline] fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { self.a.on_new_span(attrs, id, ctx); } #[inline] fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { self.a.on_record(id, values, ctx.clone()); } #[inline] fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { self.a.on_exit(id, ctx); } #[inline] fn on_close(&self, id: Id, ctx: Context<'_, S>) { self.a.on_close(id, ctx); } } impl Clone for Not where A: Clone, { fn clone(&self) -> Self { Self { a: self.a.clone(), _s: PhantomData, } } } impl fmt::Debug for Not where A: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("Not").field(&self.a).finish() } } tracing-subscriber-0.3.18/src/filter/layer_filters/mod.rs000064400000000000000000001344201046102023000215560ustar 00000000000000//! ## Per-Layer Filtering //! //! Per-layer filters permit individual `Layer`s to have their own filter //! configurations without interfering with other `Layer`s. //! //! This module is not public; the public APIs defined in this module are //! re-exported in the top-level `filter` module. Therefore, this documentation //! primarily concerns the internal implementation details. For the user-facing //! public API documentation, see the individual public types in this module, as //! well as the, see the `Layer` trait documentation's [per-layer filtering //! section]][1]. //! //! ## How does per-layer filtering work? //! //! As described in the API documentation, the [`Filter`] trait defines a //! filtering strategy for a per-layer filter. We expect there will be a variety //! of implementations of [`Filter`], both in `tracing-subscriber` and in user //! code. //! //! To actually *use* a [`Filter`] implementation, it is combined with a //! [`Layer`] by the [`Filtered`] struct defined in this module. [`Filtered`] //! implements [`Layer`] by calling into the wrapped [`Layer`], or not, based on //! the filtering strategy. While there will be a variety of types that implement //! [`Filter`], all actual *uses* of per-layer filtering will occur through the //! [`Filtered`] struct. Therefore, most of the implementation details live //! there. //! //! [1]: crate::layer#per-layer-filtering //! [`Filter`]: crate::layer::Filter use crate::{ filter::LevelFilter, layer::{self, Context, Layer}, registry, }; use std::{ any::TypeId, cell::{Cell, RefCell}, fmt, marker::PhantomData, ops::Deref, sync::Arc, thread_local, }; use tracing_core::{ span, subscriber::{Interest, Subscriber}, Dispatch, Event, Metadata, }; pub mod combinator; /// A [`Layer`] that wraps an inner [`Layer`] and adds a [`Filter`] which /// controls what spans and events are enabled for that layer. /// /// This is returned by the [`Layer::with_filter`] method. See the /// [documentation on per-layer filtering][plf] for details. /// /// [`Filter`]: crate::layer::Filter /// [plf]: crate::layer#per-layer-filtering #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] #[derive(Clone)] pub struct Filtered { filter: F, layer: L, id: MagicPlfDowncastMarker, _s: PhantomData, } /// Uniquely identifies an individual [`Filter`] instance in the context of /// a [`Subscriber`]. /// /// When adding a [`Filtered`] [`Layer`] to a [`Subscriber`], the [`Subscriber`] /// generates a `FilterId` for that [`Filtered`] layer. The [`Filtered`] layer /// will then use the generated ID to query whether a particular span was /// previously enabled by that layer's [`Filter`]. /// /// **Note**: Currently, the [`Registry`] type provided by this crate is the /// **only** [`Subscriber`] implementation capable of participating in per-layer /// filtering. Therefore, the `FilterId` type cannot currently be constructed by /// code outside of `tracing-subscriber`. In the future, new APIs will be added to `tracing-subscriber` to /// allow non-Registry [`Subscriber`]s to also participate in per-layer /// filtering. When those APIs are added, subscribers will be responsible /// for generating and assigning `FilterId`s. /// /// [`Filter`]: crate::layer::Filter /// [`Subscriber`]: tracing_core::Subscriber /// [`Layer`]: crate::layer::Layer /// [`Registry`]: crate::registry::Registry #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] #[derive(Copy, Clone)] pub struct FilterId(u64); /// A bitmap tracking which [`FilterId`]s have enabled a given span or /// event. /// /// This is currently a private type that's used exclusively by the /// [`Registry`]. However, in the future, this may become a public API, in order /// to allow user subscribers to host [`Filter`]s. /// /// [`Registry`]: crate::Registry /// [`Filter`]: crate::layer::Filter #[derive(Default, Copy, Clone, Eq, PartialEq)] pub(crate) struct FilterMap { bits: u64, } /// The current state of `enabled` calls to per-layer filters on this /// thread. /// /// When `Filtered::enabled` is called, the filter will set the bit /// corresponding to its ID if the filter will disable the event/span being /// filtered. When the event or span is recorded, the per-layer filter will /// check its bit to determine if it disabled that event or span, and skip /// forwarding the event or span to the inner layer if the bit is set. Once /// a span or event has been skipped by a per-layer filter, it unsets its /// bit, so that the `FilterMap` has been cleared for the next set of /// `enabled` calls. /// /// FilterState is also read by the `Registry`, for two reasons: /// /// 1. When filtering a span, the Registry must store the `FilterMap` /// generated by `Filtered::enabled` calls for that span as part of the /// span's per-span data. This allows `Filtered` layers to determine /// whether they had previously disabled a given span, and avoid showing it /// to the wrapped layer if it was disabled. /// /// This allows `Filtered` layers to also filter out the spans they /// disable from span traversals (such as iterating over parents, etc). /// 2. If all the bits are set, then every per-layer filter has decided it /// doesn't want to enable that span or event. In that case, the /// `Registry`'s `enabled` method will return `false`, so that /// recording a span or event can be skipped entirely. #[derive(Debug)] pub(crate) struct FilterState { enabled: Cell, // TODO(eliza): `Interest`s should _probably_ be `Copy`. The only reason // they're not is our Obsessive Commitment to Forwards-Compatibility. If // this changes in tracing-core`, we can make this a `Cell` rather than // `RefCell`... interest: RefCell>, #[cfg(debug_assertions)] counters: DebugCounters, } /// Extra counters added to `FilterState` used only to make debug assertions. #[cfg(debug_assertions)] #[derive(Debug, Default)] struct DebugCounters { /// How many per-layer filters have participated in the current `enabled` /// call? in_filter_pass: Cell, /// How many per-layer filters have participated in the current `register_callsite` /// call? in_interest_pass: Cell, } thread_local! { pub(crate) static FILTERING: FilterState = FilterState::new(); } /// Extension trait adding [combinators] for combining [`Filter`]. /// /// [combinators]: crate::filter::combinator /// [`Filter`]: crate::layer::Filter pub trait FilterExt: layer::Filter { /// Combines this [`Filter`] with another [`Filter`] s so that spans and /// events are enabled if and only if *both* filters return `true`. /// /// # Examples /// /// Enabling spans or events if they have both a particular target *and* are /// above a certain level: /// /// ``` /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, FilterExt}, /// prelude::*, /// }; /// /// // Enables spans and events with targets starting with `interesting_target`: /// let target_filter = filter_fn(|meta| { /// meta.target().starts_with("interesting_target") /// }); /// /// // Enables spans and events with levels `INFO` and below: /// let level_filter = LevelFilter::INFO; /// /// // Combine the two filters together, returning a filter that only enables /// // spans and events that *both* filters will enable: /// let filter = target_filter.and(level_filter); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// /// // This event will *not* be enabled: /// tracing::info!("an event with an uninteresting target"); /// /// // This event *will* be enabled: /// tracing::info!(target: "interesting_target", "a very interesting event"); /// /// // This event will *not* be enabled: /// tracing::debug!(target: "interesting_target", "interesting debug event..."); /// ``` /// /// [`Filter`]: crate::layer::Filter fn and(self, other: B) -> combinator::And where Self: Sized, B: layer::Filter, { combinator::And::new(self, other) } /// Combines two [`Filter`]s so that spans and events are enabled if *either* filter /// returns `true`. /// /// # Examples /// /// Enabling spans and events at the `INFO` level and above, and all spans /// and events with a particular target: /// ``` /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, FilterExt}, /// prelude::*, /// }; /// /// // Enables spans and events with targets starting with `interesting_target`: /// let target_filter = filter_fn(|meta| { /// meta.target().starts_with("interesting_target") /// }); /// /// // Enables spans and events with levels `INFO` and below: /// let level_filter = LevelFilter::INFO; /// /// // Combine the two filters together so that a span or event is enabled /// // if it is at INFO or lower, or if it has a target starting with /// // `interesting_target`. /// let filter = level_filter.or(target_filter); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// /// // This event will *not* be enabled: /// tracing::debug!("an uninteresting event"); /// /// // This event *will* be enabled: /// tracing::info!("an uninteresting INFO event"); /// /// // This event *will* be enabled: /// tracing::info!(target: "interesting_target", "a very interesting event"); /// /// // This event *will* be enabled: /// tracing::debug!(target: "interesting_target", "interesting debug event..."); /// ``` /// /// Enabling a higher level for a particular target by using `or` in /// conjunction with the [`and`] combinator: /// /// ``` /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, FilterExt}, /// prelude::*, /// }; /// /// // This filter will enable spans and events with targets beginning with /// // `my_crate`: /// let my_crate = filter_fn(|meta| { /// meta.target().starts_with("my_crate") /// }); /// /// let filter = my_crate /// // Combine the `my_crate` filter with a `LevelFilter` to produce a /// // filter that will enable the `INFO` level and lower for spans and /// // events with `my_crate` targets: /// .and(LevelFilter::INFO) /// // If a span or event *doesn't* have a target beginning with /// // `my_crate`, enable it if it has the `WARN` level or lower: /// .or(LevelFilter::WARN); /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// ``` /// /// [`Filter`]: crate::layer::Filter /// [`and`]: FilterExt::and fn or(self, other: B) -> combinator::Or where Self: Sized, B: layer::Filter, { combinator::Or::new(self, other) } /// Inverts `self`, returning a filter that enables spans and events only if /// `self` would *not* enable them. /// /// This inverts the values returned by the [`enabled`] and [`callsite_enabled`] /// methods on the wrapped filter; it does *not* invert [`event_enabled`], as /// filters which do not implement filtering on event field values will return /// the default `true` even for events that their [`enabled`] method disables. /// /// Consider a normal filter defined as: /// /// ```ignore (pseudo-code) /// // for spans /// match callsite_enabled() { /// ALWAYS => on_span(), /// SOMETIMES => if enabled() { on_span() }, /// NEVER => (), /// } /// // for events /// match callsite_enabled() { /// ALWAYS => on_event(), /// SOMETIMES => if enabled() && event_enabled() { on_event() }, /// NEVER => (), /// } /// ``` /// /// and an inverted filter defined as: /// /// ```ignore (pseudo-code) /// // for spans /// match callsite_enabled() { /// ALWAYS => (), /// SOMETIMES => if !enabled() { on_span() }, /// NEVER => on_span(), /// } /// // for events /// match callsite_enabled() { /// ALWAYS => (), /// SOMETIMES => if !enabled() { on_event() }, /// NEVER => on_event(), /// } /// ``` /// /// A proper inversion would do `!(enabled() && event_enabled())` (or /// `!enabled() || !event_enabled()`), but because of the implicit `&&` /// relation between `enabled` and `event_enabled`, it is difficult to /// short circuit and not call the wrapped `event_enabled`. /// /// A combinator which remembers the result of `enabled` in order to call /// `event_enabled` only when `enabled() == true` is possible, but requires /// additional thread-local mutable state to support a very niche use case. // // Also, it'd mean the wrapped layer's `enabled()` always gets called and // globally applied to events where it doesn't today, since we can't know // what `event_enabled` will say until we have the event to call it with. /// /// [`Filter`]: crate::layer::Filter /// [`enabled`]: crate::layer::Filter::enabled /// [`event_enabled`]: crate::layer::Filter::event_enabled /// [`callsite_enabled`]: crate::layer::Filter::callsite_enabled fn not(self) -> combinator::Not where Self: Sized, { combinator::Not::new(self) } /// [Boxes] `self`, erasing its concrete type. /// /// This is equivalent to calling [`Box::new`], but in method form, so that /// it can be used when chaining combinator methods. /// /// # Examples /// /// When different combinations of filters are used conditionally, they may /// have different types. For example, the following code won't compile, /// since the `if` and `else` clause produce filters of different types: /// /// ```compile_fail /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, FilterExt}, /// prelude::*, /// }; /// /// let enable_bar_target: bool = // ... /// # false; /// /// let filter = if enable_bar_target { /// filter_fn(|meta| meta.target().starts_with("foo")) /// // If `enable_bar_target` is true, add a `filter_fn` enabling /// // spans and events with the target `bar`: /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) /// .and(LevelFilter::INFO) /// } else { /// filter_fn(|meta| meta.target().starts_with("foo")) /// .and(LevelFilter::INFO) /// }; /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// ``` /// /// By using `boxed`, the types of the two different branches can be erased, /// so the assignment to the `filter` variable is valid (as both branches /// have the type `Box + Send + Sync + 'static>`). The /// following code *does* compile: /// /// ``` /// use tracing_subscriber::{ /// filter::{filter_fn, LevelFilter, FilterExt}, /// prelude::*, /// }; /// /// let enable_bar_target: bool = // ... /// # false; /// /// let filter = if enable_bar_target { /// filter_fn(|meta| meta.target().starts_with("foo")) /// .or(filter_fn(|meta| meta.target().starts_with("bar"))) /// .and(LevelFilter::INFO) /// // Boxing the filter erases its type, so both branches now /// // have the same type. /// .boxed() /// } else { /// filter_fn(|meta| meta.target().starts_with("foo")) /// .and(LevelFilter::INFO) /// .boxed() /// }; /// /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer().with_filter(filter)) /// .init(); /// ``` /// /// [Boxes]: std::boxed /// [`Box::new`]: std::boxed::Box::new fn boxed(self) -> Box + Send + Sync + 'static> where Self: Sized + Send + Sync + 'static, { Box::new(self) } } // === impl Filter === #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for LevelFilter { fn enabled(&self, meta: &Metadata<'_>, _: &Context<'_, S>) -> bool { meta.level() <= self } fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { if meta.level() <= self { Interest::always() } else { Interest::never() } } fn max_level_hint(&self) -> Option { Some(*self) } } macro_rules! filter_impl_body { () => { #[inline] fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool { self.deref().enabled(meta, cx) } #[inline] fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { self.deref().callsite_enabled(meta) } #[inline] 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) } }; } #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for Arc + Send + Sync + 'static> { filter_impl_body!(); } #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for Box + Send + Sync + 'static> { filter_impl_body!(); } // Implement Filter for Option where None => allow #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for Option where F: layer::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 { /// Wraps the provided [`Layer`] so that it is filtered by the given /// [`Filter`]. /// /// This is equivalent to calling the [`Layer::with_filter`] method. /// /// See the [documentation on per-layer filtering][plf] for details. /// /// [`Filter`]: crate::layer::Filter /// [plf]: crate::layer#per-layer-filtering pub fn new(layer: L, filter: F) -> Self { Self { layer, filter, id: MagicPlfDowncastMarker(FilterId::disabled()), _s: PhantomData, } } #[inline(always)] fn id(&self) -> FilterId { debug_assert!( !self.id.0.is_disabled(), "a `Filtered` layer was used, but it had no `FilterId`; \ was it registered with the subscriber?" ); self.id.0 } fn did_enable(&self, f: impl FnOnce()) { FILTERING.with(|filtering| filtering.did_enable(self.id(), f)) } /// Borrows the [`Filter`](crate::layer::Filter) used by this layer. pub fn filter(&self) -> &F { &self.filter } /// Mutably borrows the [`Filter`](crate::layer::Filter) used by this layer. /// /// When this layer can be mutably borrowed, this may be used to mutate the filter. /// Generally, this will primarily be used with the /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. /// /// # Examples /// /// ``` /// # use tracing::info; /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; /// # fn main() { /// let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN); /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); /// # /// # // specifying the Registry type is required /// # let _: &reload::Handle, /// # filter::LevelFilter, Registry>,Registry> /// # = &reload_handle; /// # /// info!("This will be ignored"); /// reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO); /// info!("This will be logged"); /// # } /// ``` pub fn filter_mut(&mut self) -> &mut F { &mut self.filter } /// Borrows the inner [`Layer`] wrapped by this `Filtered` layer. pub fn inner(&self) -> &L { &self.layer } /// Mutably borrows the inner [`Layer`] wrapped by this `Filtered` layer. /// /// This method is primarily expected to be used with the /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. /// /// # Examples /// /// ``` /// # use tracing::info; /// # use tracing_subscriber::{filter,fmt,reload,Registry,prelude::*}; /// # fn non_blocking(writer: T) -> (fn() -> std::io::Stdout) { /// # std::io::stdout /// # } /// # fn main() { /// let filtered_layer = fmt::layer().with_writer(non_blocking(std::io::stderr())).with_filter(filter::LevelFilter::INFO); /// let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); /// # /// # // specifying the Registry type is required /// # let _: &reload::Handle std::io::Stdout>, /// # filter::LevelFilter, Registry>, Registry> /// # = &reload_handle; /// # /// info!("This will be logged to stderr"); /// reload_handle.modify(|layer| *layer.inner_mut().writer_mut() = non_blocking(std::io::stdout())); /// info!("This will be logged to stdout"); /// # } /// ``` /// /// [`Layer`]: crate::layer::Layer pub fn inner_mut(&mut self) -> &mut L { &mut self.layer } } impl Layer for Filtered where S: Subscriber + for<'span> registry::LookupSpan<'span> + 'static, F: layer::Filter + 'static, L: Layer, { fn on_register_dispatch(&self, subscriber: &Dispatch) { self.layer.on_register_dispatch(subscriber); } fn on_layer(&mut self, subscriber: &mut S) { self.id = MagicPlfDowncastMarker(subscriber.register_filter()); self.layer.on_layer(subscriber); } // TODO(eliza): can we figure out a nice way to make the `Filtered` layer // not call `is_enabled_for` in hooks that the inner layer doesn't actually // have real implementations of? probably not... // // 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? fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { let interest = self.filter.callsite_enabled(metadata); // If the filter didn't disable the callsite, allow the inner layer to // register it — since `register_callsite` is also used for purposes // such as reserving/caching per-callsite data, we want the inner layer // to be able to perform any other registration steps. However, we'll // ignore its `Interest`. if !interest.is_never() { self.layer.register_callsite(metadata); } // Add our `Interest` to the current sum of per-layer filter `Interest`s // for this callsite. FILTERING.with(|filtering| filtering.add_interest(interest)); // don't short circuit! if the stack consists entirely of `Layer`s with // per-layer filters, the `Registry` will return the actual `Interest` // value that's the sum of all the `register_callsite` calls to those // per-layer filters. if we returned an actual `never` interest here, a // `Layered` layer would short-circuit and not allow any `Filtered` // layers below us if _they_ are interested in the callsite. Interest::always() } fn enabled(&self, metadata: &Metadata<'_>, cx: Context<'_, S>) -> bool { let cx = cx.with_filter(self.id()); let enabled = self.filter.enabled(metadata, &cx); FILTERING.with(|filtering| filtering.set(self.id(), enabled)); if enabled { // If the filter enabled this metadata, ask the wrapped layer if // _it_ wants it --- it might have a global filter. self.layer.enabled(metadata, cx) } else { // Otherwise, return `true`. The _per-layer_ filter disabled this // metadata, but returning `false` in `Layer::enabled` will // short-circuit and globally disable the span or event. This is // *not* what we want for per-layer filters, as other layers may // still want this event. Returning `true` here means we'll continue // asking the next layer in the stack. // // Once all per-layer filters have been evaluated, the `Registry` // at the root of the stack will return `false` from its `enabled` // method if *every* per-layer filter disabled this metadata. // Otherwise, the individual per-layer filters will skip the next // `new_span` or `on_event` call for their layer if *they* disabled // the span or event, but it was not globally disabled. true } } fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, cx: Context<'_, S>) { self.did_enable(|| { let cx = cx.with_filter(self.id()); self.filter.on_new_span(attrs, id, cx.clone()); self.layer.on_new_span(attrs, id, cx); }) } #[doc(hidden)] fn max_level_hint(&self) -> Option { self.filter.max_level_hint() } fn on_record(&self, span: &span::Id, values: &span::Record<'_>, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(span, self.id()) { self.filter.on_record(span, values, cx.clone()); self.layer.on_record(span, values, cx) } } fn on_follows_from(&self, span: &span::Id, follows: &span::Id, cx: Context<'_, S>) { // only call `on_follows_from` if both spans are enabled by us if cx.is_enabled_for(span, self.id()) && cx.is_enabled_for(follows, self.id()) { self.layer .on_follows_from(span, follows, cx.with_filter(self.id())) } } fn event_enabled(&self, event: &Event<'_>, cx: Context<'_, S>) -> bool { let cx = cx.with_filter(self.id()); let enabled = FILTERING .with(|filtering| filtering.and(self.id(), || self.filter.event_enabled(event, &cx))); if enabled { // If the filter enabled this event, ask the wrapped subscriber if // _it_ wants it --- it might have a global filter. self.layer.event_enabled(event, cx) } else { // Otherwise, return `true`. See the comment in `enabled` for why this // is necessary. true } } fn on_event(&self, event: &Event<'_>, cx: Context<'_, S>) { self.did_enable(|| { self.layer.on_event(event, cx.with_filter(self.id())); }) } fn on_enter(&self, id: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(id, self.id()) { self.filter.on_enter(id, cx.clone()); self.layer.on_enter(id, cx); } } fn on_exit(&self, id: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(id, self.id()) { self.filter.on_exit(id, cx.clone()); self.layer.on_exit(id, cx); } } fn on_close(&self, id: span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(&id, self.id()) { self.filter.on_close(id.clone(), cx.clone()); self.layer.on_close(id, cx); } } // XXX(eliza): the existence of this method still makes me sad... fn on_id_change(&self, old: &span::Id, new: &span::Id, cx: Context<'_, S>) { if let Some(cx) = cx.if_enabled_for(old, self.id()) { self.layer.on_id_change(old, new, cx) } } #[doc(hidden)] #[inline] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { match id { id if id == TypeId::of::() => Some(self as *const _ as *const ()), id if id == TypeId::of::() => Some(&self.layer as *const _ as *const ()), id if id == TypeId::of::() => Some(&self.filter as *const _ as *const ()), id if id == TypeId::of::() => { Some(&self.id as *const _ as *const ()) } _ => self.layer.downcast_raw(id), } } } impl fmt::Debug for Filtered where F: fmt::Debug, L: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Filtered") .field("filter", &self.filter) .field("layer", &self.layer) .field("id", &self.id) .finish() } } // === impl FilterId === impl FilterId { const fn disabled() -> Self { Self(std::u64::MAX) } /// Returns a `FilterId` that will consider _all_ spans enabled. pub(crate) const fn none() -> Self { Self(0) } pub(crate) fn new(id: u8) -> Self { assert!(id < 64, "filter IDs may not be greater than 64"); Self(1 << id as usize) } /// Combines two `FilterId`s, returning a new `FilterId` that will match a /// [`FilterMap`] where the span was disabled by _either_ this `FilterId` /// *or* the combined `FilterId`. /// /// This method is called by [`Context`]s when adding the `FilterId` of a /// [`Filtered`] layer to the context. /// /// This is necessary for cases where we have a tree of nested [`Filtered`] /// layers, like this: /// /// ```text /// Filtered { /// filter1, /// Layered { /// layer1, /// Filtered { /// filter2, /// layer2, /// }, /// } /// ``` /// /// We want `layer2` to be affected by both `filter1` _and_ `filter2`. /// Without combining `FilterId`s, this works fine when filtering /// `on_event`/`new_span`, because the outer `Filtered` layer (`filter1`) /// won't call the inner layer's `on_event` or `new_span` callbacks if it /// disabled the event/span. /// /// However, it _doesn't_ work when filtering span lookups and traversals /// (e.g. `scope`). This is because the [`Context`] passed to `layer2` /// would set its filter ID to the filter ID of `filter2`, and would skip /// spans that were disabled by `filter2`. However, what if a span was /// disabled by `filter1`? We wouldn't see it in `new_span`, but we _would_ /// see it in lookups and traversals...which we don't want. /// /// When a [`Filtered`] layer adds its ID to a [`Context`], it _combines_ it /// with any previous filter ID that the context had, rather than replacing /// it. That way, `layer2`'s context will check if a span was disabled by /// `filter1` _or_ `filter2`. The way we do this, instead of representing /// `FilterId`s as a number number that we shift a 1 over by to get a mask, /// we just store the actual mask,so we can combine them with a bitwise-OR. /// /// For example, if we consider the following case (pretending that the /// masks are 8 bits instead of 64 just so i don't have to write out a bunch /// of extra zeroes): /// /// - `filter1` has the filter id 1 (`0b0000_0001`) /// - `filter2` has the filter id 2 (`0b0000_0010`) /// /// A span that gets disabled by filter 1 would have the [`FilterMap`] with /// bits `0b0000_0001`. /// /// If the `FilterId` was internally represented as `(bits to shift + 1), /// when `layer2`'s [`Context`] checked if it enabled the span, it would /// make the mask `0b0000_0010` (`1 << 1`). That bit would not be set in the /// [`FilterMap`], so it would see that it _didn't_ disable the span. Which /// is *true*, it just doesn't reflect the tree-like shape of the actual /// subscriber. /// /// By having the IDs be masks instead of shifts, though, when the /// [`Filtered`] with `filter2` gets the [`Context`] with `filter1`'s filter ID, /// instead of replacing it, it ors them together: /// /// ```ignore /// 0b0000_0001 | 0b0000_0010 == 0b0000_0011; /// ``` /// /// We then test if the span was disabled by seeing if _any_ bits in the /// mask are `1`: /// /// ```ignore /// filtermap & mask != 0; /// 0b0000_0001 & 0b0000_0011 != 0; /// 0b0000_0001 != 0; /// true; /// ``` /// /// [`Context`]: crate::layer::Context pub(crate) fn and(self, FilterId(other): Self) -> Self { // If this mask is disabled, just return the other --- otherwise, we // would always see that every span is disabled. if self.0 == Self::disabled().0 { return Self(other); } Self(self.0 | other) } fn is_disabled(self) -> bool { self.0 == Self::disabled().0 } } impl fmt::Debug for FilterId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // don't print a giant set of the numbers 0..63 if the filter ID is disabled. if self.0 == Self::disabled().0 { return f .debug_tuple("FilterId") .field(&format_args!("DISABLED")) .finish(); } if f.alternate() { f.debug_struct("FilterId") .field("ids", &format_args!("{:?}", FmtBitset(self.0))) .field("bits", &format_args!("{:b}", self.0)) .finish() } else { f.debug_tuple("FilterId").field(&FmtBitset(self.0)).finish() } } } impl fmt::Binary for FilterId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("FilterId") .field(&format_args!("{:b}", self.0)) .finish() } } // === impl FilterExt === impl FilterExt for F where F: layer::Filter {} // === impl FilterMap === impl FilterMap { pub(crate) fn set(self, FilterId(mask): FilterId, enabled: bool) -> Self { if mask == std::u64::MAX { return self; } if enabled { Self { bits: self.bits & (!mask), } } else { Self { bits: self.bits | mask, } } } #[inline] pub(crate) fn is_enabled(self, FilterId(mask): FilterId) -> bool { self.bits & mask == 0 } #[inline] pub(crate) fn any_enabled(self) -> bool { self.bits != std::u64::MAX } } impl fmt::Debug for FilterMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let alt = f.alternate(); let mut s = f.debug_struct("FilterMap"); s.field("disabled_by", &format_args!("{:?}", &FmtBitset(self.bits))); if alt { s.field("bits", &format_args!("{:b}", self.bits)); } s.finish() } } impl fmt::Binary for FilterMap { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FilterMap") .field("bits", &format_args!("{:b}", self.bits)) .finish() } } // === impl FilterState === impl FilterState { fn new() -> Self { Self { enabled: Cell::new(FilterMap::default()), interest: RefCell::new(None), #[cfg(debug_assertions)] counters: DebugCounters::default(), } } fn set(&self, filter: FilterId, enabled: bool) { #[cfg(debug_assertions)] { let in_current_pass = self.counters.in_filter_pass.get(); if in_current_pass == 0 { debug_assert_eq!(self.enabled.get(), FilterMap::default()); } self.counters.in_filter_pass.set(in_current_pass + 1); debug_assert_eq!( self.counters.in_interest_pass.get(), 0, "if we are in or starting a filter pass, we must not be in an interest pass." ) } self.enabled.set(self.enabled.get().set(filter, enabled)) } fn add_interest(&self, interest: Interest) { let mut curr_interest = self.interest.borrow_mut(); #[cfg(debug_assertions)] { let in_current_pass = self.counters.in_interest_pass.get(); if in_current_pass == 0 { debug_assert!(curr_interest.is_none()); } self.counters.in_interest_pass.set(in_current_pass + 1); } if let Some(curr_interest) = curr_interest.as_mut() { if (curr_interest.is_always() && !interest.is_always()) || (curr_interest.is_never() && !interest.is_never()) { *curr_interest = Interest::sometimes(); } // If the two interests are the same, do nothing. If the current // interest is `sometimes`, stay sometimes. } else { *curr_interest = Some(interest); } } pub(crate) fn event_enabled() -> bool { FILTERING .try_with(|this| { let enabled = this.enabled.get().any_enabled(); #[cfg(debug_assertions)] { if this.counters.in_filter_pass.get() == 0 { debug_assert_eq!(this.enabled.get(), FilterMap::default()); } // Nothing enabled this event, we won't tick back down the // counter in `did_enable`. Reset it. if !enabled { this.counters.in_filter_pass.set(0); } } enabled }) .unwrap_or(true) } /// Executes a closure if the filter with the provided ID did not disable /// the current span/event. /// /// This is used to implement the `on_event` and `new_span` methods for /// `Filtered`. fn did_enable(&self, filter: FilterId, f: impl FnOnce()) { let map = self.enabled.get(); if map.is_enabled(filter) { // If the filter didn't disable the current span/event, run the // callback. f(); } else { // Otherwise, if this filter _did_ disable the span or event // currently being processed, clear its bit from this thread's // `FilterState`. The bit has already been "consumed" by skipping // this callback, and we need to ensure that the `FilterMap` for // this thread is reset when the *next* `enabled` call occurs. self.enabled.set(map.set(filter, true)); } #[cfg(debug_assertions)] { let in_current_pass = self.counters.in_filter_pass.get(); if in_current_pass <= 1 { debug_assert_eq!(self.enabled.get(), FilterMap::default()); } self.counters .in_filter_pass .set(in_current_pass.saturating_sub(1)); debug_assert_eq!( self.counters.in_interest_pass.get(), 0, "if we are in a filter pass, we must not be in an interest pass." ) } } /// Run a second filtering pass, e.g. for Layer::event_enabled. fn and(&self, filter: FilterId, f: impl FnOnce() -> bool) -> bool { let map = self.enabled.get(); let enabled = map.is_enabled(filter) && f(); self.enabled.set(map.set(filter, enabled)); enabled } /// Clears the current in-progress filter state. /// /// This resets the [`FilterMap`] and current [`Interest`] as well as /// clearing the debug counters. pub(crate) fn clear_enabled() { // Drop the `Result` returned by `try_with` --- if we are in the middle // 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()); #[cfg(debug_assertions)] filtering.counters.in_filter_pass.set(0); }); } pub(crate) fn take_interest() -> Option { FILTERING .try_with(|filtering| { #[cfg(debug_assertions)] { if filtering.counters.in_interest_pass.get() == 0 { debug_assert!(filtering.interest.try_borrow().ok()?.is_none()); } filtering.counters.in_interest_pass.set(0); } filtering.interest.try_borrow_mut().ok()?.take() }) .ok()? } pub(crate) fn filter_map(&self) -> FilterMap { let map = self.enabled.get(); #[cfg(debug_assertions)] { if self.counters.in_filter_pass.get() == 0 { debug_assert_eq!(map, FilterMap::default()); } } map } } /// This is a horrible and bad abuse of the downcasting system to expose /// *internally* whether a layer has per-layer filtering, within /// `tracing-subscriber`, without exposing a public API for it. /// /// If a `Layer` has per-layer filtering, it will downcast to a /// `MagicPlfDowncastMarker`. Since layers which contain other layers permit /// downcasting to recurse to their children, this will do the Right Thing with /// layers like Reload, Option, etc. /// /// Why is this a wrapper around the `FilterId`, you may ask? Because /// downcasting works by returning a pointer, and we don't want to risk /// introducing UB by constructing pointers that _don't_ point to a valid /// instance of the type they claim to be. In this case, we don't _intend_ for /// this pointer to be dereferenced, so it would actually be fine to return one /// that isn't a valid pointer...but we can't guarantee that the caller won't /// (accidentally) dereference it, so it's better to be safe than sorry. We /// could, alternatively, add an additional field to the type that's used only /// for returning pointers to as as part of the evil downcasting hack, but I /// thought it was nicer to just add a `repr(transparent)` wrapper to the /// existing `FilterId` field, since it won't make the struct any bigger. /// /// Don't worry, this isn't on the test. :) #[derive(Clone, Copy)] #[repr(transparent)] struct MagicPlfDowncastMarker(FilterId); impl fmt::Debug for MagicPlfDowncastMarker { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Just pretend that `MagicPlfDowncastMarker` doesn't exist for // `fmt::Debug` purposes...if no one *sees* it in their `Debug` output, // they don't have to know I thought this code would be a good idea. fmt::Debug::fmt(&self.0, f) } } pub(crate) fn is_plf_downcast_marker(type_id: TypeId) -> bool { type_id == TypeId::of::() } /// Does a type implementing `Subscriber` contain any per-layer filters? pub(crate) fn subscriber_has_plf(subscriber: &S) -> bool where S: Subscriber, { (subscriber as &dyn Subscriber).is::() } /// Does a type implementing `Layer` contain any per-layer filters? pub(crate) fn layer_has_plf(layer: &L) -> bool where L: Layer, S: Subscriber, { unsafe { // Safety: we're not actually *doing* anything with this pointer --- we // only care about the `Option`, which we're turning into a `bool`. So // even if the layer decides to be evil and give us some kind of invalid // pointer, we don't ever dereference it, so this is always safe. layer.downcast_raw(TypeId::of::()) } .is_some() } struct FmtBitset(u64); impl fmt::Debug for FmtBitset { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut set = f.debug_set(); for bit in 0..64 { // if the `bit`-th bit is set, add it to the debug set if self.0 & (1 << bit) != 0 { set.entry(&bit); } } set.finish() } } tracing-subscriber-0.3.18/src/filter/level.rs000064400000000000000000000013571046102023000172440ustar 00000000000000use tracing_core::{ subscriber::{Interest, Subscriber}, Metadata, }; #[allow(unreachable_pub)] // https://github.com/rust-lang/rust/issues/57411 pub use tracing_core::metadata::{LevelFilter, ParseLevelFilterError as ParseError}; // === impl LevelFilter === impl crate::Layer for LevelFilter { fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { if self >= metadata.level() { Interest::always() } else { Interest::never() } } fn enabled(&self, metadata: &Metadata<'_>, _: crate::layer::Context<'_, S>) -> bool { self >= metadata.level() } fn max_level_hint(&self) -> Option { Some(*self) } } tracing-subscriber-0.3.18/src/filter/mod.rs000064400000000000000000000034251046102023000167120ustar 00000000000000//! [`Layer`]s that control which spans and events are enabled by the wrapped //! subscriber. //! //! This module contains a number of types that provide implementations of //! various strategies for filtering which spans and events are enabled. For //! details on filtering spans and events using [`Layer`]s, see the //! [`layer` module's documentation]. //! //! [`layer` module's documentation]: crate::layer#filtering-with-layers //! [`Layer`]: crate::layer mod filter_fn; feature! { #![all(feature = "env-filter", feature = "std")] mod env; pub use self::env::*; } feature! { #![all(feature = "registry", feature = "std")] mod layer_filters; pub use self::layer_filters::*; } mod level; pub use self::filter_fn::*; pub use self::level::{LevelFilter, ParseError as LevelParseError}; #[cfg(not(all(feature = "registry", feature = "std")))] pub(crate) use self::has_plf_stubs::*; feature! { #![any(feature = "std", feature = "alloc")] pub mod targets; pub use self::targets::Targets; mod directive; pub use self::directive::ParseError; } /// Stub implementations of the per-layer-fitler detection functions for when the /// `registry` feature is disabled. #[cfg(not(all(feature = "registry", feature = "std")))] mod has_plf_stubs { pub(crate) fn is_plf_downcast_marker(_: core::any::TypeId) -> bool { false } /// Does a type implementing `Subscriber` contain any per-layer filters? pub(crate) fn subscriber_has_plf(_: &S) -> bool where S: tracing_core::Subscriber, { false } /// Does a type implementing `Layer` contain any per-layer filters? pub(crate) fn layer_has_plf(_: &L) -> bool where L: crate::Layer, S: tracing_core::Subscriber, { false } } tracing-subscriber-0.3.18/src/filter/targets.rs000064400000000000000000000662311046102023000176100ustar 00000000000000//! A [filter] that enables or disables spans and events based on their [target] and [level]. //! //! See [`Targets`] for details. //! //! [target]: tracing_core::Metadata::target //! [level]: tracing_core::Level //! [filter]: crate::layer#filtering-with-layers use crate::{ filter::{ directive::{DirectiveSet, ParseError, StaticDirective}, LevelFilter, }, layer, }; #[cfg(not(feature = "std"))] use alloc::string::String; use core::{ fmt, iter::{Extend, FilterMap, FromIterator}, slice, str::FromStr, }; use tracing_core::{Interest, Level, Metadata, Subscriber}; /// A filter that enables or disables spans and events based on their [target] /// and [level]. /// /// Targets are typically equal to the Rust module path of the code where the /// span or event was recorded, although they may be overridden. /// /// This type can be used for both [per-layer filtering][plf] (using its /// [`Filter`] implementation) and [global filtering][global] (using its /// [`Layer`] implementation). /// /// See the [documentation on filtering with layers][filtering] for details. /// /// # Filtering With `Targets` /// /// A `Targets` filter consists of one or more [target] prefixes, paired with /// [`LevelFilter`]s. If a span or event's [target] begins with one of those /// prefixes, and its [level] is at or below the [`LevelFilter`] enabled for /// that prefix, then the span or event will be enabled. /// /// This is similar to the behavior implemented by the [`env_logger` crate] in /// the `log` ecosystem. /// /// The [`EnvFilter`] type also provided by this crate is very similar to `Targets`, /// but is capable of a more sophisticated form of filtering where events may /// also be enabled or disabled based on the span they are recorded in. /// `Targets` can be thought of as a lighter-weight form of [`EnvFilter`] that /// can be used instead when this dynamic filtering is not required. /// /// # Examples /// /// A `Targets` filter can be constructed by programmatically adding targets and /// levels to enable: /// /// ``` /// use tracing_subscriber::{filter, prelude::*}; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// // Enable the `INFO` level for anything in `my_crate` /// .with_target("my_crate", Level::INFO) /// // Enable the `DEBUG` level for a specific module. /// .with_target("my_crate::interesting_module", Level::DEBUG); /// /// // Build a new subscriber with the `fmt` layer using the `Targets` /// // filter we constructed above. /// tracing_subscriber::registry() /// .with(tracing_subscriber::fmt::layer()) /// .with(filter) /// .init(); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::annoying_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// Alternatively, `Targets` implements [`std::str::FromStr`], allowing it to be /// parsed from a comma-delimited list of `target=level` pairs. For example: /// /// ```rust /// # fn main() -> Result<(), Box> { /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = "my_crate=info,my_crate::interesting_module=trace,other_crate=debug" /// .parse::()?; /// /// // The parsed filter is identical to a filter constructed using `with_target`: /// assert_eq!( /// filter, /// filter::Targets::new() /// .with_target("my_crate", Level::INFO) /// .with_target("my_crate::interesting_module", Level::TRACE) /// .with_target("other_crate", Level::DEBUG) /// ); /// # Ok(()) } /// ``` /// /// This is particularly useful when the list of enabled targets is configurable /// by the user at runtime. /// /// The `Targets` filter can be used as a [per-layer filter][plf] *and* as a /// [global filter][global]: /// /// ```rust /// use tracing_subscriber::{ /// fmt, /// filter::{Targets, LevelFilter}, /// prelude::*, /// }; /// use tracing_core::Level; /// use std::{sync::Arc, fs::File}; /// # fn docs() -> Result<(), Box> { /// /// // A layer that logs events to stdout using the human-readable "pretty" /// // format. /// let stdout_log = fmt::layer().pretty(); /// /// // A layer that logs events to a file, using the JSON format. /// let file = File::create("debug_log.json")?; /// let debug_log = fmt::layer() /// .with_writer(Arc::new(file)) /// .json(); /// /// tracing_subscriber::registry() /// // Only log INFO and above to stdout, unless the span or event /// // has the `my_crate::cool_module` target prefix. /// .with(stdout_log /// .with_filter( /// Targets::default() /// .with_target("my_crate::cool_module", Level::DEBUG) /// .with_default(Level::INFO) /// ) /// ) /// // Log everything enabled by the global filter to `debug_log.json`. /// .with(debug_log) /// // Configure a global filter for the whole subscriber stack. This will /// // control what spans and events are recorded by both the `debug_log` /// // and the `stdout_log` layers, and `stdout_log` will *additionally* be /// // filtered by its per-layer filter. /// .with( /// Targets::default() /// .with_target("my_crate", Level::TRACE) /// .with_target("other_crate", Level::INFO) /// .with_target("other_crate::annoying_module", LevelFilter::OFF) /// .with_target("third_crate", Level::DEBUG) /// ).init(); /// # Ok(()) } ///``` /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level /// [`Filter`]: crate::layer::Filter /// [`Layer`]: crate::layer::Layer /// [plf]: crate::layer#per-layer-filtering /// [global]: crate::layer#global-filtering /// [filtering]: crate::layer#filtering-with-layers /// [`env_logger` crate]: https://docs.rs/env_logger/0.9.0/env_logger/index.html#enabling-logging /// [`EnvFilter`]: crate::filter::EnvFilter #[derive(Debug, Default, Clone, PartialEq)] pub struct Targets(DirectiveSet); impl Targets { /// Returns a new `Targets` filter. /// /// This filter will enable no targets. Call [`with_target`] or [`with_targets`] /// to add enabled targets, and [`with_default`] to change the default level /// enabled for spans and events that didn't match any of the provided targets. /// /// [`with_target`]: Targets::with_target /// [`with_targets`]: Targets::with_targets /// [`with_default`]: Targets::with_default pub fn new() -> Self { Self::default() } /// Enables spans and events with [target]s starting with the provided target /// prefix if they are at or below the provided [`LevelFilter`]. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// // Enable the `INFO` level for anything in `my_crate` /// .with_target("my_crate", Level::INFO) /// // Enable the `DEBUG` level for a specific module. /// .with_target("my_crate::interesting_module", Level::DEBUG); /// # drop(filter); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::interesting_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn with_target(mut self, target: impl Into, level: impl Into) -> Self { self.0.add(StaticDirective::new( Some(target.into()), Default::default(), level.into(), )); self } /// Adds [target]s from an iterator of [target]-[`LevelFilter`] pairs to this filter. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter; /// use tracing_core::Level; /// /// let filter = filter::Targets::new() /// .with_targets(vec![ /// ("my_crate", Level::INFO), /// ("my_crate::some_module", Level::DEBUG), /// ("my_crate::other_module::cool_stuff", Level::TRACE), /// ("other_crate", Level::WARN) /// ]); /// # drop(filter); /// ``` /// /// [`LevelFilter::OFF`] can be used to disable a particular target: /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// // Disable all traces from `annoying_module`. /// .with_target("my_crate::interesting_module", LevelFilter::OFF); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn with_targets(mut self, targets: impl IntoIterator) -> Self where String: From, LevelFilter: From, { self.extend(targets); self } /// Sets the default level to enable for spans and events whose targets did /// not match any of the configured prefixes. /// /// By default, this is [`LevelFilter::OFF`]. This means that spans and /// events will only be enabled if they match one of the configured target /// prefixes. If this is changed to a different [`LevelFilter`], spans and /// events with targets that did not match any of the configured prefixes /// will be enabled if their level is at or below the provided level. pub fn with_default(mut self, level: impl Into) -> Self { self.0 .add(StaticDirective::new(None, Default::default(), level.into())); self } /// Returns the default level for this filter, if one is set. /// /// The default level is used to filter any spans or events with targets /// that do not match any of the configured set of prefixes. /// /// The default level can be set for a filter either by using /// [`with_default`](Self::with_default) or when parsing from a filter string that includes a /// level without a target (e.g. `"trace"`). /// /// # Examples /// /// ``` /// use tracing_subscriber::filter::{LevelFilter, Targets}; /// /// let filter = Targets::new().with_default(LevelFilter::INFO); /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); /// /// let filter: Targets = "info".parse().unwrap(); /// assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); /// ``` /// /// The default level is `None` if no default is set: /// /// ``` /// use tracing_subscriber::filter::Targets; /// /// let filter = Targets::new(); /// assert_eq!(filter.default_level(), None); /// /// let filter: Targets = "my_crate=info".parse().unwrap(); /// assert_eq!(filter.default_level(), None); /// ``` /// /// Note that an unset default level (`None`) behaves like [`LevelFilter::OFF`] when the filter is /// used, but it could also be set explicitly which may be useful to distinguish (such as when /// merging multiple `Targets`). /// /// ``` /// use tracing_subscriber::filter::{LevelFilter, Targets}; /// /// let filter = Targets::new().with_default(LevelFilter::OFF); /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); /// /// let filter: Targets = "off".parse().unwrap(); /// assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); /// ``` pub fn default_level(&self) -> Option { self.0.directives().find_map(|d| { if d.target.is_none() { Some(d.level) } else { None } }) } /// Returns an iterator over the [target]-[`LevelFilter`] pairs in this filter. /// /// The order of iteration is undefined. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// .with_target("my_crate::interesting_module", Level::DEBUG); /// /// let mut targets: Vec<_> = filter.iter().collect(); /// targets.sort(); /// /// assert_eq!(targets, vec![ /// ("my_crate", LevelFilter::INFO), /// ("my_crate::interesting_module", LevelFilter::DEBUG), /// ]); /// ``` /// /// [target]: tracing_core::Metadata::target pub fn iter(&self) -> Iter<'_> { self.into_iter() } #[inline] fn interested(&self, metadata: &'static Metadata<'static>) -> Interest { if self.0.enabled(metadata) { Interest::always() } else { Interest::never() } } /// Returns whether a [target]-[`Level`] pair would be enabled /// by this `Targets`. /// /// This method can be used with [`module_path!`] from `std` as the target /// in order to emulate the behavior of the [`tracing::event!`] and [`tracing::span!`] /// macros. /// /// # Examples /// /// ``` /// use tracing_subscriber::filter::{Targets, LevelFilter}; /// use tracing_core::Level; /// /// let filter = Targets::new() /// .with_target("my_crate", Level::INFO) /// .with_target("my_crate::interesting_module", Level::DEBUG); /// /// assert!(filter.would_enable("my_crate", &Level::INFO)); /// assert!(!filter.would_enable("my_crate::interesting_module", &Level::TRACE)); /// ``` /// /// [target]: tracing_core::Metadata::target /// [`module_path!`]: std::module_path! pub fn would_enable(&self, target: &str, level: &Level) -> bool { // "Correct" to call because `Targets` only produces `StaticDirective`'s with NO // fields self.0.target_enabled(target, level) } } impl Extend<(T, L)> for Targets where T: Into, L: Into, { fn extend>(&mut self, iter: I) { let iter = iter.into_iter().map(|(target, level)| { StaticDirective::new(Some(target.into()), Default::default(), level.into()) }); self.0.extend(iter); } } impl FromIterator<(T, L)> for Targets where T: Into, L: Into, { fn from_iter>(iter: I) -> Self { let mut this = Self::default(); this.extend(iter); this } } impl FromStr for Targets { type Err = ParseError; fn from_str(s: &str) -> Result { s.split(',') .map(StaticDirective::from_str) .collect::>() .map(Self) } } impl layer::Layer for Targets where S: Subscriber, { fn enabled(&self, metadata: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { self.0.enabled(metadata) } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.interested(metadata) } fn max_level_hint(&self) -> Option { Some(self.0.max_level) } } #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] impl layer::Filter for Targets { fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { self.0.enabled(metadata) } fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { self.interested(metadata) } fn max_level_hint(&self) -> Option { Some(self.0.max_level) } } impl IntoIterator for Targets { type Item = (String, LevelFilter); type IntoIter = IntoIter; fn into_iter(self) -> Self::IntoIter { IntoIter::new(self) } } impl<'a> IntoIterator for &'a Targets { type Item = (&'a str, LevelFilter); type IntoIter = Iter<'a>; fn into_iter(self) -> Self::IntoIter { Iter::new(self) } } impl fmt::Display for Targets { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut directives = self.0.directives(); if let Some(directive) = directives.next() { write!(f, "{}", directive)?; for directive in directives { write!(f, ",{}", directive)?; } } Ok(()) } } /// An owning iterator over the [target]-[level] pairs of a `Targets` filter. /// /// This struct is created by the `IntoIterator` trait implementation of [`Targets`]. /// /// # Examples /// /// Merge the targets from one `Targets` with another: /// /// ``` /// use tracing_subscriber::filter::Targets; /// use tracing_core::Level; /// /// let mut filter = Targets::new().with_target("my_crate", Level::INFO); /// let overrides = Targets::new().with_target("my_crate::interesting_module", Level::DEBUG); /// /// filter.extend(overrides); /// # drop(filter); /// ``` /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level #[derive(Debug)] pub struct IntoIter( #[allow(clippy::type_complexity)] // alias indirection would probably make this more confusing FilterMap< as IntoIterator>::IntoIter, fn(StaticDirective) -> Option<(String, LevelFilter)>, >, ); impl IntoIter { fn new(targets: Targets) -> Self { Self(targets.0.into_iter().filter_map(|directive| { let level = directive.level; directive.target.map(|target| (target, level)) })) } } impl Iterator for IntoIter { type Item = (String, LevelFilter); fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } /// A borrowing iterator over the [target]-[level] pairs of a `Targets` filter. /// /// This struct is created by [`iter`] method of [`Targets`], or from the `IntoIterator` /// implementation for `&Targets`. /// /// [target]: tracing_core::Metadata::target /// [level]: tracing_core::Level /// [`iter`]: Targets::iter #[derive(Debug)] pub struct Iter<'a>( FilterMap< slice::Iter<'a, StaticDirective>, fn(&'a StaticDirective) -> Option<(&'a str, LevelFilter)>, >, ); impl<'a> Iter<'a> { fn new(targets: &'a Targets) -> Self { Self(targets.0.iter().filter_map(|directive| { directive .target .as_deref() .map(|target| (target, directive.level)) })) } } impl<'a> Iterator for Iter<'a> { type Item = (&'a str, LevelFilter); fn next(&mut self) -> Option { self.0.next() } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } #[cfg(test)] mod tests { use super::*; feature! { #![not(feature = "std")] use alloc::{vec, vec::Vec, string::ToString}; // `dbg!` is only available with `libstd`; just nop it out when testing // with alloc only. macro_rules! dbg { ($x:expr) => { $x } } } fn expect_parse(s: &str) -> Targets { match dbg!(s).parse::() { Err(e) => panic!("string {:?} did not parse successfully: {}", s, e), Ok(e) => e, } } fn expect_parse_ralith(s: &str) { let dirs = expect_parse(s).0.into_vec(); assert_eq!(dirs.len(), 2, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("server".to_string())); assert_eq!(dirs[0].level, LevelFilter::DEBUG); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("common".to_string())); assert_eq!(dirs[1].level, LevelFilter::INFO); assert_eq!(dirs[1].field_names, Vec::::new()); } fn expect_parse_level_directives(s: &str) { let dirs = expect_parse(s).0.into_vec(); assert_eq!(dirs.len(), 6, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate3::mod2::mod1".to_string())); assert_eq!(dirs[0].level, LevelFilter::OFF); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("crate1::mod2::mod3".to_string())); assert_eq!(dirs[1].level, LevelFilter::INFO); assert_eq!(dirs[1].field_names, Vec::::new()); assert_eq!(dirs[2].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[2].level, LevelFilter::WARN); assert_eq!(dirs[2].field_names, Vec::::new()); assert_eq!(dirs[3].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[3].level, LevelFilter::ERROR); assert_eq!(dirs[3].field_names, Vec::::new()); assert_eq!(dirs[4].target, Some("crate3".to_string())); assert_eq!(dirs[4].level, LevelFilter::TRACE); assert_eq!(dirs[4].field_names, Vec::::new()); assert_eq!(dirs[5].target, Some("crate2".to_string())); assert_eq!(dirs[5].level, LevelFilter::DEBUG); assert_eq!(dirs[5].field_names, Vec::::new()); } #[test] fn parse_ralith() { expect_parse_ralith("common=info,server=debug"); } #[test] fn parse_ralith_uc() { expect_parse_ralith("common=INFO,server=DEBUG"); } #[test] fn parse_ralith_mixed() { expect_parse("common=iNfo,server=dEbUg"); } #[test] fn expect_parse_valid() { let dirs = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .0 .into_vec(); assert_eq!(dirs.len(), 4, "\nparsed: {:#?}", dirs); assert_eq!(dirs[0].target, Some("crate1::mod2".to_string())); assert_eq!(dirs[0].level, LevelFilter::TRACE); assert_eq!(dirs[0].field_names, Vec::::new()); assert_eq!(dirs[1].target, Some("crate1::mod1".to_string())); assert_eq!(dirs[1].level, LevelFilter::ERROR); assert_eq!(dirs[1].field_names, Vec::::new()); assert_eq!(dirs[2].target, Some("crate3".to_string())); assert_eq!(dirs[2].level, LevelFilter::OFF); assert_eq!(dirs[2].field_names, Vec::::new()); assert_eq!(dirs[3].target, Some("crate2".to_string())); assert_eq!(dirs[3].level, LevelFilter::DEBUG); assert_eq!(dirs[3].field_names, Vec::::new()); } #[test] fn parse_level_directives() { expect_parse_level_directives( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ) } #[test] fn parse_uppercase_level_directives() { expect_parse_level_directives( "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", ) } #[test] fn parse_numeric_level_directives() { expect_parse_level_directives( "crate1::mod1=1,crate1::mod2=2,crate1::mod2::mod3=3,crate2=4,\ crate3=5,crate3::mod2::mod1=0", ) } #[test] fn targets_iter() { let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::WARN); let mut targets: Vec<_> = filter.iter().collect(); targets.sort(); assert_eq!( targets, vec![ ("crate1::mod1", LevelFilter::ERROR), ("crate1::mod2", LevelFilter::TRACE), ("crate2", LevelFilter::DEBUG), ("crate3", LevelFilter::OFF), ] ); } #[test] fn targets_into_iter() { let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::WARN); let mut targets: Vec<_> = filter.into_iter().collect(); targets.sort(); assert_eq!( targets, vec![ ("crate1::mod1".to_string(), LevelFilter::ERROR), ("crate1::mod2".to_string(), LevelFilter::TRACE), ("crate2".to_string(), LevelFilter::DEBUG), ("crate3".to_string(), LevelFilter::OFF), ] ); } #[test] fn targets_default_level() { let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); assert_eq!(filter.default_level(), None); let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::OFF); assert_eq!(filter.default_level(), Some(LevelFilter::OFF)); let filter = expect_parse("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off") .with_default(LevelFilter::OFF) .with_default(LevelFilter::INFO); assert_eq!(filter.default_level(), Some(LevelFilter::INFO)); } #[test] // `println!` is only available with `libstd`. #[cfg(feature = "std")] fn size_of_filters() { fn print_sz(s: &str) { let filter = s.parse::().expect("filter should parse"); println!( "size_of_val({:?})\n -> {}B", s, std::mem::size_of_val(&filter) ); } print_sz("info"); print_sz("foo=debug"); print_sz( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ); } /// Test that the `fmt::Display` implementation for `Targets` emits a string /// that can itself be parsed as a `Targets`, and that the parsed `Targets` /// is equivalent to the original one. #[test] fn display_roundtrips() { fn test_roundtrip(s: &str) { let filter = expect_parse(s); // we don't assert that the display output is equivalent to the // original parsed filter string, because the `Display` impl always // uses lowercase level names and doesn't use the // target-without-level shorthand syntax. while they may not be // textually equivalent, though, they should still *parse* to the // same filter. let formatted = filter.to_string(); let filter2 = match dbg!(&formatted).parse::() { Ok(filter) => filter, Err(e) => panic!( "failed to parse formatted filter string {:?}: {}", formatted, e ), }; assert_eq!(filter, filter2); } test_roundtrip("crate1::mod1=error,crate1::mod2,crate2=debug,crate3=off"); test_roundtrip( "crate1::mod1=ERROR,crate1::mod2=WARN,crate1::mod2::mod3=INFO,\ crate2=DEBUG,crate3=TRACE,crate3::mod2::mod1=OFF", ); test_roundtrip( "crate1::mod1=error,crate1::mod2=warn,crate1::mod2::mod3=info,\ crate2=debug,crate3=trace,crate3::mod2::mod1=off", ); test_roundtrip("crate1::mod1,crate1::mod2,info"); test_roundtrip("crate1"); test_roundtrip("info"); } } tracing-subscriber-0.3.18/src/fmt/fmt_layer.rs000064400000000000000000001555261046102023000174300ustar 00000000000000use crate::{ field::RecordFields, fmt::{format, FormatEvent, FormatFields, MakeWriter, TestWriter}, layer::{self, Context}, registry::{self, LookupSpan, SpanRef}, }; use format::{FmtSpan, TimingDisplay}; use std::{ any::TypeId, cell::RefCell, env, fmt, io, marker::PhantomData, ops::Deref, time::Instant, }; use tracing_core::{ field, span::{Attributes, Current, Id, Record}, Event, Metadata, Subscriber, }; /// A [`Layer`] that logs formatted representations of `tracing` events. /// /// ## Examples /// /// Constructing a layer with the default configuration: /// /// ```rust /// use tracing_subscriber::{fmt, Registry}; /// use tracing_subscriber::prelude::*; /// /// let subscriber = Registry::default() /// .with(fmt::Layer::default()); /// /// tracing::subscriber::set_global_default(subscriber).unwrap(); /// ``` /// /// Overriding the layer's behavior: /// /// ```rust /// use tracing_subscriber::{fmt, Registry}; /// use tracing_subscriber::prelude::*; /// /// let fmt_layer = fmt::layer() /// .with_target(false) // don't include event targets when logging /// .with_level(false); // don't include event levels when logging /// /// let subscriber = Registry::default().with(fmt_layer); /// # tracing::subscriber::set_global_default(subscriber).unwrap(); /// ``` /// /// Setting a custom event formatter: /// /// ```rust /// use tracing_subscriber::fmt::{self, format, time}; /// use tracing_subscriber::prelude::*; /// /// let fmt = format().with_timer(time::Uptime::default()); /// let fmt_layer = fmt::layer() /// .event_format(fmt) /// .with_target(false); /// # let subscriber = fmt_layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// # tracing::subscriber::set_global_default(subscriber).unwrap(); /// ``` /// /// [`Layer`]: super::layer::Layer #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] pub struct Layer< S, N = format::DefaultFields, E = format::Format, W = fn() -> io::Stdout, > { make_writer: W, fmt_fields: N, fmt_event: E, fmt_span: format::FmtSpanConfig, is_ansi: bool, log_internal_errors: bool, _inner: PhantomData, } impl Layer { /// Returns a new [`Layer`][self::Layer] with the default configuration. pub fn new() -> Self { Self::default() } } // This needs to be a seperate impl block because they place different bounds on the type parameters. impl Layer where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, W: for<'writer> MakeWriter<'writer> + 'static, { /// Sets the [event formatter][`FormatEvent`] that the layer being built will /// use to format events. /// /// The event formatter may be any type implementing the [`FormatEvent`] /// trait, which is implemented for all functions taking a [`FmtContext`], a /// [`Writer`], and an [`Event`]. /// /// # Examples /// /// Setting a type implementing [`FormatEvent`] as the formatter: /// ```rust /// use tracing_subscriber::fmt::{self, format}; /// /// let layer = fmt::layer() /// .event_format(format().compact()); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` /// [`FormatEvent`]: format::FormatEvent /// [`Event`]: tracing::Event /// [`Writer`]: format::Writer pub fn event_format(self, e: E2) -> Layer where E2: FormatEvent + 'static, { Layer { fmt_fields: self.fmt_fields, fmt_event: e, fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Updates the event formatter by applying a function to the existing event formatter. /// /// This sets the event formatter that the layer being built will use to record fields. /// /// # Examples /// /// Updating an event formatter: /// /// ```rust /// let layer = tracing_subscriber::fmt::layer() /// .map_event_format(|e| e.compact()); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` pub fn map_event_format(self, f: impl FnOnce(E) -> E2) -> Layer where E2: FormatEvent + 'static, { Layer { fmt_fields: self.fmt_fields, fmt_event: f(self.fmt_event), fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } } // This needs to be a seperate impl block because they place different bounds on the type parameters. impl Layer { /// Sets the [`MakeWriter`] that the layer being built will use to write events. /// /// # Examples /// /// Using `stderr` rather than `stdout`: /// /// ```rust /// use std::io; /// use tracing_subscriber::fmt; /// /// let layer = fmt::layer() /// .with_writer(io::stderr); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` pub fn with_writer(self, make_writer: W2) -> Layer where W2: for<'writer> MakeWriter<'writer> + 'static, { Layer { fmt_fields: self.fmt_fields, fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, make_writer, _inner: self._inner, } } /// Borrows the [writer] for this [`Layer`]. /// /// [writer]: MakeWriter pub fn writer(&self) -> &W { &self.make_writer } /// Mutably borrows the [writer] for this [`Layer`]. /// /// This method is primarily expected to be used with the /// [`reload::Handle::modify`](crate::reload::Handle::modify) method. /// /// # Examples /// /// ``` /// # use tracing::info; /// # use tracing_subscriber::{fmt,reload,Registry,prelude::*}; /// # fn non_blocking(writer: T) -> (fn() -> std::io::Stdout) { /// # std::io::stdout /// # } /// # fn main() { /// let layer = fmt::layer().with_writer(non_blocking(std::io::stderr())); /// let (layer, reload_handle) = reload::Layer::new(layer); /// # /// # // specifying the Registry type is required /// # let _: &reload::Handle, Registry> = &reload_handle; /// # /// info!("This will be logged to stderr"); /// reload_handle.modify(|layer| *layer.writer_mut() = non_blocking(std::io::stdout())); /// info!("This will be logged to stdout"); /// # } /// ``` /// /// [writer]: MakeWriter pub fn writer_mut(&mut self) -> &mut W { &mut self.make_writer } /// Sets whether this layer should use ANSI terminal formatting /// escape codes (such as colors). /// /// This method is primarily expected to be used with the /// [`reload::Handle::modify`](crate::reload::Handle::modify) method when changing /// the writer. #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn set_ansi(&mut self, ansi: bool) { self.is_ansi = ansi; } /// Configures the layer to support [`libtest`'s output capturing][capturing] when used in /// unit tests. /// /// See [`TestWriter`] for additional details. /// /// # Examples /// /// Using [`TestWriter`] to let `cargo test` capture test output: /// /// ```rust /// use std::io; /// use tracing_subscriber::fmt; /// /// let layer = fmt::layer() /// .with_test_writer(); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` /// [capturing]: /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output /// [`TestWriter`]: super::writer::TestWriter pub fn with_test_writer(self) -> Layer { Layer { fmt_fields: self.fmt_fields, fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, make_writer: TestWriter::default(), _inner: self._inner, } } /// 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 /// 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. /// /// [`with_ansi`]: Subscriber::with_ansi /// [`set_ansi`]: Subscriber::set_ansi 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); } Self { is_ansi: ansi, ..self } } /// Sets whether to write errors from [`FormatEvent`] to the writer. /// Defaults to true. /// /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to /// the writer. These errors are unlikely and will only occur if there is a /// bug in the `FormatEvent` implementation or its dependencies. /// /// If writing to the writer fails, the error message is printed to stderr /// as a fallback. /// /// [`FormatEvent`]: crate::fmt::FormatEvent pub fn log_internal_errors(self, log_internal_errors: bool) -> Self { Self { log_internal_errors, ..self } } /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`]. /// /// This sets the [`MakeWriter`] that the layer being built will use to write events. /// /// # Examples /// /// Redirect output to stderr if level is <= WARN: /// /// ```rust /// use tracing::Level; /// use tracing_subscriber::fmt::{self, writer::MakeWriterExt}; /// /// let stderr = std::io::stderr.with_max_level(Level::WARN); /// let layer = fmt::layer() /// .map_writer(move |w| stderr.or_else(w)); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` pub fn map_writer(self, f: impl FnOnce(W) -> W2) -> Layer where W2: for<'writer> MakeWriter<'writer> + 'static, { Layer { fmt_fields: self.fmt_fields, fmt_event: self.fmt_event, fmt_span: self.fmt_span, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, make_writer: f(self.make_writer), _inner: self._inner, } } } impl Layer, W> where N: for<'writer> FormatFields<'writer> + 'static, { /// Use the given [`timer`] for span and event timestamps. /// /// See the [`time` module] for the provided timer implementations. /// /// Note that using the `"time`"" feature flag enables the /// additional time formatters [`UtcTime`] and [`LocalTime`], which use the /// [`time` crate] to provide more sophisticated timestamp formatting /// options. /// /// [`timer`]: super::time::FormatTime /// [`time` module]: mod@super::time /// [`UtcTime`]: super::time::UtcTime /// [`LocalTime`]: super::time::LocalTime /// [`time` crate]: https://docs.rs/time/0.3 pub fn with_timer(self, timer: T2) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_timer(timer), fmt_fields: self.fmt_fields, fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Do not emit timestamps with spans and event. pub fn without_time(self) -> Layer, W> { Layer { fmt_event: self.fmt_event.without_time(), fmt_fields: self.fmt_fields, fmt_span: self.fmt_span.without_time(), make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Configures how synthesized events are emitted at points in the [span /// lifecycle][lifecycle]. /// /// The following options are available: /// /// - `FmtSpan::NONE`: No events will be synthesized when spans are /// created, entered, exited, or closed. Data from spans will still be /// included as the context for formatted events. This is the default. /// - `FmtSpan::NEW`: An event will be synthesized when spans are created. /// - `FmtSpan::ENTER`: An event will be synthesized when spans are entered. /// - `FmtSpan::EXIT`: An event will be synthesized when spans are exited. /// - `FmtSpan::CLOSE`: An event will be synthesized when a span closes. If /// [timestamps are enabled][time] for this formatter, the generated /// event will contain fields with the span's _busy time_ (the total /// time for which it was entered) and _idle time_ (the total time that /// the span existed but was not entered). /// - `FmtSpan::ACTIVE`: Events will be synthesized when spans are entered /// or exited. /// - `FmtSpan::FULL`: Events will be synthesized whenever a span is /// created, entered, exited, or closed. If timestamps are enabled, the /// close event will contain the span's busy and idle time, as /// described above. /// /// The options can be enabled in any combination. For instance, the following /// will synthesize events whenever spans are created and closed: /// /// ```rust /// use tracing_subscriber::fmt; /// use tracing_subscriber::fmt::format::FmtSpan; /// /// let subscriber = fmt() /// .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) /// .finish(); /// ``` /// /// Note that the generated events will only be part of the log output by /// this formatter; they will not be recorded by other `Subscriber`s or by /// `Layer`s added to this subscriber. /// /// [lifecycle]: https://docs.rs/tracing/latest/tracing/span/index.html#the-span-lifecycle /// [time]: Layer::without_time() pub fn with_span_events(self, kind: FmtSpan) -> Self { Layer { fmt_span: self.fmt_span.with_kind(kind), ..self } } /// Sets whether or not an event's target is displayed. pub fn with_target(self, display_target: bool) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_target(display_target), ..self } } /// Sets whether or not an event's [source code file path][file] is /// displayed. /// /// [file]: tracing_core::Metadata::file pub fn with_file(self, display_filename: bool) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_file(display_filename), ..self } } /// Sets whether or not an event's [source code line number][line] is /// displayed. /// /// [line]: tracing_core::Metadata::line pub fn with_line_number( self, display_line_number: bool, ) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_line_number(display_line_number), ..self } } /// Sets whether or not an event's level is displayed. pub fn with_level(self, display_level: bool) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_level(display_level), ..self } } /// Sets whether or not the [thread ID] of the current thread is displayed /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids(self, display_thread_ids: bool) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_thread_ids(display_thread_ids), ..self } } /// Sets whether or not the [name] of the current thread is displayed /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names( self, display_thread_names: bool, ) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_thread_names(display_thread_names), ..self } } /// Sets the layer being built to use a [less verbose formatter][super::format::Compact]. pub fn compact(self) -> Layer, W> where N: for<'writer> FormatFields<'writer> + 'static, { Layer { fmt_event: self.fmt_event.compact(), fmt_fields: self.fmt_fields, fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Sets the layer being built to use an [excessively pretty, human-readable formatter](crate::fmt::format::Pretty). #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn pretty(self) -> Layer, W> { Layer { fmt_event: self.fmt_event.pretty(), fmt_fields: format::Pretty::default(), fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Sets the layer being built to use a [JSON formatter][super::format::Json]. /// /// The full format includes fields from all entered spans. /// /// # Example Output /// /// ```ignore,json /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}} /// ``` /// /// # Options /// /// - [`Layer::flatten_event`] can be used to enable flattening event fields into the root /// object. /// /// [`Layer::flatten_event`]: Layer::flatten_event() #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json(self) -> Layer, W> { Layer { fmt_event: self.fmt_event.json(), fmt_fields: format::JsonFields::new(), fmt_span: self.fmt_span, make_writer: self.make_writer, // always disable ANSI escapes in JSON mode! is_ansi: false, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } } #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl Layer, W> { /// Sets the JSON layer being built to flatten event metadata. /// /// See [`format::Json`][super::format::Json] pub fn flatten_event( self, flatten_event: bool, ) -> Layer, W> { Layer { fmt_event: self.fmt_event.flatten_event(flatten_event), fmt_fields: format::JsonFields::new(), ..self } } /// Sets whether or not the formatter will include the current span in /// formatted events. /// /// See [`format::Json`][super::format::Json] pub fn with_current_span( self, display_current_span: bool, ) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_current_span(display_current_span), fmt_fields: format::JsonFields::new(), ..self } } /// Sets whether or not the formatter will include a list (from root to leaf) /// of all currently entered spans in formatted events. /// /// See [`format::Json`][super::format::Json] pub fn with_span_list( self, display_span_list: bool, ) -> Layer, W> { Layer { fmt_event: self.fmt_event.with_span_list(display_span_list), fmt_fields: format::JsonFields::new(), ..self } } } impl Layer { /// Sets the field formatter that the layer being built will use to record /// fields. pub fn fmt_fields(self, fmt_fields: N2) -> Layer where N2: for<'writer> FormatFields<'writer> + 'static, { Layer { fmt_event: self.fmt_event, fmt_fields, fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } /// Updates the field formatter by applying a function to the existing field formatter. /// /// This sets the field formatter that the layer being built will use to record fields. /// /// # Examples /// /// Updating a field formatter: /// /// ```rust /// use tracing_subscriber::field::MakeExt; /// let layer = tracing_subscriber::fmt::layer() /// .map_fmt_fields(|f| f.debug_alt()); /// # // this is necessary for type inference. /// # use tracing_subscriber::Layer as _; /// # let _ = layer.with_subscriber(tracing_subscriber::registry::Registry::default()); /// ``` pub fn map_fmt_fields(self, f: impl FnOnce(N) -> N2) -> Layer where N2: for<'writer> FormatFields<'writer> + 'static, { Layer { fmt_event: self.fmt_event, fmt_fields: f(self.fmt_fields), fmt_span: self.fmt_span, make_writer: self.make_writer, is_ansi: self.is_ansi, log_internal_errors: self.log_internal_errors, _inner: self._inner, } } } impl Default for Layer { 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()); Layer { fmt_fields: format::DefaultFields::default(), fmt_event: format::Format::default(), fmt_span: format::FmtSpanConfig::default(), make_writer: io::stdout, is_ansi: ansi, log_internal_errors: false, _inner: PhantomData, } } } impl Layer where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, W: for<'writer> MakeWriter<'writer> + 'static, { #[inline] fn make_ctx<'a>(&'a self, ctx: Context<'a, S>, event: &'a Event<'a>) -> FmtContext<'a, S, N> { FmtContext { ctx, fmt_fields: &self.fmt_fields, event, } } } /// A formatted representation of a span's fields stored in its [extensions]. /// /// Because `FormattedFields` is generic over the type of the formatter that /// produced it, multiple versions of a span's formatted fields can be stored in /// the [`Extensions`][extensions] type-map. This means that when multiple /// formatters are in use, each can store its own formatted representation /// without conflicting. /// /// [extensions]: crate::registry::Extensions #[derive(Default)] pub struct FormattedFields { _format_fields: PhantomData, was_ansi: bool, /// The formatted fields of a span. pub fields: String, } impl FormattedFields { /// Returns a new `FormattedFields`. pub fn new(fields: String) -> Self { Self { fields, was_ansi: false, _format_fields: PhantomData, } } /// Returns a new [`format::Writer`] for writing to this `FormattedFields`. /// /// The returned [`format::Writer`] can be used with the /// [`FormatFields::format_fields`] method. pub fn as_writer(&mut self) -> format::Writer<'_> { format::Writer::new(&mut self.fields).with_ansi(self.was_ansi) } } impl fmt::Debug for FormattedFields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FormattedFields") .field("fields", &self.fields) .field("formatter", &format_args!("{}", std::any::type_name::())) .field("was_ansi", &self.was_ansi) .finish() } } impl fmt::Display for FormattedFields { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.fields, f) } } impl Deref for FormattedFields { type Target = String; fn deref(&self) -> &Self::Target { &self.fields } } // === impl FmtLayer === macro_rules! with_event_from_span { ($id:ident, $span:ident, $($field:literal = $value:expr),*, |$event:ident| $code:block) => { let meta = $span.metadata(); let cs = meta.callsite(); let fs = field::FieldSet::new(&[$($field),*], cs); #[allow(unused)] let mut iter = fs.iter(); let v = [$( (&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); $code }; } impl layer::Layer for Layer where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, W: for<'writer> MakeWriter<'writer> + 'static, { fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { let span = ctx.span(id).expect("Span not found, this is a bug"); let mut extensions = span.extensions_mut(); if extensions.get_mut::>().is_none() { let mut fields = FormattedFields::::new(String::new()); if self .fmt_fields .format_fields(fields.as_writer().with_ansi(self.is_ansi), attrs) .is_ok() { fields.was_ansi = self.is_ansi; extensions.insert(fields); } else { eprintln!( "[tracing-subscriber] Unable to format the following event, ignoring: {:?}", attrs ); } } if self.fmt_span.fmt_timing && self.fmt_span.trace_close() && extensions.get_mut::().is_none() { extensions.insert(Timings::new()); } if self.fmt_span.trace_new() { with_event_from_span!(id, span, "message" = "new", |event| { drop(extensions); drop(span); self.on_event(&event, ctx); }); } } fn on_record(&self, id: &Id, values: &Record<'_>, ctx: Context<'_, S>) { let span = ctx.span(id).expect("Span not found, this is a bug"); let mut extensions = span.extensions_mut(); if let Some(fields) = extensions.get_mut::>() { let _ = self.fmt_fields.add_fields(fields, values); return; } let mut fields = FormattedFields::::new(String::new()); if self .fmt_fields .format_fields(fields.as_writer().with_ansi(self.is_ansi), values) .is_ok() { fields.was_ansi = self.is_ansi; extensions.insert(fields); } } fn on_enter(&self, id: &Id, ctx: Context<'_, S>) { if self.fmt_span.trace_enter() || self.fmt_span.trace_close() && self.fmt_span.fmt_timing { 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 u64; timings.last = now; } if self.fmt_span.trace_enter() { with_event_from_span!(id, span, "message" = "enter", |event| { drop(extensions); drop(span); self.on_event(&event, ctx); }); } } } fn on_exit(&self, id: &Id, ctx: Context<'_, S>) { if self.fmt_span.trace_exit() || self.fmt_span.trace_close() && self.fmt_span.fmt_timing { 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 u64; timings.last = now; } if self.fmt_span.trace_exit() { with_event_from_span!(id, span, "message" = "exit", |event| { drop(extensions); drop(span); self.on_event(&event, ctx); }); } } } fn on_close(&self, id: Id, ctx: Context<'_, S>) { if self.fmt_span.trace_close() { let span = ctx.span(&id).expect("Span not found, this is a bug"); let extensions = span.extensions(); if let Some(timing) = extensions.get::() { let Timings { busy, mut idle, last, } = *timing; idle += (Instant::now() - last).as_nanos() as u64; let t_idle = field::display(TimingDisplay(idle)); let t_busy = field::display(TimingDisplay(busy)); with_event_from_span!( id, span, "message" = "close", "time.busy" = t_busy, "time.idle" = t_idle, |event| { drop(extensions); drop(span); self.on_event(&event, ctx); } ); } else { with_event_from_span!(id, span, "message" = "close", |event| { drop(extensions); drop(span); self.on_event(&event, ctx); }); } } } fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { thread_local! { static BUF: RefCell = RefCell::new(String::new()); } BUF.with(|buf| { let borrow = buf.try_borrow_mut(); let mut a; let mut b; let mut buf = match borrow { Ok(buf) => { a = buf; &mut *a } _ => { b = String::new(); &mut b } }; let ctx = self.make_ctx(ctx, event); if self .fmt_event .format_event( &ctx, format::Writer::new(&mut buf).with_ansi(self.is_ansi), event, ) .is_ok() { let mut writer = self.make_writer.make_writer_for(event.metadata()); let res = io::Write::write_all(&mut writer, buf.as_bytes()); if self.log_internal_errors { if let Err(e) = res { eprintln!("[tracing-subscriber] Unable to write an event to the Writer for this Subscriber! Error: {}\n", e); } } } else if self.log_internal_errors { let err_msg = format!("Unable to format the following event. Name: {}; Fields: {:?}\n", event.metadata().name(), event.fields()); let mut writer = self.make_writer.make_writer_for(event.metadata()); let res = io::Write::write_all(&mut writer, err_msg.as_bytes()); if let Err(e) = res { eprintln!("[tracing-subscriber] Unable to write an \"event formatting error\" to the Writer for this Subscriber! Error: {}\n", e); } } buf.clear(); }); } unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { // This `downcast_raw` impl allows downcasting a `fmt` layer to any of // its components (event formatter, field formatter, and `MakeWriter`) // as well as to the layer's type itself. The potential use-cases for // this *may* be somewhat niche, though... match () { _ if id == TypeId::of::() => Some(self as *const Self as *const ()), _ if id == TypeId::of::() => Some(&self.fmt_event as *const E as *const ()), _ if id == TypeId::of::() => Some(&self.fmt_fields as *const N as *const ()), _ if id == TypeId::of::() => Some(&self.make_writer as *const W as *const ()), _ => None, } } } /// Provides the current span context to a formatter. pub struct FmtContext<'a, S, N> { pub(crate) ctx: Context<'a, S>, pub(crate) fmt_fields: &'a N, pub(crate) event: &'a Event<'a>, } impl<'a, S, N> fmt::Debug for FmtContext<'a, S, N> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FmtContext").finish() } } impl<'cx, 'writer, S, N> FormatFields<'writer> for FmtContext<'cx, S, N> where S: Subscriber + for<'lookup> LookupSpan<'lookup>, N: FormatFields<'writer> + 'static, { fn format_fields( &self, writer: format::Writer<'writer>, fields: R, ) -> fmt::Result { self.fmt_fields.format_fields(writer, fields) } } impl<'a, S, N> FmtContext<'a, S, N> where S: Subscriber + for<'lookup> LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, { /// Visits every span in the current context with a closure. /// /// The provided closure will be called first with the current span, /// and then with that span's parent, and then that span's parent, /// and so on until a root span is reached. pub fn visit_spans(&self, mut f: F) -> Result<(), E> where F: FnMut(&SpanRef<'_, S>) -> Result<(), E>, { // visit all the current spans if let Some(scope) = self.event_scope() { for span in scope.from_root() { f(&span)?; } } Ok(()) } /// Returns metadata for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). #[inline] pub fn metadata(&self, id: &Id) -> Option<&'static Metadata<'static>> where S: for<'lookup> LookupSpan<'lookup>, { self.ctx.metadata(id) } /// Returns [stored data] for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). /// /// [stored data]: crate::registry::SpanRef #[inline] pub fn span(&self, id: &Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { self.ctx.span(id) } /// Returns `true` if an active span exists for the given `Id`. #[inline] pub fn exists(&self, id: &Id) -> bool where S: for<'lookup> LookupSpan<'lookup>, { self.ctx.exists(id) } /// Returns [stored data] for the span that the wrapped subscriber considers /// to be the current. /// /// If this returns `None`, then we are not currently within a span. /// /// [stored data]: crate::registry::SpanRef #[inline] pub fn lookup_current(&self) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { self.ctx.lookup_current() } /// Returns the current span for this formatter. pub fn current_span(&self) -> Current { self.ctx.current_span() } /// Returns [stored data] for the parent span of the event currently being /// formatted. /// /// If the event has a contextual parent, this will return the current span. If /// the event has an explicit parent span, this will return that span. If /// the event does not have a parent span, this will return `None`. /// /// [stored data]: SpanRef pub fn parent_span(&self) -> Option> { self.ctx.event_span(self.event) } /// 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. /// /// This is equivalent to the [`Context::span_scope`] method. /// ///
///
Note
///
///
///
    /// Note: Compared to scope this
    /// returns the spans in reverse order (from leaf to root). Use
    /// Scope::from_root
    /// in case root-to-leaf ordering is desired.
    /// 
/// ///
///
    /// Note: This requires the wrapped subscriber to implement the
    /// LookupSpan trait.
    /// See the documentation on Context's
    /// declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef pub fn span_scope(&self, id: &Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { self.ctx.span_scope(id) } /// Returns an iterator over the [stored data] for all the spans in the /// event's span context, starting with its parent span and ending with the /// root of the trace tree. /// /// This is equivalent to calling the [`Context::event_scope`] method and /// passing the event currently being formatted. /// ///
///
    /// Note: Compared to scope this
    /// returns the spans in reverse order (from leaf to root). Use
    /// Scope::from_root
    /// in case root-to-leaf ordering is desired.
    /// 
/// ///
///
    /// Note: This requires the wrapped subscriber to implement the
    /// LookupSpan trait.
    /// See the documentation on Context's
    /// declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef pub fn event_scope(&self) -> Option> where S: for<'lookup> registry::LookupSpan<'lookup>, { self.ctx.event_scope(self.event) } /// Returns the [field formatter] configured by the subscriber invoking /// `format_event`. /// /// The event formatter may use the returned field formatter to format the /// fields of any events it records. /// /// [field formatter]: FormatFields pub fn field_format(&self) -> &N { self.fmt_fields } } struct Timings { idle: u64, busy: u64, last: Instant, } impl Timings { fn new() -> Self { Self { idle: 0, busy: 0, last: Instant::now(), } } } #[cfg(test)] mod test { use super::*; use crate::fmt::{ self, format::{self, test::MockTime, Format}, layer::Layer as _, test::{MockMakeWriter, MockWriter}, time, }; use crate::Registry; use format::FmtSpan; use regex::Regex; use tracing::subscriber::with_default; use tracing_core::dispatcher::Dispatch; #[test] fn impls() { let f = Format::default().with_timer(time::Uptime::default()); let fmt = fmt::Layer::default().event_format(f); let subscriber = fmt.with_subscriber(Registry::default()); let _dispatch = Dispatch::new(subscriber); let f = format::Format::default(); let fmt = fmt::Layer::default().event_format(f); let subscriber = fmt.with_subscriber(Registry::default()); let _dispatch = Dispatch::new(subscriber); let f = format::Format::default().compact(); let fmt = fmt::Layer::default().event_format(f); let subscriber = fmt.with_subscriber(Registry::default()); let _dispatch = Dispatch::new(subscriber); } #[test] fn fmt_layer_downcasts() { let f = format::Format::default(); let fmt = fmt::Layer::default().event_format(f); let subscriber = fmt.with_subscriber(Registry::default()); let dispatch = Dispatch::new(subscriber); assert!(dispatch.downcast_ref::>().is_some()); } #[test] fn fmt_layer_downcasts_to_parts() { let f = format::Format::default(); let fmt = fmt::Layer::default().event_format(f); let subscriber = fmt.with_subscriber(Registry::default()); let dispatch = Dispatch::new(subscriber); assert!(dispatch.downcast_ref::().is_some()); assert!(dispatch.downcast_ref::().is_some()) } #[test] fn is_lookup_span() { fn assert_lookup_span crate::registry::LookupSpan<'a>>(_: T) {} let fmt = fmt::Layer::default(); let subscriber = fmt.with_subscriber(Registry::default()); assert_lookup_span(subscriber) } fn sanitize_timings(s: String) -> String { let re = Regex::new("time\\.(idle|busy)=([0-9.]+)[mµn]s").unwrap(); re.replace_all(s.as_str(), "timing").to_string() } #[test] fn format_error_print_to_stderr() { struct AlwaysError; impl std::fmt::Debug for AlwaysError { fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { Err(std::fmt::Error) } } let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); with_default(subscriber, || { tracing::info!(?AlwaysError); }); let actual = sanitize_timings(make_writer.get_string()); // Only assert the start because the line number and callsite may change. let expected = concat!( "Unable to format the following event. Name: event ", file!(), ":" ); assert!( actual.as_str().starts_with(expected), "\nactual = {}\nshould start with expected = {}\n", actual, expected ); } #[test] fn format_error_ignore_if_log_internal_errors_is_false() { struct AlwaysError; impl std::fmt::Debug for AlwaysError { fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { Err(std::fmt::Error) } } let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .log_internal_errors(false) .finish(); with_default(subscriber, || { tracing::info!(?AlwaysError); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!("", actual.as_str()); } #[test] fn synthesize_span_none() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) // check that FmtSpan::NONE is the default .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!("", actual.as_str()); } #[test] fn synthesize_span_active() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .with_span_events(FmtSpan::ACTIVE) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: enter\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: exit\n", actual.as_str() ); } #[test] fn synthesize_span_close() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .with_span_events(FmtSpan::CLOSE) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: close timing timing\n", actual.as_str() ); } #[test] fn synthesize_span_close_no_timing() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .without_time() .with_span_events(FmtSpan::CLOSE) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: close\n", actual.as_str() ); } #[test] fn synthesize_span_full() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .with_span_events(FmtSpan::FULL) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", x = 42); let _e = span1.enter(); }); let actual = sanitize_timings(make_writer.get_string()); assert_eq!( "fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: new\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: enter\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: exit\n\ fake time span1{x=42}: tracing_subscriber::fmt::fmt_layer::test: close timing timing\n", actual.as_str() ); } #[test] fn make_writer_based_on_meta() { struct MakeByTarget { make_writer1: MockMakeWriter, make_writer2: MockMakeWriter, } impl<'a> MakeWriter<'a> for MakeByTarget { type Writer = MockWriter; fn make_writer(&'a self) -> Self::Writer { self.make_writer1.make_writer() } fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.target() == "writer2" { return self.make_writer2.make_writer(); } self.make_writer() } } let make_writer1 = MockMakeWriter::default(); let make_writer2 = MockMakeWriter::default(); let make_writer = MakeByTarget { make_writer1: make_writer1.clone(), make_writer2: make_writer2.clone(), }; let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer) .with_level(false) .with_target(false) .with_ansi(false) .with_timer(MockTime) .with_span_events(FmtSpan::CLOSE) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("writer1_span", x = 42); let _e = span1.enter(); tracing::info!(target: "writer2", "hello writer2!"); let span2 = tracing::info_span!(target: "writer2", "writer2_span"); let _e = span2.enter(); tracing::warn!(target: "writer1", "hello writer1!"); }); let actual = sanitize_timings(make_writer1.get_string()); assert_eq!( "fake time writer1_span{x=42}:writer2_span: hello writer1!\n\ fake time writer1_span{x=42}: close timing timing\n", actual.as_str() ); let actual = sanitize_timings(make_writer2.get_string()); assert_eq!( "fake time writer1_span{x=42}: hello writer2!\n\ fake time writer1_span{x=42}:writer2_span: close timing timing\n", 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 layer_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 layer: Layer<()> = fmt::Layer::default(); assert_eq!( layer.is_ansi, ansi, "NO_COLOR={:?}; Layer::default().is_ansi should be {}", var, ansi ); // with_ansi should override any `NO_COLOR` value let layer: Layer<()> = fmt::Layer::default().with_ansi(true); assert!( layer.is_ansi, "NO_COLOR={:?}; Layer::default().with_ansi(true).is_ansi should be true", var ); // set_ansi should override any `NO_COLOR` value let mut layer: Layer<()> = fmt::Layer::default(); layer.set_ansi(true); assert!( layer.is_ansi, "NO_COLOR={:?}; layer.set_ansi(true); layer.is_ansi should be true", var ); } // dropping `_saved_no_color` will restore the previous value of // `NO_COLOR`. } } tracing-subscriber-0.3.18/src/fmt/format/json.rs000064400000000000000000001110221046102023000176660ustar 00000000000000use super::{Format, FormatEvent, FormatFields, FormatTime, Writer}; use crate::{ field::{RecordFields, VisitOutput}, fmt::{ fmt_layer::{FmtContext, FormattedFields}, writer::WriteAdaptor, }, registry::LookupSpan, }; use serde::ser::{SerializeMap, Serializer as _}; use serde_json::Serializer; use std::{ collections::BTreeMap, fmt::{self, Write}, }; use tracing_core::{ field::{self, Field}, span::Record, Event, Subscriber, }; use tracing_serde::AsSerde; #[cfg(feature = "tracing-log")] use tracing_log::NormalizeEvent; /// Marker for [`Format`] that indicates that the newline-delimited JSON log /// format should be used. /// /// This formatter is intended for production use with systems where structured /// logs are consumed as JSON by analysis and viewing tools. The JSON output is /// not optimized for human readability; instead, it should be pretty-printed /// using external JSON tools such as `jq`, or using a JSON log viewer. /// /// # Example Output /// ///
:; cargo run --example fmt-json
///     Finished dev [unoptimized + debuginfo] target(s) in 0.08s
///      Running `target/debug/examples/fmt-json`
/// {"timestamp":"2022-02-15T18:47:10.821315Z","level":"INFO","fields":{"message":"preparing to shave yaks","number_of_yaks":3},"target":"fmt_json"}
/// {"timestamp":"2022-02-15T18:47:10.821422Z","level":"INFO","fields":{"message":"shaving yaks"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.821495Z","level":"TRACE","fields":{"message":"hello! I'm gonna shave a yak","excitement":"yay!"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":1,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.821546Z","level":"TRACE","fields":{"message":"yak shaved successfully"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":1,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.821598Z","level":"DEBUG","fields":{"yak":1,"shaved":true},"target":"yak_events","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.821637Z","level":"TRACE","fields":{"yaks_shaved":1},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.821684Z","level":"TRACE","fields":{"message":"hello! I'm gonna shave a yak","excitement":"yay!"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":2,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.821727Z","level":"TRACE","fields":{"message":"yak shaved successfully"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":2,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.821773Z","level":"DEBUG","fields":{"yak":2,"shaved":true},"target":"yak_events","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.821806Z","level":"TRACE","fields":{"yaks_shaved":2},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.821909Z","level":"TRACE","fields":{"message":"hello! I'm gonna shave a yak","excitement":"yay!"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":3,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.821956Z","level":"WARN","fields":{"message":"could not locate yak"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"},{"yak":3,"name":"shave"}]}
/// {"timestamp":"2022-02-15T18:47:10.822006Z","level":"DEBUG","fields":{"yak":3,"shaved":false},"target":"yak_events","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.822041Z","level":"ERROR","fields":{"message":"failed to shave yak","yak":3,"error":"missing yak"},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.822079Z","level":"TRACE","fields":{"yaks_shaved":2},"target":"fmt_json::yak_shave","spans":[{"yaks":3,"name":"shaving_yaks"}]}
/// {"timestamp":"2022-02-15T18:47:10.822117Z","level":"INFO","fields":{"message":"yak shaving completed","all_yaks_shaved":false},"target":"fmt_json"}
/// 
/// /// # Options /// /// This formatter exposes additional options to configure the structure of the /// output JSON objects: /// /// - [`Json::flatten_event`] can be used to enable flattening event fields into /// the root /// - [`Json::with_current_span`] can be used to control logging of the current /// span /// - [`Json::with_span_list`] can be used to control logging of the span list /// object. /// /// By default, event fields are not flattened, and both current span and span /// list are logged. /// /// # Valuable Support /// /// Experimental support is available for using the [`valuable`] crate to record /// user-defined values as structured JSON. When the ["valuable" unstable /// feature][unstable] is enabled, types implementing [`valuable::Valuable`] will /// be recorded as structured JSON, rather than /// using their [`std::fmt::Debug`] implementations. /// /// **Note**: This is an experimental feature. [Unstable features][unstable] /// must be enabled in order to use `valuable` support. /// /// [`Json::flatten_event`]: Json::flatten_event() /// [`Json::with_current_span`]: Json::with_current_span() /// [`Json::with_span_list`]: Json::with_span_list() /// [`valuable`]: https://crates.io/crates/valuable /// [unstable]: crate#unstable-features /// [`valuable::Valuable`]: https://docs.rs/valuable/latest/valuable/trait.Valuable.html #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct Json { pub(crate) flatten_event: bool, pub(crate) display_current_span: bool, pub(crate) display_span_list: bool, } impl Json { /// If set to `true` event metadata will be flattened into the root object. pub fn flatten_event(&mut self, flatten_event: bool) { self.flatten_event = flatten_event; } /// If set to `false`, formatted events won't contain a field for the current span. pub fn with_current_span(&mut self, display_current_span: bool) { self.display_current_span = display_current_span; } /// If set to `false`, formatted events won't contain a list of all currently /// entered spans. Spans are logged in a list from root to leaf. pub fn with_span_list(&mut self, display_span_list: bool) { self.display_span_list = display_span_list; } } struct SerializableContext<'a, 'b, Span, N>( &'b crate::layer::Context<'a, Span>, std::marker::PhantomData, ) where Span: Subscriber + for<'lookup> crate::registry::LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static; impl<'a, 'b, Span, N> serde::ser::Serialize for SerializableContext<'a, 'b, Span, N> where Span: Subscriber + for<'lookup> crate::registry::LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, { fn serialize(&self, serializer_o: Ser) -> Result where Ser: serde::ser::Serializer, { use serde::ser::SerializeSeq; let mut serializer = serializer_o.serialize_seq(None)?; if let Some(leaf_span) = self.0.lookup_current() { for span in leaf_span.scope().from_root() { serializer.serialize_element(&SerializableSpan(&span, self.1))?; } } serializer.end() } } struct SerializableSpan<'a, 'b, Span, N>( &'b crate::registry::SpanRef<'a, Span>, std::marker::PhantomData, ) where Span: for<'lookup> crate::registry::LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static; impl<'a, 'b, Span, N> serde::ser::Serialize for SerializableSpan<'a, 'b, Span, N> where Span: for<'lookup> crate::registry::LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, { fn serialize(&self, serializer: Ser) -> Result where Ser: serde::ser::Serializer, { let mut serializer = serializer.serialize_map(None)?; let ext = self.0.extensions(); let data = ext .get::>() .expect("Unable to find FormattedFields in extensions; this is a bug"); // TODO: let's _not_ do this, but this resolves // https://github.com/tokio-rs/tracing/issues/391. // We should probably rework this to use a `serde_json::Value` or something // similar in a JSON-specific layer, but I'd (david) // rather have a uglier fix now rather than shipping broken JSON. match serde_json::from_str::(data) { Ok(serde_json::Value::Object(fields)) => { for field in fields { serializer.serialize_entry(&field.0, &field.1)?; } } // We have fields for this span which are valid JSON but not an object. // This is probably a bug, so panic if we're in debug mode Ok(_) if cfg!(debug_assertions) => panic!( "span '{}' had malformed fields! this is a bug.\n error: invalid JSON object\n fields: {:?}", self.0.metadata().name(), data ), // If we *aren't* in debug mode, it's probably best not to // crash the program, let's log the field found but also an // message saying it's type is invalid Ok(value) => { serializer.serialize_entry("field", &value)?; serializer.serialize_entry("field_error", "field was no a valid object")? } // We have previously recorded fields for this span // should be valid JSON. However, they appear to *not* // be valid JSON. This is almost certainly a bug, so // panic if we're in debug mode Err(e) if cfg!(debug_assertions) => panic!( "span '{}' had malformed fields! this is a bug.\n error: {}\n fields: {:?}", self.0.metadata().name(), e, data ), // If we *aren't* in debug mode, it's probably best not // crash the program, but let's at least make sure it's clear // that the fields are not supposed to be missing. Err(e) => serializer.serialize_entry("field_error", &format!("{}", e))?, }; serializer.serialize_entry("name", self.0.metadata().name())?; serializer.end() } } impl FormatEvent for Format where S: Subscriber + for<'lookup> LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, T: FormatTime, { fn format_event( &self, ctx: &FmtContext<'_, S, N>, mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result where S: Subscriber + for<'a> LookupSpan<'a>, { let mut timestamp = String::new(); self.timer.format_time(&mut Writer::new(&mut timestamp))?; #[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 mut visit = || { let mut serializer = Serializer::new(WriteAdaptor::new(&mut writer)); let mut serializer = serializer.serialize_map(None)?; if self.display_timestamp { serializer.serialize_entry("timestamp", ×tamp)?; } if self.display_level { serializer.serialize_entry("level", &meta.level().as_serde())?; } let format_field_marker: std::marker::PhantomData = std::marker::PhantomData; let current_span = if self.format.display_current_span || self.format.display_span_list { event .parent() .and_then(|id| ctx.span(id)) .or_else(|| ctx.lookup_current()) } else { None }; if self.format.flatten_event { let mut visitor = tracing_serde::SerdeMapVisitor::new(serializer); event.record(&mut visitor); serializer = visitor.take_serializer()?; } else { use tracing_serde::fields::AsMap; serializer.serialize_entry("fields", &event.field_map())?; }; if self.display_target { serializer.serialize_entry("target", meta.target())?; } if self.display_filename { if let Some(filename) = meta.file() { serializer.serialize_entry("filename", filename)?; } } if self.display_line_number { if let Some(line_number) = meta.line() { serializer.serialize_entry("line_number", &line_number)?; } } if self.format.display_current_span { if let Some(ref span) = current_span { serializer .serialize_entry("span", &SerializableSpan(span, format_field_marker)) .unwrap_or(()); } } if self.format.display_span_list && current_span.is_some() { serializer.serialize_entry( "spans", &SerializableContext(&ctx.ctx, format_field_marker), )?; } if self.display_thread_name { let current_thread = std::thread::current(); match current_thread.name() { Some(name) => { serializer.serialize_entry("threadName", name)?; } // fall-back to thread id when name is absent and ids are not enabled None if !self.display_thread_id => { serializer .serialize_entry("threadName", &format!("{:?}", current_thread.id()))?; } _ => {} } } if self.display_thread_id { serializer .serialize_entry("threadId", &format!("{:?}", std::thread::current().id()))?; } serializer.end() }; visit().map_err(|_| fmt::Error)?; writeln!(writer) } } impl Default for Json { fn default() -> Json { Json { flatten_event: false, display_current_span: true, display_span_list: true, } } } /// The JSON [`FormatFields`] implementation. /// #[derive(Debug)] pub struct JsonFields { // reserve the ability to add fields to this without causing a breaking // change in the future. _private: (), } impl JsonFields { /// Returns a new JSON [`FormatFields`] implementation. /// pub fn new() -> Self { Self { _private: () } } } impl Default for JsonFields { fn default() -> Self { Self::new() } } impl<'a> FormatFields<'a> for JsonFields { /// Format the provided `fields` to the provided `writer`, returning a result. fn format_fields(&self, mut writer: Writer<'_>, fields: R) -> fmt::Result { let mut v = JsonVisitor::new(&mut writer); fields.record(&mut v); v.finish() } /// Record additional field(s) on an existing span. /// /// By default, this appends a space to the current set of fields if it is /// non-empty, and then calls `self.format_fields`. If different behavior is /// required, the default implementation of this method can be overridden. fn add_fields( &self, current: &'a mut FormattedFields, fields: &Record<'_>, ) -> fmt::Result { if current.is_empty() { // If there are no previously recorded fields, we can just reuse the // existing string. let mut writer = current.as_writer(); let mut v = JsonVisitor::new(&mut writer); fields.record(&mut v); v.finish()?; return Ok(()); } // If fields were previously recorded on this span, we need to parse // the current set of fields as JSON, add the new fields, and // re-serialize them. Otherwise, if we just appended the new fields // to a previously serialized JSON object, we would end up with // malformed JSON. // // XXX(eliza): this is far from efficient, but unfortunately, it is // necessary as long as the JSON formatter is implemented on top of // an interface that stores all formatted fields as strings. // // We should consider reimplementing the JSON formatter as a // separate layer, rather than a formatter for the `fmt` layer — // then, we could store fields as JSON values, and add to them // without having to parse and re-serialize. let mut new = String::new(); let map: BTreeMap<&'_ str, serde_json::Value> = serde_json::from_str(current).map_err(|_| fmt::Error)?; let mut v = JsonVisitor::new(&mut new); v.values = map; fields.record(&mut v); v.finish()?; current.fields = new; Ok(()) } } /// The [visitor] produced by [`JsonFields`]'s [`MakeVisitor`] implementation. /// /// [visitor]: crate::field::Visit /// [`MakeVisitor`]: crate::field::MakeVisitor pub struct JsonVisitor<'a> { values: BTreeMap<&'a str, serde_json::Value>, writer: &'a mut dyn Write, } impl<'a> fmt::Debug for JsonVisitor<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_fmt(format_args!("JsonVisitor {{ values: {:?} }}", self.values)) } } impl<'a> JsonVisitor<'a> { /// Returns a new default visitor that formats to the provided `writer`. /// /// # Arguments /// - `writer`: the writer to format to. /// - `is_empty`: whether or not any fields have been previously written to /// that writer. pub fn new(writer: &'a mut dyn Write) -> Self { Self { values: BTreeMap::new(), writer, } } } impl<'a> crate::field::VisitFmt for JsonVisitor<'a> { fn writer(&mut self) -> &mut dyn fmt::Write { self.writer } } impl<'a> crate::field::VisitOutput for JsonVisitor<'a> { fn finish(self) -> fmt::Result { let inner = || { let mut serializer = Serializer::new(WriteAdaptor::new(self.writer)); let mut ser_map = serializer.serialize_map(None)?; for (k, v) in self.values { ser_map.serialize_entry(k, &v)?; } ser_map.end() }; if inner().is_err() { Err(fmt::Error) } else { Ok(()) } } } impl<'a> field::Visit for JsonVisitor<'a> { #[cfg(all(tracing_unstable, feature = "valuable"))] fn record_value(&mut self, field: &Field, value: valuable_crate::Value<'_>) { let value = match serde_json::to_value(valuable_serde::Serializable::new(value)) { Ok(value) => value, Err(_e) => { #[cfg(debug_assertions)] unreachable!( "`valuable::Valuable` implementations should always serialize \ successfully, but an error occurred: {}", _e, ); #[cfg(not(debug_assertions))] return; } }; self.values.insert(field.name(), value); } /// Visit a double precision floating point value. fn record_f64(&mut self, field: &Field, value: f64) { self.values .insert(field.name(), serde_json::Value::from(value)); } /// Visit a signed 64-bit integer value. fn record_i64(&mut self, field: &Field, value: i64) { self.values .insert(field.name(), serde_json::Value::from(value)); } /// Visit an unsigned 64-bit integer value. fn record_u64(&mut self, field: &Field, value: u64) { self.values .insert(field.name(), serde_json::Value::from(value)); } /// Visit a boolean value. fn record_bool(&mut self, field: &Field, value: bool) { self.values .insert(field.name(), serde_json::Value::from(value)); } /// Visit a string value. fn record_str(&mut self, field: &Field, value: &str) { 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 #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => (), name if name.starts_with("r#") => { self.values .insert(&name[2..], serde_json::Value::from(format!("{:?}", value))); } name => { self.values .insert(name, serde_json::Value::from(format!("{:?}", value))); } }; } } #[cfg(test)] mod test { use super::*; use crate::fmt::{format::FmtSpan, test::MockMakeWriter, time::FormatTime, SubscriberBuilder}; use tracing::{self, subscriber::with_default}; use std::fmt; use std::path::Path; struct MockTime; impl FormatTime for MockTime { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!(w, "fake time") } } fn subscriber() -> SubscriberBuilder> { SubscriberBuilder::default().json() } #[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"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_filename() { let current_path = Path::new("tracing-subscriber") .join("src") .join("fmt") .join("format") .join("json.rs") .to_str() .expect("path must be valid unicode") // escape windows backslashes .replace('\\', "\\\\"); let expected = &format!("{}{}{}", "{\"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\",\"filename\":\"", current_path, "\",\"fields\":{\"message\":\"some json test\"}}\n"); let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_file(true) .with_span_list(true); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_line_number() { 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\",\"line_number\":42,\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_line_number(true) .with_span_list(true); test_json_with_line_number(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_flattened_event() { 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\",\"message\":\"some json test\"}\n"; let subscriber = subscriber() .flatten_event(true) .with_current_span(true) .with_span_list(true); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_disabled_current_span_event() { let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(false) .with_span_list(true); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_disabled_span_list_event() { let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":42,\"name\":\"json_span\",\"number\":3},\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(false); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_nested_span() { let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"span\":{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4},\"spans\":[{\"answer\":42,\"name\":\"json_span\",\"number\":3},{\"answer\":43,\"name\":\"nested_json_span\",\"number\":4}],\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, subscriber, || { let span = tracing::span!(tracing::Level::INFO, "json_span", answer = 42, number = 3); let _guard = span.enter(); let span = tracing::span!( tracing::Level::INFO, "nested_json_span", answer = 43, number = 4 ); let _guard = span.enter(); tracing::info!("some json test"); }); } #[test] fn json_no_span() { let expected = "{\"timestamp\":\"fake time\",\"level\":\"INFO\",\"target\":\"tracing_subscriber::fmt::format::json::test\",\"fields\":{\"message\":\"some json test\"}}\n"; let subscriber = subscriber() .flatten_event(false) .with_current_span(true) .with_span_list(true); test_json(expected, subscriber, || { tracing::info!("some json test"); }); } #[test] fn record_works() { // This test reproduces issue #707, where using `Span::record` causes // any events inside the span to be ignored. let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt() .json() .with_writer(make_writer.clone()) .finish(); with_default(subscriber, || { tracing::info!("an event outside the root span"); assert_eq!( parse_as_json(&make_writer)["fields"]["message"], "an event outside the root span" ); let span = tracing::info_span!("the span", na = tracing::field::Empty); span.record("na", "value"); let _enter = span.enter(); tracing::info!("an event inside the root span"); assert_eq!( parse_as_json(&make_writer)["fields"]["message"], "an event inside the root span" ); }); } #[test] fn json_span_event_show_correct_context() { let buffer = MockMakeWriter::default(); let subscriber = subscriber() .with_writer(buffer.clone()) .flatten_event(false) .with_current_span(true) .with_span_list(false) .with_span_events(FmtSpan::FULL) .finish(); with_default(subscriber, || { let context = "parent"; let parent_span = tracing::info_span!("parent_span", context); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "new"); assert_eq!(event["span"]["context"], "parent"); let _parent_enter = parent_span.enter(); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "enter"); assert_eq!(event["span"]["context"], "parent"); let context = "child"; let child_span = tracing::info_span!("child_span", context); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "new"); assert_eq!(event["span"]["context"], "child"); let _child_enter = child_span.enter(); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "enter"); assert_eq!(event["span"]["context"], "child"); drop(_child_enter); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "exit"); assert_eq!(event["span"]["context"], "child"); drop(child_span); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "close"); assert_eq!(event["span"]["context"], "child"); drop(_parent_enter); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "exit"); assert_eq!(event["span"]["context"], "parent"); drop(parent_span); let event = parse_as_json(&buffer); assert_eq!(event["fields"]["message"], "close"); assert_eq!(event["span"]["context"], "parent"); }); } #[test] fn json_span_event_with_no_fields() { // Check span events serialize correctly. // Discussion: https://github.com/tokio-rs/tracing/issues/829#issuecomment-661984255 let buffer = MockMakeWriter::default(); let subscriber = subscriber() .with_writer(buffer.clone()) .flatten_event(false) .with_current_span(false) .with_span_list(false) .with_span_events(FmtSpan::FULL) .finish(); with_default(subscriber, || { let span = tracing::info_span!("valid_json"); assert_eq!(parse_as_json(&buffer)["fields"]["message"], "new"); let _enter = span.enter(); assert_eq!(parse_as_json(&buffer)["fields"]["message"], "enter"); drop(_enter); assert_eq!(parse_as_json(&buffer)["fields"]["message"], "exit"); drop(span); assert_eq!(parse_as_json(&buffer)["fields"]["message"], "close"); }); } fn parse_as_json(buffer: &MockMakeWriter) -> serde_json::Value { let buf = String::from_utf8(buffer.buf().to_vec()).unwrap(); let json = buf .lines() .last() .expect("expected at least one line to be written!"); match serde_json::from_str(json) { Ok(v) => v, Err(e) => panic!( "assertion failed: JSON shouldn't be malformed\n error: {}\n json: {}", e, json ), } } fn test_json( expected: &str, builder: crate::fmt::SubscriberBuilder>, producer: impl FnOnce() -> T, ) { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_timer(MockTime) .finish(); with_default(subscriber, producer); let buf = make_writer.buf(); let actual = std::str::from_utf8(&buf[..]).unwrap(); assert_eq!( serde_json::from_str::>(expected) .unwrap(), serde_json::from_str(actual).unwrap() ); } fn test_json_with_line_number( expected: &str, builder: crate::fmt::SubscriberBuilder>, producer: impl FnOnce() -> T, ) { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_timer(MockTime) .finish(); with_default(subscriber, producer); let buf = make_writer.buf(); let actual = std::str::from_utf8(&buf[..]).unwrap(); let mut expected = serde_json::from_str::>(expected) .unwrap(); let expect_line_number = expected.remove("line_number").is_some(); let mut actual: std::collections::HashMap<&str, serde_json::Value> = serde_json::from_str(actual).unwrap(); let line_number = actual.remove("line_number"); if expect_line_number { assert_eq!(line_number.map(|x| x.is_number()), Some(true)); } else { assert!(line_number.is_none()); } assert_eq!(actual, expected); } } tracing-subscriber-0.3.18/src/fmt/format/mod.rs000064400000000000000000002356321046102023000175120ustar 00000000000000//! Formatters for logging `tracing` events. //! //! This module provides several formatter implementations, as well as utilities //! for implementing custom formatters. //! //! # Formatters //! This module provides a number of formatter implementations: //! //! * [`Full`]: The default formatter. This emits human-readable, //! single-line logs for each event that occurs, with the current span context //! displayed before the formatted representation of the event. See //! [here](Full#example-output) for sample output. //! //! * [`Compact`]: A variant of the default formatter, optimized for //! short line lengths. Fields from the current span context are appended to //! the fields of the formatted event, and span names are not shown; the //! verbosity level is abbreviated to a single character. See //! [here](Compact#example-output) for sample output. //! //! * [`Pretty`]: Emits excessively pretty, multi-line logs, optimized //! for human readability. This is primarily intended to be used in local //! development and debugging, or for command-line applications, where //! automated analysis and compact storage of logs is less of a priority than //! readability and visual appeal. See [here](Pretty#example-output) //! for sample output. //! //! * [`Json`]: Outputs newline-delimited JSON logs. This is intended //! for production use with systems where structured logs are consumed as JSON //! by analysis and viewing tools. The JSON output is not optimized for human //! readability. See [here](Json#example-output) for sample output. use super::time::{FormatTime, SystemTime}; use crate::{ field::{MakeOutput, MakeVisitor, RecordFields, VisitFmt, VisitOutput}, fmt::fmt_layer::FmtContext, fmt::fmt_layer::FormattedFields, registry::LookupSpan, }; use std::fmt::{self, Debug, Display, Write}; use tracing_core::{ field::{self, Field, Visit}, span, Event, Level, Subscriber, }; #[cfg(feature = "tracing-log")] use tracing_log::NormalizeEvent; #[cfg(feature = "ansi")] use nu_ansi_term::{Color, Style}; #[cfg(feature = "json")] mod json; #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub use json::*; #[cfg(feature = "ansi")] mod pretty; #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub use pretty::*; /// A type that can format a tracing [`Event`] to a [`Writer`]. /// /// `FormatEvent` is primarily used in the context of [`fmt::Subscriber`] or /// [`fmt::Layer`]. Each time an event is dispatched to [`fmt::Subscriber`] or /// [`fmt::Layer`], the subscriber or layer /// forwards it to its associated `FormatEvent` to emit a log message. /// /// This trait is already implemented for function pointers with the same /// signature as `format_event`. /// /// # Arguments /// /// The following arguments are passed to `FormatEvent::format_event`: /// /// * A [`FmtContext`]. This is an extension of the [`layer::Context`] type, /// which can be used for accessing stored information such as the current /// span context an event occurred in. /// /// In addition, [`FmtContext`] exposes access to the [`FormatFields`] /// implementation that the subscriber was configured to use via the /// [`FmtContext::field_format`] method. This can be used when the /// [`FormatEvent`] implementation needs to format the event's fields. /// /// For convenience, [`FmtContext`] also [implements `FormatFields`], /// forwarding to the configured [`FormatFields`] type. /// /// * A [`Writer`] to which the formatted representation of the event is /// written. This type implements the [`std::fmt::Write`] trait, and therefore /// can be used with the [`std::write!`] and [`std::writeln!`] macros, as well /// as calling [`std::fmt::Write`] methods directly. /// /// The [`Writer`] type also implements additional methods that provide /// information about how the event should be formatted. The /// [`Writer::has_ansi_escapes`] method indicates whether [ANSI terminal /// escape codes] are supported by the underlying I/O writer that the event /// will be written to. If this returns `true`, the formatter is permitted to /// use ANSI escape codes to add colors and other text formatting to its /// output. If it returns `false`, the event will be written to an output that /// does not support ANSI escape codes (such as a log file), and they should /// not be emitted. /// /// Crates like [`nu_ansi_term`] and [`owo-colors`] can be used to add ANSI /// escape codes to formatted output. /// /// * The actual [`Event`] to be formatted. /// /// # Examples /// /// This example re-implements a simiplified version of this crate's [default /// formatter]: /// /// ```rust /// use std::fmt; /// use tracing_core::{Subscriber, Event}; /// use tracing_subscriber::fmt::{ /// format::{self, FormatEvent, FormatFields}, /// FmtContext, /// FormattedFields, /// }; /// use tracing_subscriber::registry::LookupSpan; /// /// struct MyFormatter; /// /// impl FormatEvent for MyFormatter /// where /// S: Subscriber + for<'a> LookupSpan<'a>, /// N: for<'a> FormatFields<'a> + 'static, /// { /// fn format_event( /// &self, /// ctx: &FmtContext<'_, S, N>, /// mut writer: format::Writer<'_>, /// event: &Event<'_>, /// ) -> fmt::Result { /// // Format values from the event's's metadata: /// let metadata = event.metadata(); /// write!(&mut writer, "{} {}: ", metadata.level(), metadata.target())?; /// /// // Format all the spans in the event's span context. /// if let Some(scope) = ctx.event_scope() { /// for span in scope.from_root() { /// write!(writer, "{}", span.name())?; /// /// // `FormattedFields` is a formatted representation of the span's /// // fields, which is stored in its extensions by the `fmt` layer's /// // `new_span` method. The fields will have been formatted /// // by the same field formatter that's provided to the event /// // formatter in the `FmtContext`. /// let ext = span.extensions(); /// let fields = &ext /// .get::>() /// .expect("will never be `None`"); /// /// // Skip formatting the fields if the span had no fields. /// if !fields.is_empty() { /// write!(writer, "{{{}}}", fields)?; /// } /// write!(writer, ": ")?; /// } /// } /// /// // Write fields on the event /// ctx.field_format().format_fields(writer.by_ref(), event)?; /// /// writeln!(writer) /// } /// } /// /// let _subscriber = tracing_subscriber::fmt() /// .event_format(MyFormatter) /// .init(); /// /// let _span = tracing::info_span!("my_span", answer = 42).entered(); /// tracing::info!(question = "life, the universe, and everything", "hello world"); /// ``` /// /// This formatter will print events like this: /// /// ```text /// DEBUG yak_shaving::shaver: some-span{field-on-span=foo}: started shaving yak /// ``` /// /// [`layer::Context`]: crate::layer::Context /// [`fmt::Layer`]: super::Layer /// [`fmt::Subscriber`]: super::Subscriber /// [`Event`]: tracing::Event /// [implements `FormatFields`]: super::FmtContext#impl-FormatFields<'writer> /// [ANSI terminal escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code /// [`Writer::has_ansi_escapes`]: Writer::has_ansi_escapes /// [`nu_ansi_term`]: https://crates.io/crates/nu_ansi_term /// [`owo-colors`]: https://crates.io/crates/owo-colors /// [default formatter]: Full pub trait FormatEvent where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, { /// Write a log message for `Event` in `Context` to the given [`Writer`]. fn format_event( &self, ctx: &FmtContext<'_, S, N>, writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result; } impl FormatEvent for fn(ctx: &FmtContext<'_, S, N>, Writer<'_>, &Event<'_>) -> fmt::Result where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, { fn format_event( &self, ctx: &FmtContext<'_, S, N>, writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { (*self)(ctx, writer, event) } } /// A type that can format a [set of fields] to a [`Writer`]. /// /// `FormatFields` is primarily used in the context of [`FmtSubscriber`]. Each /// time a span or event with fields is recorded, the subscriber will format /// those fields with its associated `FormatFields` implementation. /// /// [set of fields]: crate::field::RecordFields /// [`FmtSubscriber`]: super::Subscriber pub trait FormatFields<'writer> { /// Format the provided `fields` to the provided [`Writer`], returning a result. fn format_fields(&self, writer: Writer<'writer>, fields: R) -> fmt::Result; /// Record additional field(s) on an existing span. /// /// By default, this appends a space to the current set of fields if it is /// non-empty, and then calls `self.format_fields`. If different behavior is /// required, the default implementation of this method can be overridden. fn add_fields( &self, current: &'writer mut FormattedFields, fields: &span::Record<'_>, ) -> fmt::Result { if !current.fields.is_empty() { current.fields.push(' '); } self.format_fields(current.as_writer(), fields) } } /// Returns the default configuration for an [event formatter]. /// /// Methods on the returned event formatter can be used for further /// configuration. For example: /// /// ```rust /// let format = tracing_subscriber::fmt::format() /// .without_time() // Don't include timestamps /// .with_target(false) // Don't include event targets. /// .with_level(false) // Don't include event levels. /// .compact(); // Use a more compact, abbreviated format. /// /// // Use the configured formatter when building a new subscriber. /// tracing_subscriber::fmt() /// .event_format(format) /// .init(); /// ``` pub fn format() -> Format { Format::default() } /// Returns the default configuration for a JSON [event formatter]. #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json() -> Format { format().json() } /// Returns a [`FormatFields`] implementation that formats fields using the /// provided function or closure. /// pub fn debug_fn(f: F) -> FieldFn where F: Fn(&mut Writer<'_>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, { FieldFn(f) } /// A writer to which formatted representations of spans and events are written. /// /// This type is provided as input to the [`FormatEvent::format_event`] and /// [`FormatFields::format_fields`] methods, which will write formatted /// representations of [`Event`]s and [fields] to the `Writer`. /// /// This type implements the [`std::fmt::Write`] trait, allowing it to be used /// with any function that takes an instance of [`std::fmt::Write`]. /// Additionally, it can be used with the standard library's [`std::write!`] and /// [`std::writeln!`] macros. /// /// Additionally, a `Writer` may expose additional `tracing`-specific /// information to the formatter implementation. /// /// [fields]: tracing_core::field pub struct Writer<'writer> { writer: &'writer mut dyn fmt::Write, // TODO(eliza): add ANSI support is_ansi: bool, } /// A [`FormatFields`] implementation that formats fields by calling a function /// or closure. /// #[derive(Debug, Clone)] pub struct FieldFn(F); /// The [visitor] produced by [`FieldFn`]'s [`MakeVisitor`] implementation. /// /// [visitor]: super::super::field::Visit /// [`MakeVisitor`]: super::super::field::MakeVisitor pub struct FieldFnVisitor<'a, F> { f: F, writer: Writer<'a>, result: fmt::Result, } /// Marker for [`Format`] that indicates that the compact log format should be used. /// /// The compact format includes fields from all currently entered spans, after /// the event's fields. Span fields are ordered (but not grouped) by /// span, and span names are not shown. A more compact representation of the /// event's [`Level`] is used, and additional information—such as the event's /// target—is disabled by default. /// /// # Example Output /// ///
:; cargo run --example fmt-compact
///     Finished dev [unoptimized + debuginfo] target(s) in 0.08s
///      Running `target/debug/examples/fmt-compact`
/// 2022-02-17T19:51:05.809287Z  INFO fmt_compact: preparing to shave yaks number_of_yaks=3
/// 2022-02-17T19:51:05.809367Z  INFO shaving_yaks: fmt_compact::yak_shave: shaving yaks yaks=3
/// 2022-02-17T19:51:05.809414Z TRACE shaving_yaks:shave: fmt_compact::yak_shave: hello! I'm gonna shave a yak excitement="yay!" yaks=3 yak=1
/// 2022-02-17T19:51:05.809443Z TRACE shaving_yaks:shave: fmt_compact::yak_shave: yak shaved successfully yaks=3 yak=1
/// 2022-02-17T19:51:05.809477Z DEBUG shaving_yaks: yak_events: yak=1 shaved=true yaks=3
/// 2022-02-17T19:51:05.809500Z TRACE shaving_yaks: fmt_compact::yak_shave: yaks_shaved=1 yaks=3
/// 2022-02-17T19:51:05.809531Z TRACE shaving_yaks:shave: fmt_compact::yak_shave: hello! I'm gonna shave a yak excitement="yay!" yaks=3 yak=2
/// 2022-02-17T19:51:05.809554Z TRACE shaving_yaks:shave: fmt_compact::yak_shave: yak shaved successfully yaks=3 yak=2
/// 2022-02-17T19:51:05.809581Z DEBUG shaving_yaks: yak_events: yak=2 shaved=true yaks=3
/// 2022-02-17T19:51:05.809606Z TRACE shaving_yaks: fmt_compact::yak_shave: yaks_shaved=2 yaks=3
/// 2022-02-17T19:51:05.809635Z TRACE shaving_yaks:shave: fmt_compact::yak_shave: hello! I'm gonna shave a yak excitement="yay!" yaks=3 yak=3
/// 2022-02-17T19:51:05.809664Z  WARN shaving_yaks:shave: fmt_compact::yak_shave: could not locate yak yaks=3 yak=3
/// 2022-02-17T19:51:05.809693Z DEBUG shaving_yaks: yak_events: yak=3 shaved=false yaks=3
/// 2022-02-17T19:51:05.809717Z ERROR shaving_yaks: fmt_compact::yak_shave: failed to shave yak yak=3 error=missing yak error.sources=[out of space, out of cash] yaks=3
/// 2022-02-17T19:51:05.809743Z TRACE shaving_yaks: fmt_compact::yak_shave: yaks_shaved=2 yaks=3
/// 2022-02-17T19:51:05.809768Z  INFO fmt_compact: yak shaving completed all_yaks_shaved=false
///
/// 
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub struct Compact; /// Marker for [`Format`] that indicates that the default log format should be used. /// /// This formatter shows the span context before printing event data. Spans are /// displayed including their names and fields. /// /// # Example Output /// ///
:; cargo run --example fmt
///     Finished dev [unoptimized + debuginfo] target(s) in 0.08s
///      Running `target/debug/examples/fmt`
/// 2022-02-15T18:40:14.289898Z  INFO fmt: preparing to shave yaks number_of_yaks=3
/// 2022-02-15T18:40:14.289974Z  INFO shaving_yaks{yaks=3}: fmt::yak_shave: shaving yaks
/// 2022-02-15T18:40:14.290011Z TRACE shaving_yaks{yaks=3}:shave{yak=1}: fmt::yak_shave: hello! I'm gonna shave a yak excitement="yay!"
/// 2022-02-15T18:40:14.290038Z TRACE shaving_yaks{yaks=3}:shave{yak=1}: fmt::yak_shave: yak shaved successfully
/// 2022-02-15T18:40:14.290070Z DEBUG shaving_yaks{yaks=3}: yak_events: yak=1 shaved=true
/// 2022-02-15T18:40:14.290089Z TRACE shaving_yaks{yaks=3}: fmt::yak_shave: yaks_shaved=1
/// 2022-02-15T18:40:14.290114Z TRACE shaving_yaks{yaks=3}:shave{yak=2}: fmt::yak_shave: hello! I'm gonna shave a yak excitement="yay!"
/// 2022-02-15T18:40:14.290134Z TRACE shaving_yaks{yaks=3}:shave{yak=2}: fmt::yak_shave: yak shaved successfully
/// 2022-02-15T18:40:14.290157Z DEBUG shaving_yaks{yaks=3}: yak_events: yak=2 shaved=true
/// 2022-02-15T18:40:14.290174Z TRACE shaving_yaks{yaks=3}: fmt::yak_shave: yaks_shaved=2
/// 2022-02-15T18:40:14.290198Z TRACE shaving_yaks{yaks=3}:shave{yak=3}: fmt::yak_shave: hello! I'm gonna shave a yak excitement="yay!"
/// 2022-02-15T18:40:14.290222Z  WARN shaving_yaks{yaks=3}:shave{yak=3}: fmt::yak_shave: could not locate yak
/// 2022-02-15T18:40:14.290247Z DEBUG shaving_yaks{yaks=3}: yak_events: yak=3 shaved=false
/// 2022-02-15T18:40:14.290268Z ERROR shaving_yaks{yaks=3}: fmt::yak_shave: failed to shave yak yak=3 error=missing yak error.sources=[out of space, out of cash]
/// 2022-02-15T18:40:14.290287Z TRACE shaving_yaks{yaks=3}: fmt::yak_shave: yaks_shaved=2
/// 2022-02-15T18:40:14.290309Z  INFO fmt: yak shaving completed. all_yaks_shaved=false
/// 
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)] pub struct Full; /// A pre-configured event formatter. /// /// You will usually want to use this as the `FormatEvent` for a `FmtSubscriber`. /// /// The default logging format, [`Full`] includes all fields in each event and its containing /// spans. The [`Compact`] logging format is intended to produce shorter log /// lines; it displays each event's fields, along with fields from the current /// span context, but other information is abbreviated. The [`Pretty`] logging /// format is an extra-verbose, multi-line human-readable logging format /// intended for use in development. #[derive(Debug, Clone)] pub struct Format { format: F, pub(crate) timer: T, pub(crate) ansi: Option, pub(crate) display_timestamp: bool, pub(crate) display_target: bool, pub(crate) display_level: bool, pub(crate) display_thread_id: bool, pub(crate) display_thread_name: bool, pub(crate) display_filename: bool, pub(crate) display_line_number: bool, } // === impl Writer === impl<'writer> Writer<'writer> { // TODO(eliza): consider making this a public API? // 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...?) //(@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, } } // TODO(eliza): consider making this a public API? pub(crate) fn with_ansi(self, is_ansi: bool) -> Self { Self { is_ansi, ..self } } /// Return a new `Writer` that mutably borrows `self`. /// /// This can be used to temporarily borrow a `Writer` to pass a new `Writer` /// to a function that takes a `Writer` by value, allowing the original writer /// to still be used once that function returns. pub fn by_ref(&mut self) -> Writer<'_> { let is_ansi = self.is_ansi; Writer { writer: self as &mut dyn fmt::Write, is_ansi, } } /// Writes a string slice into this `Writer`, returning whether the write succeeded. /// /// This method can only succeed if the entire string slice was successfully /// written, and this method will not return until all data has been written /// or an error occurs. /// /// This is identical to calling the [`write_str` method] from the `Writer`'s /// [`std::fmt::Write`] implementation. However, it is also provided as an /// inherent method, so that `Writer`s can be used without needing to import the /// [`std::fmt::Write`] trait. /// /// # Errors /// /// This function will return an instance of [`std::fmt::Error`] on error. /// /// [`write_str` method]: std::fmt::Write::write_str #[inline] pub fn write_str(&mut self, s: &str) -> fmt::Result { self.writer.write_str(s) } /// Writes a [`char`] into this writer, returning whether the write succeeded. /// /// A single [`char`] may be encoded as more than one byte. /// This method can only succeed if the entire byte sequence was successfully /// written, and this method will not return until all data has been /// written or an error occurs. /// /// This is identical to calling the [`write_char` method] from the `Writer`'s /// [`std::fmt::Write`] implementation. However, it is also provided as an /// inherent method, so that `Writer`s can be used without needing to import the /// [`std::fmt::Write`] trait. /// /// # Errors /// /// This function will return an instance of [`std::fmt::Error`] on error. /// /// [`write_char` method]: std::fmt::Write::write_char #[inline] pub fn write_char(&mut self, c: char) -> fmt::Result { self.writer.write_char(c) } /// Glue for usage of the [`write!`] macro with `Writer`s. /// /// This method should generally not be invoked manually, but rather through /// the [`write!`] macro itself. /// /// This is identical to calling the [`write_fmt` method] from the `Writer`'s /// [`std::fmt::Write`] implementation. However, it is also provided as an /// inherent method, so that `Writer`s can be used with the [`write!` macro] /// without needing to import the /// [`std::fmt::Write`] trait. /// /// [`write_fmt` method]: std::fmt::Write::write_fmt pub fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { self.writer.write_fmt(args) } /// Returns `true` if [ANSI escape codes] may be used to add colors /// and other formatting when writing to this `Writer`. /// /// If this returns `false`, formatters should not emit ANSI escape codes. /// /// [ANSI escape codes]: https://en.wikipedia.org/wiki/ANSI_escape_code pub fn has_ansi_escapes(&self) -> bool { self.is_ansi } pub(in crate::fmt::format) fn bold(&self) -> Style { #[cfg(feature = "ansi")] { if self.is_ansi { return Style::new().bold(); } } Style::new() } pub(in crate::fmt::format) fn dimmed(&self) -> Style { #[cfg(feature = "ansi")] { if self.is_ansi { return Style::new().dimmed(); } } Style::new() } pub(in crate::fmt::format) fn italic(&self) -> Style { #[cfg(feature = "ansi")] { if self.is_ansi { return Style::new().italic(); } } Style::new() } } impl fmt::Write for Writer<'_> { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { Writer::write_str(self, s) } #[inline] fn write_char(&mut self, c: char) -> fmt::Result { Writer::write_char(self, c) } #[inline] fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { Writer::write_fmt(self, args) } } impl fmt::Debug for Writer<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Writer") .field("writer", &format_args!("<&mut dyn fmt::Write>")) .field("is_ansi", &self.is_ansi) .finish() } } // === impl Format === impl Default for Format { fn default() -> Self { Format { format: Full, timer: SystemTime, ansi: None, display_timestamp: true, display_target: true, display_level: true, display_thread_id: false, display_thread_name: false, display_filename: false, display_line_number: false, } } } impl Format { /// Use a less verbose output format. /// /// See [`Compact`]. pub fn compact(self) -> Format { Format { format: Compact, timer: self.timer, ansi: self.ansi, display_target: self.display_target, display_timestamp: self.display_timestamp, display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, display_filename: self.display_filename, display_line_number: self.display_line_number, } } /// Use an excessively pretty, human-readable output format. /// /// See [`Pretty`]. /// /// Note that this requires the "ansi" feature to be enabled. /// /// # Options /// /// [`Format::with_ansi`] can be used to disable ANSI terminal escape codes (which enable /// formatting such as colors, bold, italic, etc) in event formatting. However, a field /// formatter must be manually provided to avoid ANSI in the formatting of parent spans, like /// so: /// /// ``` /// # use tracing_subscriber::fmt::format; /// tracing_subscriber::fmt() /// .pretty() /// .with_ansi(false) /// .fmt_fields(format::PrettyFields::new().with_ansi(false)) /// // ... other settings ... /// .init(); /// ``` #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn pretty(self) -> Format { Format { format: Pretty::default(), timer: self.timer, ansi: self.ansi, display_target: self.display_target, display_timestamp: self.display_timestamp, display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, display_filename: true, display_line_number: true, } } /// Use the full JSON format. /// /// The full format includes fields from all entered spans. /// /// # Example Output /// /// ```ignore,json /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate","fields":{"message":"some message", "key": "value"}} /// ``` /// /// # Options /// /// - [`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 { Format { format: Json::default(), timer: self.timer, ansi: self.ansi, display_target: self.display_target, display_timestamp: self.display_timestamp, display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, display_filename: self.display_filename, display_line_number: self.display_line_number, } } /// Use the given [`timer`] for log message timestamps. /// /// See [`time` module] for the provided timer implementations. /// /// Note that using the `"time"` feature flag enables the /// additional time formatters [`UtcTime`] and [`LocalTime`], which use the /// [`time` crate] to provide more sophisticated timestamp formatting /// options. /// /// [`timer`]: super::time::FormatTime /// [`time` module]: mod@super::time /// [`UtcTime`]: super::time::UtcTime /// [`LocalTime`]: super::time::LocalTime /// [`time` crate]: https://docs.rs/time/0.3 pub fn with_timer(self, timer: T2) -> Format { Format { format: self.format, timer, ansi: self.ansi, display_target: self.display_target, display_timestamp: self.display_timestamp, display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, display_filename: self.display_filename, display_line_number: self.display_line_number, } } /// Do not emit timestamps with log messages. pub fn without_time(self) -> Format { Format { format: self.format, timer: (), ansi: self.ansi, display_timestamp: false, display_target: self.display_target, display_level: self.display_level, display_thread_id: self.display_thread_id, display_thread_name: self.display_thread_name, display_filename: self.display_filename, display_line_number: self.display_line_number, } } /// Enable ANSI terminal colors for formatted output. pub fn with_ansi(self, ansi: bool) -> Format { Format { ansi: Some(ansi), ..self } } /// Sets whether or not an event's target is displayed. pub fn with_target(self, display_target: bool) -> Format { Format { display_target, ..self } } /// Sets whether or not an event's level is displayed. pub fn with_level(self, display_level: bool) -> Format { Format { display_level, ..self } } /// Sets whether or not the [thread ID] of the current thread is displayed /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids(self, display_thread_id: bool) -> Format { Format { display_thread_id, ..self } } /// Sets whether or not the [name] of the current thread is displayed /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names(self, display_thread_name: bool) -> Format { Format { display_thread_name, ..self } } /// Sets whether or not an event's [source code file path][file] is /// displayed. /// /// [file]: tracing_core::Metadata::file pub fn with_file(self, display_filename: bool) -> Format { Format { display_filename, ..self } } /// Sets whether or not an event's [source code line number][line] is /// displayed. /// /// [line]: tracing_core::Metadata::line pub fn with_line_number(self, display_line_number: bool) -> Format { Format { display_line_number, ..self } } /// Sets whether or not the source code location from which an event /// originated is displayed. /// /// This is equivalent to calling [`Format::with_file`] and /// [`Format::with_line_number`] with the same value. pub fn with_source_location(self, display_location: bool) -> Self { self.with_line_number(display_location) .with_file(display_location) } #[inline] fn format_timestamp(&self, writer: &mut Writer<'_>) -> fmt::Result where T: FormatTime, { // If timestamps are disabled, do nothing. if !self.display_timestamp { return Ok(()); } // If ANSI color codes are enabled, format the timestamp with ANSI // colors. #[cfg(feature = "ansi")] { if writer.has_ansi_escapes() { let style = Style::new().dimmed(); write!(writer, "{}", style.prefix())?; // If getting the timestamp failed, don't bail --- only bail on // formatting errors. if self.timer.format_time(writer).is_err() { writer.write_str("")?; } write!(writer, "{} ", style.suffix())?; return Ok(()); } } // Otherwise, just format the timestamp without ANSI formatting. // If getting the timestamp failed, don't bail --- only bail on // formatting errors. if self.timer.format_time(writer).is_err() { writer.write_str("")?; } writer.write_char(' ') } } #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl Format { /// Use the full JSON format with the event's event fields flattened. /// /// # Example Output /// /// ```ignore,json /// {"timestamp":"Feb 20 11:28:15.096","level":"INFO","target":"mycrate", "message":"some message", "key": "value"} /// ``` /// See [`Json`][super::format::Json]. #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn flatten_event(mut self, flatten_event: bool) -> Format { self.format.flatten_event(flatten_event); self } /// Sets whether or not the formatter will include the current span in /// formatted events. /// /// See [`format::Json`][Json] #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn with_current_span(mut self, display_current_span: bool) -> Format { self.format.with_current_span(display_current_span); self } /// Sets whether or not the formatter will include a list (from root to /// leaf) of all currently entered spans in formatted events. /// /// See [`format::Json`][Json] #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn with_span_list(mut self, display_span_list: bool) -> Format { self.format.with_span_list(display_span_list); self } } impl FormatEvent for Format where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, T: FormatTime, { fn format_event( &self, ctx: &FmtContext<'_, S, N>, mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[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(); // if the `Format` struct *also* has an ANSI color configuration, // override the writer...the API for configuring ANSI color codes on the // `Format` struct is deprecated, but we still need to honor those // configurations. if let Some(ansi) = self.ansi { writer = writer.with_ansi(ansi); } self.format_timestamp(&mut writer)?; if self.display_level { let fmt_level = { #[cfg(feature = "ansi")] { FmtLevel::new(meta.level(), writer.has_ansi_escapes()) } #[cfg(not(feature = "ansi"))] { FmtLevel::new(meta.level()) } }; write!(writer, "{} ", fmt_level)?; } if self.display_thread_name { let current_thread = std::thread::current(); match current_thread.name() { Some(name) => { write!(writer, "{} ", FmtThreadName::new(name))?; } // fall-back to thread id when name is absent and ids are not enabled None if !self.display_thread_id => { write!(writer, "{:0>2?} ", current_thread.id())?; } _ => {} } } if self.display_thread_id { write!(writer, "{:0>2?} ", std::thread::current().id())?; } let dimmed = writer.dimmed(); if let Some(scope) = ctx.event_scope() { let bold = writer.bold(); let mut seen = false; for span in scope.from_root() { write!(writer, "{}", bold.paint(span.metadata().name()))?; seen = true; let ext = span.extensions(); if let Some(fields) = &ext.get::>() { if !fields.is_empty() { write!(writer, "{}{}{}", bold.paint("{"), fields, bold.paint("}"))?; } } write!(writer, "{}", dimmed.paint(":"))?; } if seen { writer.write_char(' ')?; } }; if self.display_target { write!( writer, "{}{} ", dimmed.paint(meta.target()), dimmed.paint(":") )?; } let line_number = if self.display_line_number { meta.line() } else { None }; if self.display_filename { if let Some(filename) = meta.file() { write!( writer, "{}{}{}", dimmed.paint(filename), dimmed.paint(":"), if line_number.is_some() { "" } else { " " } )?; } } if let Some(line_number) = line_number { write!( writer, "{}{}:{} ", dimmed.prefix(), line_number, dimmed.suffix() )?; } ctx.format_fields(writer.by_ref(), event)?; writeln!(writer) } } impl FormatEvent for Format where S: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, T: FormatTime, { fn format_event( &self, ctx: &FmtContext<'_, S, N>, mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[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(); // if the `Format` struct *also* has an ANSI color configuration, // override the writer...the API for configuring ANSI color codes on the // `Format` struct is deprecated, but we still need to honor those // configurations. if let Some(ansi) = self.ansi { writer = writer.with_ansi(ansi); } self.format_timestamp(&mut writer)?; if self.display_level { let fmt_level = { #[cfg(feature = "ansi")] { FmtLevel::new(meta.level(), writer.has_ansi_escapes()) } #[cfg(not(feature = "ansi"))] { FmtLevel::new(meta.level()) } }; write!(writer, "{} ", fmt_level)?; } if self.display_thread_name { let current_thread = std::thread::current(); match current_thread.name() { Some(name) => { write!(writer, "{} ", FmtThreadName::new(name))?; } // fall-back to thread id when name is absent and ids are not enabled None if !self.display_thread_id => { write!(writer, "{:0>2?} ", current_thread.id())?; } _ => {} } } if self.display_thread_id { write!(writer, "{:0>2?} ", std::thread::current().id())?; } let fmt_ctx = { #[cfg(feature = "ansi")] { FmtCtx::new(ctx, event.parent(), writer.has_ansi_escapes()) } #[cfg(not(feature = "ansi"))] { FmtCtx::new(&ctx, event.parent()) } }; write!(writer, "{}", fmt_ctx)?; let dimmed = writer.dimmed(); let mut needs_space = false; if self.display_target { write!( writer, "{}{}", dimmed.paint(meta.target()), dimmed.paint(":") )?; needs_space = true; } if self.display_filename { if let Some(filename) = meta.file() { if self.display_target { writer.write_char(' ')?; } write!(writer, "{}{}", dimmed.paint(filename), dimmed.paint(":"))?; needs_space = true; } } if self.display_line_number { if let Some(line_number) = meta.line() { write!( writer, "{}{}{}{}", dimmed.prefix(), line_number, dimmed.suffix(), dimmed.paint(":") )?; needs_space = true; } } if needs_space { writer.write_char(' ')?; } ctx.format_fields(writer.by_ref(), event)?; for span in ctx .event_scope() .into_iter() .flat_map(crate::registry::Scope::from_root) { let exts = span.extensions(); if let Some(fields) = exts.get::>() { if !fields.is_empty() { write!(writer, " {}", dimmed.paint(&fields.fields))?; } } } writeln!(writer) } } // === impl FormatFields === impl<'writer, M> FormatFields<'writer> for M where M: MakeOutput, fmt::Result>, M::Visitor: VisitFmt + VisitOutput, { fn format_fields(&self, writer: Writer<'writer>, fields: R) -> fmt::Result { let mut v = self.make_visitor(writer); fields.record(&mut v); v.finish() } } /// The default [`FormatFields`] implementation. /// #[derive(Debug)] pub struct DefaultFields { // reserve the ability to add fields to this without causing a breaking // change in the future. _private: (), } /// The [visitor] produced by [`DefaultFields`]'s [`MakeVisitor`] implementation. /// /// [visitor]: super::super::field::Visit /// [`MakeVisitor`]: super::super::field::MakeVisitor #[derive(Debug)] pub struct DefaultVisitor<'a> { writer: Writer<'a>, is_empty: bool, result: fmt::Result, } impl DefaultFields { /// Returns a new default [`FormatFields`] implementation. /// pub fn new() -> Self { Self { _private: () } } } impl Default for DefaultFields { fn default() -> Self { Self::new() } } impl<'a> MakeVisitor> for DefaultFields { type Visitor = DefaultVisitor<'a>; #[inline] fn make_visitor(&self, target: Writer<'a>) -> Self::Visitor { DefaultVisitor::new(target, true) } } // === impl DefaultVisitor === impl<'a> DefaultVisitor<'a> { /// Returns a new default visitor that formats to the provided `writer`. /// /// # Arguments /// - `writer`: the writer to format to. /// - `is_empty`: whether or not any fields have been previously written to /// that writer. pub fn new(writer: Writer<'a>, is_empty: bool) -> Self { Self { writer, is_empty, result: Ok(()), } } fn maybe_pad(&mut self) { if self.is_empty { self.is_empty = false; } else { self.result = write!(self.writer, " "); } } } impl<'a> field::Visit for DefaultVisitor<'a> { fn record_str(&mut self, field: &Field, value: &str) { if self.result.is_err() { return; } if field.name() == "message" { self.record_debug(field, &format_args!("{}", value)) } else { self.record_debug(field, &value) } } fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { if let Some(source) = value.source() { let italic = self.writer.italic(); self.record_debug( field, &format_args!( "{} {}{}{}{}", value, italic.paint(field.name()), italic.paint(".sources"), self.writer.dimmed().paint("="), ErrorSourceList(source) ), ) } else { self.record_debug(field, &format_args!("{}", value)) } } fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { if self.result.is_err() { return; } self.maybe_pad(); self.result = match field.name() { "message" => write!(self.writer, "{:?}", value), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => Ok(()), name if name.starts_with("r#") => write!( self.writer, "{}{}{:?}", self.writer.italic().paint(&name[2..]), self.writer.dimmed().paint("="), value ), name => write!( self.writer, "{}{}{:?}", self.writer.italic().paint(name), self.writer.dimmed().paint("="), value ), }; } } impl<'a> crate::field::VisitOutput for DefaultVisitor<'a> { fn finish(self) -> fmt::Result { self.result } } impl<'a> crate::field::VisitFmt for DefaultVisitor<'a> { fn writer(&mut self) -> &mut dyn fmt::Write { &mut self.writer } } /// Renders an error into a list of sources, *including* the error struct ErrorSourceList<'a>(&'a (dyn std::error::Error + 'static)); impl<'a> Display for ErrorSourceList<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut list = f.debug_list(); let mut curr = Some(self.0); while let Some(curr_err) = curr { list.entry(&format_args!("{}", curr_err)); curr = curr_err.source(); } list.finish() } } struct FmtCtx<'a, S, N> { ctx: &'a FmtContext<'a, S, N>, span: Option<&'a span::Id>, #[cfg(feature = "ansi")] ansi: bool, } impl<'a, S, N: 'a> FmtCtx<'a, S, N> where S: Subscriber + for<'lookup> LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, { #[cfg(feature = "ansi")] pub(crate) fn new( ctx: &'a FmtContext<'_, S, N>, span: Option<&'a span::Id>, ansi: bool, ) -> Self { Self { ctx, span, ansi } } #[cfg(not(feature = "ansi"))] pub(crate) fn new(ctx: &'a FmtContext<'_, S, N>, span: Option<&'a span::Id>) -> Self { Self { ctx, span } } fn bold(&self) -> Style { #[cfg(feature = "ansi")] { if self.ansi { return Style::new().bold(); } } Style::new() } } impl<'a, S, N: 'a> fmt::Display for FmtCtx<'a, S, N> where S: Subscriber + for<'lookup> LookupSpan<'lookup>, N: for<'writer> FormatFields<'writer> + 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let bold = self.bold(); let mut seen = false; let span = self .span .and_then(|id| self.ctx.ctx.span(id)) .or_else(|| self.ctx.ctx.lookup_current()); let scope = span.into_iter().flat_map(|span| span.scope().from_root()); for span in scope { seen = true; write!(f, "{}:", bold.paint(span.metadata().name()))?; } if seen { f.write_char(' ')?; } Ok(()) } } #[cfg(not(feature = "ansi"))] struct Style; #[cfg(not(feature = "ansi"))] impl Style { fn new() -> Self { Style } fn bold(self) -> Self { self } fn paint(&self, d: impl fmt::Display) -> impl fmt::Display { d } fn prefix(&self) -> impl fmt::Display { "" } fn suffix(&self) -> impl fmt::Display { "" } } struct FmtThreadName<'a> { name: &'a str, } impl<'a> FmtThreadName<'a> { pub(crate) fn new(name: &'a str) -> Self { Self { name } } } impl<'a> fmt::Display for FmtThreadName<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use std::sync::atomic::{ AtomicUsize, Ordering::{AcqRel, Acquire, Relaxed}, }; // Track the longest thread name length we've seen so far in an atomic, // so that it can be updated by any thread. static MAX_LEN: AtomicUsize = AtomicUsize::new(0); let len = self.name.len(); // Snapshot the current max thread name length. let mut max_len = MAX_LEN.load(Relaxed); while len > max_len { // Try to set a new max length, if it is still the value we took a // snapshot of. match MAX_LEN.compare_exchange(max_len, len, AcqRel, Acquire) { // We successfully set the new max value Ok(_) => break, // Another thread set a new max value since we last observed // it! It's possible that the new length is actually longer than // ours, so we'll loop again and check whether our length is // still the longest. If not, we'll just use the newer value. Err(actual) => max_len = actual, } } // pad thread name using `max_len` write!(f, "{:>width$}", self.name, width = max_len) } } struct FmtLevel<'a> { level: &'a Level, #[cfg(feature = "ansi")] ansi: bool, } impl<'a> FmtLevel<'a> { #[cfg(feature = "ansi")] pub(crate) fn new(level: &'a Level, ansi: bool) -> Self { Self { level, ansi } } #[cfg(not(feature = "ansi"))] pub(crate) fn new(level: &'a Level) -> Self { Self { level } } } const TRACE_STR: &str = "TRACE"; const DEBUG_STR: &str = "DEBUG"; const INFO_STR: &str = " INFO"; const WARN_STR: &str = " WARN"; const ERROR_STR: &str = "ERROR"; #[cfg(not(feature = "ansi"))] impl<'a> fmt::Display for FmtLevel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self.level { Level::TRACE => f.pad(TRACE_STR), Level::DEBUG => f.pad(DEBUG_STR), Level::INFO => f.pad(INFO_STR), Level::WARN => f.pad(WARN_STR), Level::ERROR => f.pad(ERROR_STR), } } } #[cfg(feature = "ansi")] impl<'a> fmt::Display for FmtLevel<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.ansi { match *self.level { Level::TRACE => write!(f, "{}", Color::Purple.paint(TRACE_STR)), Level::DEBUG => write!(f, "{}", Color::Blue.paint(DEBUG_STR)), Level::INFO => write!(f, "{}", Color::Green.paint(INFO_STR)), Level::WARN => write!(f, "{}", Color::Yellow.paint(WARN_STR)), Level::ERROR => write!(f, "{}", Color::Red.paint(ERROR_STR)), } } else { match *self.level { Level::TRACE => f.pad(TRACE_STR), Level::DEBUG => f.pad(DEBUG_STR), Level::INFO => f.pad(INFO_STR), Level::WARN => f.pad(WARN_STR), Level::ERROR => f.pad(ERROR_STR), } } } } // === impl FieldFn === impl<'a, F> MakeVisitor> for FieldFn where F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result + Clone, { type Visitor = FieldFnVisitor<'a, F>; fn make_visitor(&self, writer: Writer<'a>) -> Self::Visitor { FieldFnVisitor { writer, f: self.0.clone(), result: Ok(()), } } } impl<'a, F> Visit for FieldFnVisitor<'a, F> where F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { if self.result.is_ok() { self.result = (self.f)(&mut self.writer, field, value) } } } impl<'a, F> VisitOutput for FieldFnVisitor<'a, F> where F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn finish(self) -> fmt::Result { self.result } } impl<'a, F> VisitFmt for FieldFnVisitor<'a, F> where F: Fn(&mut Writer<'a>, &Field, &dyn fmt::Debug) -> fmt::Result, { fn writer(&mut self) -> &mut dyn fmt::Write { &mut self.writer } } impl<'a, F> fmt::Debug for FieldFnVisitor<'a, F> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("FieldFnVisitor") .field("f", &format_args!("{}", std::any::type_name::())) .field("writer", &self.writer) .field("result", &self.result) .finish() } } // === printing synthetic Span events === /// Configures what points in the span lifecycle are logged as events. /// /// See also [`with_span_events`](super::SubscriberBuilder.html::with_span_events). #[derive(Clone, Eq, PartialEq, Ord, PartialOrd)] pub struct FmtSpan(u8); impl FmtSpan { /// one event when span is created pub const NEW: FmtSpan = FmtSpan(1 << 0); /// one event per enter of a span pub const ENTER: FmtSpan = FmtSpan(1 << 1); /// one event per exit of a span pub const EXIT: FmtSpan = FmtSpan(1 << 2); /// one event when the span is dropped pub const CLOSE: FmtSpan = FmtSpan(1 << 3); /// spans are ignored (this is the default) pub const NONE: FmtSpan = FmtSpan(0); /// one event per enter/exit of a span pub const ACTIVE: FmtSpan = FmtSpan(FmtSpan::ENTER.0 | FmtSpan::EXIT.0); /// events at all points (new, enter, exit, drop) pub const FULL: FmtSpan = FmtSpan(FmtSpan::NEW.0 | FmtSpan::ENTER.0 | FmtSpan::EXIT.0 | FmtSpan::CLOSE.0); /// Check whether or not a certain flag is set for this [`FmtSpan`] fn contains(&self, other: FmtSpan) -> bool { self.clone() & other.clone() == other } } macro_rules! impl_fmt_span_bit_op { ($trait:ident, $func:ident, $op:tt) => { impl std::ops::$trait for FmtSpan { type Output = FmtSpan; fn $func(self, rhs: Self) -> Self::Output { FmtSpan(self.0 $op rhs.0) } } }; } macro_rules! impl_fmt_span_bit_assign_op { ($trait:ident, $func:ident, $op:tt) => { impl std::ops::$trait for FmtSpan { fn $func(&mut self, rhs: Self) { *self = FmtSpan(self.0 $op rhs.0) } } }; } impl_fmt_span_bit_op!(BitAnd, bitand, &); impl_fmt_span_bit_op!(BitOr, bitor, |); impl_fmt_span_bit_op!(BitXor, bitxor, ^); impl_fmt_span_bit_assign_op!(BitAndAssign, bitand_assign, &); impl_fmt_span_bit_assign_op!(BitOrAssign, bitor_assign, |); impl_fmt_span_bit_assign_op!(BitXorAssign, bitxor_assign, ^); impl Debug for FmtSpan { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut wrote_flag = false; let mut write_flags = |flag, flag_str| -> fmt::Result { if self.contains(flag) { if wrote_flag { f.write_str(" | ")?; } f.write_str(flag_str)?; wrote_flag = true; } Ok(()) }; if FmtSpan::NONE | self.clone() == FmtSpan::NONE { f.write_str("FmtSpan::NONE")?; } else { write_flags(FmtSpan::NEW, "FmtSpan::NEW")?; write_flags(FmtSpan::ENTER, "FmtSpan::ENTER")?; write_flags(FmtSpan::EXIT, "FmtSpan::EXIT")?; write_flags(FmtSpan::CLOSE, "FmtSpan::CLOSE")?; } Ok(()) } } pub(super) struct FmtSpanConfig { pub(super) kind: FmtSpan, pub(super) fmt_timing: bool, } impl FmtSpanConfig { pub(super) fn without_time(self) -> Self { Self { kind: self.kind, fmt_timing: false, } } pub(super) fn with_kind(self, kind: FmtSpan) -> Self { Self { kind, fmt_timing: self.fmt_timing, } } pub(super) fn trace_new(&self) -> bool { self.kind.contains(FmtSpan::NEW) } pub(super) fn trace_enter(&self) -> bool { self.kind.contains(FmtSpan::ENTER) } pub(super) fn trace_exit(&self) -> bool { self.kind.contains(FmtSpan::EXIT) } pub(super) fn trace_close(&self) -> bool { self.kind.contains(FmtSpan::CLOSE) } } impl Debug for FmtSpanConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.kind.fmt(f) } } impl Default for FmtSpanConfig { fn default() -> Self { Self { kind: FmtSpan::NONE, fmt_timing: true, } } } pub(super) struct TimingDisplay(pub(super) u64); impl Display for TimingDisplay { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut t = self.0 as f64; for unit in ["ns", "µs", "ms", "s"].iter() { if t < 10.0 { return write!(f, "{:.2}{}", t, unit); } else if t < 100.0 { return write!(f, "{:.1}{}", t, unit); } else if t < 1000.0 { return write!(f, "{:.0}{}", t, unit); } t /= 1000.0; } write!(f, "{:.0}s", t * 1000.0) } } #[cfg(test)] pub(super) mod test { use crate::fmt::{test::MockMakeWriter, time::FormatTime}; use tracing::{ self, dispatcher::{set_default, Dispatch}, subscriber::with_default, }; use super::*; use regex::Regex; use std::{fmt, path::Path}; pub(crate) struct MockTime; impl FormatTime for MockTime { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!(w, "fake time") } } #[test] fn disable_everything() { // This test reproduces https://github.com/tokio-rs/tracing/issues/1354 let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .without_time() .with_level(false) .with_target(false) .with_thread_ids(false) .with_thread_names(false); #[cfg(feature = "ansi")] let subscriber = subscriber.with_ansi(false); assert_info_hello(subscriber, make_writer, "hello\n") } fn test_ansi( is_ansi: bool, expected: &str, builder: crate::fmt::SubscriberBuilder>, ) where Format: FormatEvent, T: Send + Sync + 'static, { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_ansi(is_ansi) .with_timer(MockTime); run_test(subscriber, make_writer, expected) } #[cfg(not(feature = "ansi"))] fn test_without_ansi( expected: &str, builder: crate::fmt::SubscriberBuilder>, ) where Format: FormatEvent, T: Send + Sync, { let make_writer = MockMakeWriter::default(); let subscriber = builder.with_writer(make_writer).with_timer(MockTime); run_test(subscriber, make_writer, expected) } fn test_without_level( expected: &str, builder: crate::fmt::SubscriberBuilder>, ) where Format: FormatEvent, T: Send + Sync + 'static, { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime); run_test(subscriber, make_writer, expected); } #[test] fn with_line_number_and_file_name() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_file(true) .with_line_number(true) .with_level(false) .with_ansi(false) .with_timer(MockTime); let expected = Regex::new(&format!( "^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. .replace('\\', "\\\\") )) .unwrap(); let _default = set_default(&subscriber.into()); tracing::info!("hello"); let res = make_writer.get_string(); assert!(expected.is_match(&res)); } #[test] fn with_line_number() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_line_number(true) .with_level(false) .with_ansi(false) .with_timer(MockTime); let expected = Regex::new("^fake time tracing_subscriber::fmt::format::test: [0-9]+: hello\n$") .unwrap(); let _default = set_default(&subscriber.into()); tracing::info!("hello"); let res = make_writer.get_string(); assert!(expected.is_match(&res)); } #[test] fn with_filename() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_file(true) .with_level(false) .with_ansi(false) .with_timer(MockTime); let expected = &format!( "fake time tracing_subscriber::fmt::format::test: {}: hello\n", current_path(), ); assert_info_hello(subscriber, make_writer, expected); } #[test] fn with_thread_ids() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_thread_ids(true) .with_ansi(false) .with_timer(MockTime); let expected = "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n"; assert_info_hello_ignore_numeric(subscriber, make_writer, expected); } #[test] fn pretty_default() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .pretty() .with_writer(make_writer.clone()) .with_ansi(false) .with_timer(MockTime); let expected = format!( r#" fake time INFO tracing_subscriber::fmt::format::test: hello at {}:NUMERIC "#, file!() ); assert_info_hello_ignore_numeric(subscriber, make_writer, &expected) } fn assert_info_hello(subscriber: impl Into, buf: MockMakeWriter, expected: &str) { let _default = set_default(&subscriber.into()); tracing::info!("hello"); let result = buf.get_string(); assert_eq!(expected, result) } // When numeric characters are used they often form a non-deterministic value as they usually represent things like a thread id or line number. // This assert method should be used when non-deterministic numeric characters are present. fn assert_info_hello_ignore_numeric( subscriber: impl Into, buf: MockMakeWriter, expected: &str, ) { let _default = set_default(&subscriber.into()); tracing::info!("hello"); let regex = Regex::new("[0-9]+").unwrap(); let result = buf.get_string(); let result_cleaned = regex.replace_all(&result, "NUMERIC"); assert_eq!(expected, result_cleaned) } fn test_overridden_parents( expected: &str, builder: crate::fmt::SubscriberBuilder>, ) where Format: FormatEvent, T: Send + Sync + 'static, { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", span = 1); let span2 = tracing::info_span!(parent: &span1, "span2", span = 2); tracing::info!(parent: &span2, "hello"); }); assert_eq!(expected, make_writer.get_string()); } fn test_overridden_parents_in_scope( expected1: &str, expected2: &str, builder: crate::fmt::SubscriberBuilder>, ) where Format: FormatEvent, T: Send + Sync + 'static, { let make_writer = MockMakeWriter::default(); let subscriber = builder .with_writer(make_writer.clone()) .with_level(false) .with_ansi(false) .with_timer(MockTime) .finish(); with_default(subscriber, || { let span1 = tracing::info_span!("span1", span = 1); let span2 = tracing::info_span!(parent: &span1, "span2", span = 2); let span3 = tracing::info_span!("span3", span = 3); let _e3 = span3.enter(); tracing::info!("hello"); assert_eq!(expected1, make_writer.get_string().as_str()); tracing::info!(parent: &span2, "hello"); assert_eq!(expected2, make_writer.get_string().as_str()); }); } fn run_test(subscriber: impl Into, buf: MockMakeWriter, expected: &str) { let _default = set_default(&subscriber.into()); tracing::info!("hello"); assert_eq!(expected, buf.get_string()) } mod default { use super::*; #[test] fn with_thread_ids() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .with_writer(make_writer.clone()) .with_thread_ids(true) .with_ansi(false) .with_timer(MockTime); let expected = "fake time INFO ThreadId(NUMERIC) tracing_subscriber::fmt::format::test: hello\n"; assert_info_hello_ignore_numeric(subscriber, make_writer, expected); } #[cfg(feature = "ansi")] #[test] fn with_ansi_true() { let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n"; test_ansi(true, expected, crate::fmt::Subscriber::builder()); } #[cfg(feature = "ansi")] #[test] fn with_ansi_false() { let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; test_ansi(false, expected, crate::fmt::Subscriber::builder()); } #[cfg(not(feature = "ansi"))] #[test] fn without_ansi() { let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; test_without_ansi(expected, crate::fmt::Subscriber::builder()) } #[test] fn without_level() { let expected = "fake time tracing_subscriber::fmt::format::test: hello\n"; test_without_level(expected, crate::fmt::Subscriber::builder()) } #[test] fn overridden_parents() { let expected = "fake time span1{span=1}:span2{span=2}: tracing_subscriber::fmt::format::test: hello\n"; test_overridden_parents(expected, crate::fmt::Subscriber::builder()) } #[test] fn overridden_parents_in_scope() { test_overridden_parents_in_scope( "fake time span3{span=3}: tracing_subscriber::fmt::format::test: hello\n", "fake time span1{span=1}:span2{span=2}: tracing_subscriber::fmt::format::test: hello\n", crate::fmt::Subscriber::builder(), ) } } mod compact { use super::*; #[cfg(feature = "ansi")] #[test] fn with_ansi_true() { let expected = "\u{1b}[2mfake time\u{1b}[0m \u{1b}[32m INFO\u{1b}[0m \u{1b}[2mtracing_subscriber::fmt::format::test\u{1b}[0m\u{1b}[2m:\u{1b}[0m hello\n"; test_ansi(true, expected, crate::fmt::Subscriber::builder().compact()) } #[cfg(feature = "ansi")] #[test] fn with_ansi_false() { let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; test_ansi(false, expected, crate::fmt::Subscriber::builder().compact()); } #[cfg(not(feature = "ansi"))] #[test] fn without_ansi() { let expected = "fake time INFO tracing_subscriber::fmt::format::test: hello\n"; test_without_ansi(expected, crate::fmt::Subscriber::builder().compact()) } #[test] fn without_level() { let expected = "fake time tracing_subscriber::fmt::format::test: hello\n"; test_without_level(expected, crate::fmt::Subscriber::builder().compact()); } #[test] fn overridden_parents() { let expected = "fake time span1:span2: tracing_subscriber::fmt::format::test: hello span=1 span=2\n"; test_overridden_parents(expected, crate::fmt::Subscriber::builder().compact()) } #[test] fn overridden_parents_in_scope() { test_overridden_parents_in_scope( "fake time span3: tracing_subscriber::fmt::format::test: hello span=3\n", "fake time span1:span2: tracing_subscriber::fmt::format::test: hello span=1 span=2\n", crate::fmt::Subscriber::builder().compact(), ) } } mod pretty { use super::*; #[test] fn pretty_default() { let make_writer = MockMakeWriter::default(); let subscriber = crate::fmt::Subscriber::builder() .pretty() .with_writer(make_writer.clone()) .with_ansi(false) .with_timer(MockTime); let expected = format!( " fake time INFO tracing_subscriber::fmt::format::test: hello\n at {}:NUMERIC\n\n", file!() ); assert_info_hello_ignore_numeric(subscriber, make_writer, &expected) } } #[test] fn format_nanos() { fn fmt(t: u64) -> String { TimingDisplay(t).to_string() } assert_eq!(fmt(1), "1.00ns"); assert_eq!(fmt(12), "12.0ns"); assert_eq!(fmt(123), "123ns"); assert_eq!(fmt(1234), "1.23µs"); assert_eq!(fmt(12345), "12.3µs"); assert_eq!(fmt(123456), "123µs"); assert_eq!(fmt(1234567), "1.23ms"); assert_eq!(fmt(12345678), "12.3ms"); assert_eq!(fmt(123456789), "123ms"); assert_eq!(fmt(1234567890), "1.23s"); assert_eq!(fmt(12345678901), "12.3s"); assert_eq!(fmt(123456789012), "123s"); assert_eq!(fmt(1234567890123), "1235s"); } #[test] fn fmt_span_combinations() { let f = FmtSpan::NONE; assert!(!f.contains(FmtSpan::NEW)); assert!(!f.contains(FmtSpan::ENTER)); assert!(!f.contains(FmtSpan::EXIT)); assert!(!f.contains(FmtSpan::CLOSE)); let f = FmtSpan::ACTIVE; assert!(!f.contains(FmtSpan::NEW)); assert!(f.contains(FmtSpan::ENTER)); assert!(f.contains(FmtSpan::EXIT)); assert!(!f.contains(FmtSpan::CLOSE)); let f = FmtSpan::FULL; assert!(f.contains(FmtSpan::NEW)); assert!(f.contains(FmtSpan::ENTER)); assert!(f.contains(FmtSpan::EXIT)); assert!(f.contains(FmtSpan::CLOSE)); let f = FmtSpan::NEW | FmtSpan::CLOSE; assert!(f.contains(FmtSpan::NEW)); assert!(!f.contains(FmtSpan::ENTER)); assert!(!f.contains(FmtSpan::EXIT)); assert!(f.contains(FmtSpan::CLOSE)); } /// Returns the test's module path. fn current_path() -> String { Path::new("tracing-subscriber") .join("src") .join("fmt") .join("format") .join("mod.rs") .to_str() .expect("path must not contain invalid unicode") .to_owned() } } tracing-subscriber-0.3.18/src/fmt/format/pretty.rs000064400000000000000000000533611046102023000202570ustar 00000000000000use super::*; use crate::{ field::{VisitFmt, VisitOutput}, fmt::fmt_layer::{FmtContext, FormattedFields}, registry::LookupSpan, }; use std::fmt; use tracing_core::{ field::{self, Field}, Event, Level, Subscriber, }; #[cfg(feature = "tracing-log")] use tracing_log::NormalizeEvent; use nu_ansi_term::{Color, Style}; /// An excessively pretty, human-readable event formatter. /// /// Unlike the [`Full`], [`Compact`], and [`Json`] formatters, this is a /// multi-line output format. Each individual event may output multiple lines of /// text. /// /// # Example Output /// ///
:; cargo run --example fmt-pretty
///     Finished dev [unoptimized + debuginfo] target(s) in 0.08s
///      Running `target/debug/examples/fmt-pretty`
///   2022-02-15T18:44:24.535324Z  INFO fmt_pretty: preparing to shave yaks, number_of_yaks: 3
///     at examples/examples/fmt-pretty.rs:16 on main
///
///   2022-02-15T18:44:24.535403Z  INFO fmt_pretty::yak_shave: shaving yaks
///     at examples/examples/fmt/yak_shave.rs:41 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535442Z TRACE fmt_pretty::yak_shave: hello! I'm gonna shave a yak, excitement: "yay!"
///     at examples/examples/fmt/yak_shave.rs:16 on main
///     in fmt_pretty::yak_shave::shave with yak: 1
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535469Z TRACE fmt_pretty::yak_shave: yak shaved successfully
///     at examples/examples/fmt/yak_shave.rs:25 on main
///     in fmt_pretty::yak_shave::shave with yak: 1
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535502Z DEBUG yak_events: yak: 1, shaved: true
///     at examples/examples/fmt/yak_shave.rs:46 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535524Z TRACE fmt_pretty::yak_shave: yaks_shaved: 1
///     at examples/examples/fmt/yak_shave.rs:55 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535551Z TRACE fmt_pretty::yak_shave: hello! I'm gonna shave a yak, excitement: "yay!"
///     at examples/examples/fmt/yak_shave.rs:16 on main
///     in fmt_pretty::yak_shave::shave with yak: 2
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535573Z TRACE fmt_pretty::yak_shave: yak shaved successfully
///     at examples/examples/fmt/yak_shave.rs:25 on main
///     in fmt_pretty::yak_shave::shave with yak: 2
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535600Z DEBUG yak_events: yak: 2, shaved: true
///     at examples/examples/fmt/yak_shave.rs:46 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535618Z TRACE fmt_pretty::yak_shave: yaks_shaved: 2
///     at examples/examples/fmt/yak_shave.rs:55 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535644Z TRACE fmt_pretty::yak_shave: hello! I'm gonna shave a yak, excitement: "yay!"
///     at examples/examples/fmt/yak_shave.rs:16 on main
///     in fmt_pretty::yak_shave::shave with yak: 3
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535670Z  WARN fmt_pretty::yak_shave: could not locate yak
///     at examples/examples/fmt/yak_shave.rs:18 on main
///     in fmt_pretty::yak_shave::shave with yak: 3
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535698Z DEBUG yak_events: yak: 3, shaved: false
///     at examples/examples/fmt/yak_shave.rs:46 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535720Z ERROR fmt_pretty::yak_shave: failed to shave yak, yak: 3, error: missing yak, error.sources: [out of space, out of cash]
///     at examples/examples/fmt/yak_shave.rs:51 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535742Z TRACE fmt_pretty::yak_shave: yaks_shaved: 2
///     at examples/examples/fmt/yak_shave.rs:55 on main
///     in fmt_pretty::yak_shave::shaving_yaks with yaks: 3
///
///   2022-02-15T18:44:24.535765Z  INFO fmt_pretty: yak shaving completed, all_yaks_shaved: false
///     at examples/examples/fmt-pretty.rs:19 on main
/// 
#[derive(Debug, Clone, Eq, PartialEq)] pub struct Pretty { display_location: bool, } /// The [visitor] produced by [`Pretty`]'s [`MakeVisitor`] implementation. /// /// [visitor]: field::Visit /// [`MakeVisitor`]: crate::field::MakeVisitor #[derive(Debug)] pub struct PrettyVisitor<'a> { writer: Writer<'a>, is_empty: bool, style: Style, result: fmt::Result, } /// An excessively pretty, human-readable [`MakeVisitor`] implementation. /// /// [`MakeVisitor`]: crate::field::MakeVisitor #[derive(Debug)] pub struct PrettyFields { /// A value to override the provided `Writer`'s ANSI formatting /// configuration. /// /// If this is `Some`, we override the `Writer`'s ANSI setting. This is /// necessary in order to continue supporting the deprecated /// `PrettyFields::with_ansi` method. If it is `None`, we don't override the /// ANSI formatting configuration (because the deprecated method was not /// called). // TODO: when `PrettyFields::with_ansi` is removed, we can get rid // of this entirely. ansi: Option, } // === impl Pretty === impl Default for Pretty { fn default() -> Self { Self { display_location: true, } } } impl Pretty { fn style_for(level: &Level) -> Style { match *level { Level::TRACE => Style::new().fg(Color::Purple), Level::DEBUG => Style::new().fg(Color::Blue), Level::INFO => Style::new().fg(Color::Green), Level::WARN => Style::new().fg(Color::Yellow), Level::ERROR => Style::new().fg(Color::Red), } } /// Sets whether the event's source code location is displayed. /// /// This defaults to `true`. #[deprecated( since = "0.3.6", note = "all formatters now support configurable source locations. Use `Format::with_source_location` instead." )] pub fn with_source_location(self, display_location: bool) -> Self { Self { display_location, ..self } } } impl FormatEvent for Format where C: Subscriber + for<'a> LookupSpan<'a>, N: for<'a> FormatFields<'a> + 'static, T: FormatTime, { fn format_event( &self, ctx: &FmtContext<'_, C, N>, mut writer: Writer<'_>, event: &Event<'_>, ) -> fmt::Result { #[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(); write!(&mut writer, " ")?; // if the `Format` struct *also* has an ANSI color configuration, // override the writer...the API for configuring ANSI color codes on the // `Format` struct is deprecated, but we still need to honor those // configurations. if let Some(ansi) = self.ansi { writer = writer.with_ansi(ansi); } self.format_timestamp(&mut writer)?; let style = if self.display_level && writer.has_ansi_escapes() { Pretty::style_for(meta.level()) } else { Style::new() }; if self.display_level { write!( writer, "{} ", super::FmtLevel::new(meta.level(), writer.has_ansi_escapes()) )?; } if self.display_target { let target_style = if writer.has_ansi_escapes() { style.bold() } else { style }; write!( writer, "{}{}{}:", target_style.prefix(), meta.target(), target_style.infix(style) )?; } let line_number = if self.display_line_number { meta.line() } else { None }; // If the file name is disabled, format the line number right after the // target. Otherwise, if we also display the file, it'll go on a // separate line. if let (Some(line_number), false, true) = ( line_number, self.display_filename, self.format.display_location, ) { write!( writer, "{}{}{}:", style.prefix(), line_number, style.infix(style) )?; } writer.write_char(' ')?; let mut v = PrettyVisitor::new(writer.by_ref(), true).with_style(style); event.record(&mut v); v.finish()?; writer.write_char('\n')?; let dimmed = if writer.has_ansi_escapes() { Style::new().dimmed().italic() } else { Style::new() }; let thread = self.display_thread_name || self.display_thread_id; if let (Some(file), true, true) = ( meta.file(), self.format.display_location, self.display_filename, ) { write!(writer, " {} {}", dimmed.paint("at"), file,)?; if let Some(line) = line_number { write!(writer, ":{}", line)?; } writer.write_char(if thread { ' ' } else { '\n' })?; } else if thread { write!(writer, " ")?; }; if thread { write!(writer, "{} ", dimmed.paint("on"))?; let thread = std::thread::current(); if self.display_thread_name { if let Some(name) = thread.name() { write!(writer, "{}", name)?; if self.display_thread_id { writer.write_char(' ')?; } } } if self.display_thread_id { write!(writer, "{:?}", thread.id())?; } writer.write_char('\n')?; } let bold = writer.bold(); let span = event .parent() .and_then(|id| ctx.span(id)) .or_else(|| ctx.lookup_current()); let scope = span.into_iter().flat_map(|span| span.scope()); for span in scope { let meta = span.metadata(); if self.display_target { write!( writer, " {} {}::{}", dimmed.paint("in"), meta.target(), bold.paint(meta.name()), )?; } else { write!( writer, " {} {}", dimmed.paint("in"), bold.paint(meta.name()), )?; } let ext = span.extensions(); let fields = &ext .get::>() .expect("Unable to find FormattedFields in extensions; this is a bug"); if !fields.is_empty() { write!(writer, " {} {}", dimmed.paint("with"), fields)?; } writer.write_char('\n')?; } writer.write_char('\n') } } impl<'writer> FormatFields<'writer> for Pretty { fn format_fields(&self, writer: Writer<'writer>, fields: R) -> fmt::Result { let mut v = PrettyVisitor::new(writer, true); fields.record(&mut v); v.finish() } fn add_fields( &self, current: &'writer mut FormattedFields, fields: &span::Record<'_>, ) -> fmt::Result { let empty = current.is_empty(); let writer = current.as_writer(); let mut v = PrettyVisitor::new(writer, empty); fields.record(&mut v); v.finish() } } // === impl PrettyFields === impl Default for PrettyFields { fn default() -> Self { Self::new() } } impl PrettyFields { /// Returns a new default [`PrettyFields`] implementation. pub fn new() -> Self { // By default, don't override the `Writer`'s ANSI colors // configuration. We'll only do this if the user calls the // deprecated `PrettyFields::with_ansi` method. Self { ansi: None } } /// Enable ANSI encoding for formatted fields. #[deprecated( since = "0.3.3", note = "Use `fmt::Subscriber::with_ansi` or `fmt::Layer::with_ansi` instead." )] pub fn with_ansi(self, ansi: bool) -> Self { Self { ansi: Some(ansi), ..self } } } impl<'a> MakeVisitor> for PrettyFields { type Visitor = PrettyVisitor<'a>; #[inline] fn make_visitor(&self, mut target: Writer<'a>) -> Self::Visitor { if let Some(ansi) = self.ansi { target = target.with_ansi(ansi); } PrettyVisitor::new(target, true) } } // === impl PrettyVisitor === impl<'a> PrettyVisitor<'a> { /// Returns a new default visitor that formats to the provided `writer`. /// /// # Arguments /// - `writer`: the writer to format to. /// - `is_empty`: whether or not any fields have been previously written to /// that writer. pub fn new(writer: Writer<'a>, is_empty: bool) -> Self { Self { writer, is_empty, style: Style::default(), result: Ok(()), } } pub(crate) fn with_style(self, style: Style) -> Self { Self { style, ..self } } fn write_padded(&mut self, value: &impl fmt::Debug) { let padding = if self.is_empty { self.is_empty = false; "" } else { ", " }; self.result = write!(self.writer, "{}{:?}", padding, value); } fn bold(&self) -> Style { if self.writer.has_ansi_escapes() { self.style.bold() } else { Style::new() } } } impl<'a> field::Visit for PrettyVisitor<'a> { fn record_str(&mut self, field: &Field, value: &str) { if self.result.is_err() { return; } if field.name() == "message" { self.record_debug(field, &format_args!("{}", value)) } else { self.record_debug(field, &value) } } fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { if let Some(source) = value.source() { let bold = self.bold(); self.record_debug( field, &format_args!( "{}, {}{}.sources{}: {}", value, bold.prefix(), field, bold.infix(self.style), ErrorSourceList(source), ), ) } else { self.record_debug(field, &format_args!("{}", value)) } } fn record_debug(&mut self, field: &Field, value: &dyn fmt::Debug) { if self.result.is_err() { return; } let bold = self.bold(); match field.name() { "message" => self.write_padded(&format_args!("{}{:?}", self.style.prefix(), value,)), // Skip fields that are actually log metadata that have already been handled #[cfg(feature = "tracing-log")] name if name.starts_with("log.") => self.result = Ok(()), name if name.starts_with("r#") => self.write_padded(&format_args!( "{}{}{}: {:?}", bold.prefix(), &name[2..], bold.infix(self.style), value )), name => self.write_padded(&format_args!( "{}{}{}: {:?}", bold.prefix(), name, bold.infix(self.style), value )), }; } } impl<'a> VisitOutput for PrettyVisitor<'a> { fn finish(mut self) -> fmt::Result { write!(&mut self.writer, "{}", self.style.suffix())?; self.result } } impl<'a> VisitFmt for PrettyVisitor<'a> { fn writer(&mut self) -> &mut dyn fmt::Write { &mut self.writer } } tracing-subscriber-0.3.18/src/fmt/mod.rs000064400000000000000000001320711046102023000162130ustar 00000000000000//! A `Subscriber` for formatting and logging `tracing` data. //! //! # Overview //! //! [`tracing`] is a framework for instrumenting Rust programs with context-aware, //! structured, event-based diagnostic information. This crate provides an //! implementation of the [`Subscriber`] trait that records `tracing`'s `Event`s //! and `Span`s by formatting them as text and logging them to stdout. //! //! # Usage //! //! First, add this to your `Cargo.toml` file: //! //! ```toml //! [dependencies] //! tracing-subscriber = "0.3" //! ``` //! //! *Compiler support: [requires `rustc` 1.63+][msrv]* //! //! [msrv]: super#supported-rust-versions //! //! Add the following to your executable to initialize the default subscriber: //! ```rust //! use tracing_subscriber; //! //! tracing_subscriber::fmt::init(); //! ``` //! //! ## Filtering Events with Environment Variables //! //! The default subscriber installed by `init` enables you to filter events //! at runtime using environment variables (using the [`EnvFilter`]). //! //! The filter syntax is a superset of the [`env_logger`] syntax. //! //! For example: //! - Setting `RUST_LOG=debug` enables all `Span`s and `Event`s //! set to the log level `DEBUG` or higher //! - Setting `RUST_LOG=my_crate=trace` enables `Span`s and `Event`s //! in `my_crate` at all log levels //! //! **Note**: This should **not** be called by libraries. Libraries should use //! [`tracing`] to publish `tracing` `Event`s. //! //! # Configuration //! //! You can configure a subscriber instead of using the defaults with //! the following functions: //! //! ### Subscriber //! //! The [`FmtSubscriber`] formats and records `tracing` events as line-oriented logs. //! You can create one by calling: //! //! ```rust //! let subscriber = tracing_subscriber::fmt() //! // ... add configuration //! .finish(); //! ``` //! //! You can find the configuration methods for [`FmtSubscriber`] in //! [`SubscriberBuilder`]. //! //! ## Formatters //! //! The output format used by the layer and subscriber in this module is //! represented by implementing the [`FormatEvent`] trait, and can be //! customized. This module provides a number of formatter implementations: //! //! * [`format::Full`]: The default formatter. This emits human-readable, //! single-line logs for each event that occurs, with the current span context //! displayed before the formatted representation of the event. See //! [here](format::Full#example-output) for sample output. //! //! * [`format::Compact`]: A variant of the default formatter, optimized for //! short line lengths. Fields from the current span context are appended to //! the fields of the formatted event. See //! [here](format::Compact#example-output) for sample output. //! //! * [`format::Pretty`]: Emits excessively pretty, multi-line logs, optimized //! for human readability. This is primarily intended to be used in local //! development and debugging, or for command-line applications, where //! automated analysis and compact storage of logs is less of a priority than //! readability and visual appeal. See [here](format::Pretty#example-output) //! for sample output. //! //! * [`format::Json`]: Outputs newline-delimited JSON logs. This is intended //! for production use with systems where structured logs are consumed as JSON //! by analysis and viewing tools. The JSON output is not optimized for human //! readability. See [here](format::Json#example-output) for sample output. //! //! ### Customizing Formatters //! //! The formatting of log lines for spans and events is controlled by two //! traits, [`FormatEvent`] and [`FormatFields`]. The [`FormatEvent`] trait //! determines the overall formatting of the log line, such as what information //! from the event's metadata and span context is included and in what order. //! The [`FormatFields`] trait determines how fields — both the event's //! fields and fields on spans — are formatted. //! //! The [`fmt::format`] module provides several types which implement these traits, //! many of which expose additional configuration options to customize their //! output. The [`format::Format`] type implements common configuration used by //! all the formatters provided in this crate, and can be used as a builder to //! set specific formatting settings. For example: //! //! ``` //! use tracing_subscriber::fmt; //! //! // Configure a custom event formatter //! let format = fmt::format() //! .with_level(false) // don't include levels in formatted output //! .with_target(false) // don't include targets //! .with_thread_ids(true) // include the thread ID of the current thread //! .with_thread_names(true) // include the name of the current thread //! .compact(); // use the `Compact` formatting style. //! //! // Create a `fmt` subscriber that uses our custom event format, and set it //! // as the default. //! tracing_subscriber::fmt() //! .event_format(format) //! .init(); //! ``` //! //! However, if a specific output format is needed, other crates can //! also implement [`FormatEvent`] and [`FormatFields`]. See those traits' //! documentation for details on how to implement them. //! //! ## Filters //! //! If you want to filter the `tracing` `Events` based on environment //! variables, you can use the [`EnvFilter`] as follows: //! //! ```rust //! use tracing_subscriber::EnvFilter; //! //! let filter = EnvFilter::from_default_env(); //! ``` //! //! As mentioned above, the [`EnvFilter`] allows `Span`s and `Event`s to //! be filtered at runtime by setting the `RUST_LOG` environment variable. //! //! You can find the other available [`filter`]s in the documentation. //! //! ### Using Your Subscriber //! //! Finally, once you have configured your `Subscriber`, you need to //! configure your executable to use it. //! //! A subscriber can be installed globally using: //! ```rust //! use tracing; //! use tracing_subscriber::FmtSubscriber; //! //! let subscriber = FmtSubscriber::new(); //! //! tracing::subscriber::set_global_default(subscriber) //! .map_err(|_err| eprintln!("Unable to set global default subscriber")); //! // Note this will only fail if you try to set the global default //! // subscriber multiple times //! ``` //! //! ### Composing Layers //! //! Composing an [`EnvFilter`] `Layer` and a [format `Layer`][super::fmt::Layer]: //! //! ```rust //! use tracing_subscriber::{fmt, EnvFilter}; //! use tracing_subscriber::prelude::*; //! //! let fmt_layer = fmt::layer() //! .with_target(false); //! let filter_layer = EnvFilter::try_from_default_env() //! .or_else(|_| EnvFilter::try_new("info")) //! .unwrap(); //! //! tracing_subscriber::registry() //! .with(filter_layer) //! .with(fmt_layer) //! .init(); //! ``` //! //! [`EnvFilter`]: super::filter::EnvFilter //! [`env_logger`]: https://docs.rs/env_logger/ //! [`filter`]: super::filter //! [`FmtSubscriber`]: Subscriber //! [`Subscriber`]: //! https://docs.rs/tracing/latest/tracing/trait.Subscriber.html //! [`tracing`]: https://crates.io/crates/tracing //! [`fmt::format`]: mod@crate::fmt::format use std::{any::TypeId, error::Error, io}; use tracing_core::{span, subscriber::Interest, Event, Metadata}; mod fmt_layer; #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod format; #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod time; #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub mod writer; pub use fmt_layer::{FmtContext, FormattedFields, Layer}; use crate::layer::Layer as _; use crate::util::SubscriberInitExt; use crate::{ filter::LevelFilter, layer, registry::{LookupSpan, Registry}, }; #[doc(inline)] pub use self::{ format::{format, FormatEvent, FormatFields}, time::time, writer::{MakeWriter, TestWriter}, }; /// A `Subscriber` that logs formatted representations of `tracing` events. /// /// This consists of an inner `Formatter` wrapped in a layer that performs filtering. #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] pub struct Subscriber< N = format::DefaultFields, E = format::Format, F = LevelFilter, W = fn() -> io::Stdout, > { inner: layer::Layered>, } /// A `Subscriber` that logs formatted representations of `tracing` events. /// This type only logs formatted events; it does not perform any filtering. #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub type Formatter< N = format::DefaultFields, E = format::Format, W = fn() -> io::Stdout, > = layer::Layered, Registry>; /// Configures and constructs `Subscriber`s. #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] #[derive(Debug)] #[must_use] pub struct SubscriberBuilder< N = format::DefaultFields, E = format::Format, F = LevelFilter, W = fn() -> io::Stdout, > { filter: F, inner: Layer, } /// Returns a new [`SubscriberBuilder`] for configuring a [formatting subscriber]. /// /// This is essentially shorthand for [`SubscriberBuilder::default()]`. /// /// # Examples /// /// Using [`init`] to set the default subscriber: /// /// ```rust /// tracing_subscriber::fmt().init(); /// ``` /// /// Configuring the output format: /// /// ```rust /// /// tracing_subscriber::fmt() /// // Configure formatting settings. /// .with_target(false) /// .with_timer(tracing_subscriber::fmt::time::uptime()) /// .with_level(true) /// // Set the subscriber as the default. /// .init(); /// ``` /// /// [`try_init`] returns an error if the default subscriber could not be set: /// /// ```rust /// use std::error::Error; /// /// fn init_subscriber() -> Result<(), Box> { /// tracing_subscriber::fmt() /// // Configure the subscriber to emit logs in JSON format. /// .json() /// // Configure the subscriber to flatten event fields in the output JSON objects. /// .flatten_event(true) /// // Set the subscriber as the default, returning an error if this fails. /// .try_init()?; /// /// Ok(()) /// } /// ``` /// /// Rather than setting the subscriber as the default, [`finish`] _returns_ the /// constructed subscriber, which may then be passed to other functions: /// /// ```rust /// let subscriber = tracing_subscriber::fmt() /// .with_max_level(tracing::Level::DEBUG) /// .compact() /// .finish(); /// /// tracing::subscriber::with_default(subscriber, || { /// // the subscriber will only be set as the default /// // inside this closure... /// }) /// ``` /// /// [formatting subscriber]: Subscriber /// [`SubscriberBuilder::default()`]: SubscriberBuilder::default /// [`init`]: SubscriberBuilder::init() /// [`try_init`]: SubscriberBuilder::try_init() /// [`finish`]: SubscriberBuilder::finish() #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub fn fmt() -> SubscriberBuilder { SubscriberBuilder::default() } /// Returns a new [formatting layer] that can be [composed] with other layers to /// construct a [`Subscriber`]. /// /// This is a shorthand for the equivalent [`Layer::default()`] function. /// /// [formatting layer]: Layer /// [composed]: crate::layer /// [`Layer::default()`]: Layer::default #[cfg_attr(docsrs, doc(cfg(all(feature = "fmt", feature = "std"))))] pub fn layer() -> Layer { Layer::default() } impl Subscriber { /// The maximum [verbosity level] that is enabled by a `Subscriber` by /// default. /// /// This can be overridden with the [`SubscriberBuilder::with_max_level`] method. /// /// [verbosity level]: tracing_core::Level /// [`SubscriberBuilder::with_max_level`]: SubscriberBuilder::with_max_level pub const DEFAULT_MAX_LEVEL: LevelFilter = LevelFilter::INFO; /// Returns a new `SubscriberBuilder` for configuring a format subscriber. pub fn builder() -> SubscriberBuilder { SubscriberBuilder::default() } /// Returns a new format subscriber with the default configuration. pub fn new() -> Self { Default::default() } } impl Default for Subscriber { fn default() -> Self { SubscriberBuilder::default().finish() } } // === impl Subscriber === impl tracing_core::Subscriber for Subscriber where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, F: layer::Layer> + 'static, W: for<'writer> MakeWriter<'writer> + 'static, layer::Layered>: tracing_core::Subscriber, fmt_layer::Layer: layer::Layer, { #[inline] fn register_callsite(&self, meta: &'static Metadata<'static>) -> Interest { self.inner.register_callsite(meta) } #[inline] fn enabled(&self, meta: &Metadata<'_>) -> bool { self.inner.enabled(meta) } #[inline] fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id { self.inner.new_span(attrs) } #[inline] fn record(&self, span: &span::Id, values: &span::Record<'_>) { self.inner.record(span, values) } #[inline] fn record_follows_from(&self, span: &span::Id, follows: &span::Id) { self.inner.record_follows_from(span, follows) } #[inline] fn event_enabled(&self, event: &Event<'_>) -> bool { self.inner.event_enabled(event) } #[inline] fn event(&self, event: &Event<'_>) { self.inner.event(event); } #[inline] fn enter(&self, id: &span::Id) { // TODO: add on_enter hook self.inner.enter(id); } #[inline] fn exit(&self, id: &span::Id) { self.inner.exit(id); } #[inline] fn current_span(&self) -> span::Current { self.inner.current_span() } #[inline] fn clone_span(&self, id: &span::Id) -> span::Id { self.inner.clone_span(id) } #[inline] fn try_close(&self, id: span::Id) -> bool { self.inner.try_close(id) } #[inline] fn max_level_hint(&self) -> Option { self.inner.max_level_hint() } unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { if id == TypeId::of::() { Some(self as *const Self as *const ()) } else { self.inner.downcast_raw(id) } } } impl<'a, N, E, F, W> LookupSpan<'a> for Subscriber where layer::Layered>: LookupSpan<'a>, { type Data = > as LookupSpan<'a>>::Data; fn span_data(&'a self, id: &span::Id) -> Option { self.inner.span_data(id) } } // ===== impl SubscriberBuilder ===== impl Default for SubscriberBuilder { fn default() -> Self { SubscriberBuilder { filter: Subscriber::DEFAULT_MAX_LEVEL, inner: Default::default(), } .log_internal_errors(true) } } impl SubscriberBuilder where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, W: for<'writer> MakeWriter<'writer> + 'static, F: layer::Layer> + Send + Sync + 'static, fmt_layer::Layer: layer::Layer + Send + Sync + 'static, { /// Finish the builder, returning a new `FmtSubscriber`. pub fn finish(self) -> Subscriber { let subscriber = self.inner.with_subscriber(Registry::default()); Subscriber { inner: self.filter.with_subscriber(subscriber), } } /// Install this Subscriber as the global default if one is /// not already set. /// /// If the `tracing-log` feature is enabled, this will also install /// the LogTracer to convert `Log` records into `tracing` `Event`s. /// /// # Errors /// Returns an Error if the initialization was unsuccessful, likely /// because a global subscriber was already installed by another /// call to `try_init`. pub fn try_init(self) -> Result<(), Box> { use crate::util::SubscriberInitExt; self.finish().try_init()?; Ok(()) } /// Install this Subscriber as the global default. /// /// If the `tracing-log` feature is enabled, this will also install /// the LogTracer to convert `Log` records into `tracing` `Event`s. /// /// # Panics /// Panics if the initialization was unsuccessful, likely because a /// global subscriber was already installed by another call to `try_init`. pub fn init(self) { self.try_init() .expect("Unable to install global subscriber") } } impl From> for tracing_core::Dispatch where N: for<'writer> FormatFields<'writer> + 'static, E: FormatEvent + 'static, W: for<'writer> MakeWriter<'writer> + 'static, F: layer::Layer> + Send + Sync + 'static, fmt_layer::Layer: layer::Layer + Send + Sync + 'static, { fn from(builder: SubscriberBuilder) -> tracing_core::Dispatch { tracing_core::Dispatch::new(builder.finish()) } } impl SubscriberBuilder, F, W> where N: for<'writer> FormatFields<'writer> + 'static, { /// Use the given [`timer`] for log message timestamps. /// /// See the [`time` module] for the provided timer implementations. /// /// Note that using the `"time`"" feature flag enables the /// additional time formatters [`UtcTime`] and [`LocalTime`], which use the /// [`time` crate] to provide more sophisticated timestamp formatting /// options. /// /// [`timer`]: time::FormatTime /// [`time` module]: mod@time /// [`UtcTime`]: time::UtcTime /// [`LocalTime`]: time::LocalTime /// [`time` crate]: https://docs.rs/time/0.3 pub fn with_timer(self, timer: T2) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.with_timer(timer), } } /// Do not emit timestamps with log messages. pub fn without_time(self) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.without_time(), } } /// Configures how synthesized events are emitted at points in the [span /// lifecycle][lifecycle]. /// /// The following options are available: /// /// - `FmtSpan::NONE`: No events will be synthesized when spans are /// created, entered, exited, or closed. Data from spans will still be /// included as the context for formatted events. This is the default. /// - `FmtSpan::NEW`: An event will be synthesized when spans are created. /// - `FmtSpan::ENTER`: An event will be synthesized when spans are entered. /// - `FmtSpan::EXIT`: An event will be synthesized when spans are exited. /// - `FmtSpan::CLOSE`: An event will be synthesized when a span closes. If /// [timestamps are enabled][time] for this formatter, the generated /// event will contain fields with the span's _busy time_ (the total /// time for which it was entered) and _idle time_ (the total time that /// the span existed but was not entered). /// - `FmtSpan::ACTIVE`: An event will be synthesized when spans are entered /// or exited. /// - `FmtSpan::FULL`: Events will be synthesized whenever a span is /// created, entered, exited, or closed. If timestamps are enabled, the /// close event will contain the span's busy and idle time, as /// described above. /// /// The options can be enabled in any combination. For instance, the following /// will synthesize events whenever spans are created and closed: /// /// ```rust /// use tracing_subscriber::fmt::format::FmtSpan; /// use tracing_subscriber::fmt; /// /// let subscriber = fmt() /// .with_span_events(FmtSpan::NEW | FmtSpan::CLOSE) /// .finish(); /// ``` /// /// Note that the generated events will only be part of the log output by /// this formatter; they will not be recorded by other `Subscriber`s or by /// `Layer`s added to this subscriber. /// /// [lifecycle]: https://docs.rs/tracing/latest/tracing/span/index.html#the-span-lifecycle /// [time]: SubscriberBuilder::without_time() pub fn with_span_events(self, kind: format::FmtSpan) -> Self { SubscriberBuilder { inner: self.inner.with_span_events(kind), ..self } } /// 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. #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn with_ansi(self, ansi: bool) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_ansi(ansi), ..self } } /// Sets whether to write errors from [`FormatEvent`] to the writer. /// Defaults to true. /// /// By default, `fmt::Layer` will write any `FormatEvent`-internal errors to /// the writer. These errors are unlikely and will only occur if there is a /// bug in the `FormatEvent` implementation or its dependencies. /// /// If writing to the writer fails, the error message is printed to stderr /// as a fallback. /// /// [`FormatEvent`]: crate::fmt::FormatEvent pub fn log_internal_errors( self, log_internal_errors: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.log_internal_errors(log_internal_errors), ..self } } /// Sets whether or not an event's target is displayed. pub fn with_target( self, display_target: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_target(display_target), ..self } } /// Sets whether or not an event's [source code file path][file] is /// displayed. /// /// [file]: tracing_core::Metadata::file pub fn with_file( self, display_filename: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_file(display_filename), ..self } } /// Sets whether or not an event's [source code line number][line] is /// displayed. /// /// [line]: tracing_core::Metadata::line pub fn with_line_number( self, display_line_number: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_line_number(display_line_number), ..self } } /// Sets whether or not an event's level is displayed. pub fn with_level( self, display_level: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_level(display_level), ..self } } /// Sets whether or not the [name] of the current thread is displayed /// when formatting events. /// /// [name]: std::thread#naming-threads pub fn with_thread_names( self, display_thread_names: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_thread_names(display_thread_names), ..self } } /// Sets whether or not the [thread ID] of the current thread is displayed /// when formatting events. /// /// [thread ID]: std::thread::ThreadId pub fn with_thread_ids( self, display_thread_ids: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { inner: self.inner.with_thread_ids(display_thread_ids), ..self } } /// Sets the subscriber being built to use a less verbose formatter. /// /// See [`format::Compact`]. pub fn compact(self) -> SubscriberBuilder, F, W> where N: for<'writer> FormatFields<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.compact(), } } /// Sets the subscriber being built to use an [excessively pretty, human-readable formatter](crate::fmt::format::Pretty). #[cfg(feature = "ansi")] #[cfg_attr(docsrs, doc(cfg(feature = "ansi")))] pub fn pretty( self, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.pretty(), } } /// Sets the subscriber being built to use a JSON formatter. /// /// See [`format::Json`] for details. #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub fn json( self, ) -> SubscriberBuilder, F, W> where N: for<'writer> FormatFields<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.json(), } } } #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl SubscriberBuilder, F, W> { /// Sets the json subscriber being built to flatten event metadata. /// /// See [`format::Json`] for details. pub fn flatten_event( self, flatten_event: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.flatten_event(flatten_event), } } /// Sets whether or not the JSON subscriber being built will include the current span /// in formatted events. /// /// See [`format::Json`] for details. pub fn with_current_span( self, display_current_span: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.with_current_span(display_current_span), } } /// Sets whether or not the JSON subscriber being built will include a list (from /// root to leaf) of all currently entered spans in formatted events. /// /// See [`format::Json`] for details. pub fn with_span_list( self, display_span_list: bool, ) -> SubscriberBuilder, F, W> { SubscriberBuilder { filter: self.filter, inner: self.inner.with_span_list(display_span_list), } } } #[cfg(feature = "env-filter")] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] impl SubscriberBuilder where Formatter: tracing_core::Subscriber + 'static, { /// Configures the subscriber being built to allow filter reloading at /// runtime. pub fn with_filter_reloading( self, ) -> SubscriberBuilder>, W> { let (filter, _) = crate::reload::Layer::new(self.filter); SubscriberBuilder { filter, inner: self.inner, } } } #[cfg(feature = "env-filter")] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] impl SubscriberBuilder>, W> where Formatter: tracing_core::Subscriber + 'static, { /// Returns a `Handle` that may be used to reload the constructed subscriber's /// filter. pub fn reload_handle(&self) -> crate::reload::Handle> { self.filter.handle() } } impl SubscriberBuilder { /// Sets the field formatter that the subscriber being built will use to record /// fields. /// /// For example: /// ```rust /// use tracing_subscriber::fmt::format; /// use tracing_subscriber::prelude::*; /// /// let formatter = /// // Construct a custom formatter for `Debug` fields /// format::debug_fn(|writer, field, value| write!(writer, "{}: {:?}", field, value)) /// // Use the `tracing_subscriber::MakeFmtExt` trait to wrap the /// // formatter so that a delimiter is added between fields. /// .delimited(", "); /// /// let subscriber = tracing_subscriber::fmt() /// .fmt_fields(formatter) /// .finish(); /// # drop(subscriber) /// ``` pub fn fmt_fields(self, fmt_fields: N2) -> SubscriberBuilder where N2: for<'writer> FormatFields<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.fmt_fields(fmt_fields), } } /// Sets the [`EnvFilter`] that the subscriber will use to determine if /// a span or event is enabled. /// /// Note that this method requires the "env-filter" feature flag to be enabled. /// /// If a filter was previously set, or a maximum level was set by the /// [`with_max_level`] method, that value is replaced by the new filter. /// /// # Examples /// /// Setting a filter based on the value of the `RUST_LOG` environment /// variable: /// ```rust /// use tracing_subscriber::{fmt, EnvFilter}; /// /// fmt() /// .with_env_filter(EnvFilter::from_default_env()) /// .init(); /// ``` /// /// Setting a filter based on a pre-set filter directive string: /// ```rust /// use tracing_subscriber::fmt; /// /// fmt() /// .with_env_filter("my_crate=info,my_crate::my_mod=debug,[my_span]=trace") /// .init(); /// ``` /// /// Adding additional directives to a filter constructed from an env var: /// ```rust /// use tracing_subscriber::{fmt, filter::{EnvFilter, LevelFilter}}; /// /// # fn filter() -> Result<(), Box> { /// let filter = EnvFilter::try_from_env("MY_CUSTOM_FILTER_ENV_VAR")? /// // Set the base level when not matched by other directives to WARN. /// .add_directive(LevelFilter::WARN.into()) /// // Set the max level for `my_crate::my_mod` to DEBUG, overriding /// // any directives parsed from the env variable. /// .add_directive("my_crate::my_mod=debug".parse()?); /// /// fmt() /// .with_env_filter(filter) /// .try_init()?; /// # Ok(())} /// ``` /// [`EnvFilter`]: super::filter::EnvFilter /// [`with_max_level`]: SubscriberBuilder::with_max_level() #[cfg(feature = "env-filter")] #[cfg_attr(docsrs, doc(cfg(feature = "env-filter")))] pub fn with_env_filter( self, filter: impl Into, ) -> SubscriberBuilder where Formatter: tracing_core::Subscriber + 'static, { let filter = filter.into(); SubscriberBuilder { filter, inner: self.inner, } } /// Sets the maximum [verbosity level] that will be enabled by the /// subscriber. /// /// If the max level has already been set, or a [`EnvFilter`] was added by /// [`with_env_filter`], this replaces that configuration with the new /// maximum level. /// /// # Examples /// /// Enable up to the `DEBUG` verbosity level: /// ```rust /// use tracing_subscriber::fmt; /// use tracing::Level; /// /// fmt() /// .with_max_level(Level::DEBUG) /// .init(); /// ``` /// This subscriber won't record any spans or events! /// ```rust /// use tracing_subscriber::{fmt, filter::LevelFilter}; /// /// let subscriber = fmt() /// .with_max_level(LevelFilter::OFF) /// .finish(); /// ``` /// [verbosity level]: tracing_core::Level /// [`EnvFilter`]: struct@crate::filter::EnvFilter /// [`with_env_filter`]: fn@Self::with_env_filter pub fn with_max_level( self, filter: impl Into, ) -> SubscriberBuilder { let filter = filter.into(); SubscriberBuilder { filter, inner: self.inner, } } /// Sets the [event formatter][`FormatEvent`] that the subscriber being built /// will use to format events that occur. /// /// The event formatter may be any type implementing the [`FormatEvent`] /// trait, which is implemented for all functions taking a [`FmtContext`], a /// [`Writer`], and an [`Event`]. /// /// # Examples /// /// Setting a type implementing [`FormatEvent`] as the formatter: /// /// ```rust /// use tracing_subscriber::fmt::format; /// /// let subscriber = tracing_subscriber::fmt() /// .event_format(format().compact()) /// .finish(); /// ``` /// /// [`Writer`]: struct@self::format::Writer pub fn event_format(self, fmt_event: E2) -> SubscriberBuilder where E2: FormatEvent + 'static, N: for<'writer> FormatFields<'writer> + 'static, W: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.event_format(fmt_event), } } /// Sets the [`MakeWriter`] that the subscriber being built will use to write events. /// /// # Examples /// /// Using `stderr` rather than `stdout`: /// /// ```rust /// use tracing_subscriber::fmt; /// use std::io; /// /// fmt() /// .with_writer(io::stderr) /// .init(); /// ``` pub fn with_writer(self, make_writer: W2) -> SubscriberBuilder where W2: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.with_writer(make_writer), } } /// Configures the subscriber to support [`libtest`'s output capturing][capturing] when used in /// unit tests. /// /// See [`TestWriter`] for additional details. /// /// # Examples /// /// Using [`TestWriter`] to let `cargo test` capture test output. Note that we do not install it /// globally as it may cause conflicts. /// /// ```rust /// use tracing_subscriber::fmt; /// use tracing::subscriber; /// /// subscriber::set_default( /// fmt() /// .with_test_writer() /// .finish() /// ); /// ``` /// /// [capturing]: /// https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output /// [`TestWriter`]: writer::TestWriter pub fn with_test_writer(self) -> SubscriberBuilder { SubscriberBuilder { filter: self.filter, inner: self.inner.with_writer(TestWriter::default()), } } /// Updates the event formatter by applying a function to the existing event formatter. /// /// This sets the event formatter that the subscriber being built will use to record fields. /// /// # Examples /// /// Updating an event formatter: /// /// ```rust /// let subscriber = tracing_subscriber::fmt() /// .map_event_format(|e| e.compact()) /// .finish(); /// ``` pub fn map_event_format(self, f: impl FnOnce(E) -> E2) -> SubscriberBuilder where E2: FormatEvent + 'static, N: for<'writer> FormatFields<'writer> + 'static, W: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.map_event_format(f), } } /// Updates the field formatter by applying a function to the existing field formatter. /// /// This sets the field formatter that the subscriber being built will use to record fields. /// /// # Examples /// /// Updating a field formatter: /// /// ```rust /// use tracing_subscriber::field::MakeExt; /// let subscriber = tracing_subscriber::fmt() /// .map_fmt_fields(|f| f.debug_alt()) /// .finish(); /// ``` pub fn map_fmt_fields(self, f: impl FnOnce(N) -> N2) -> SubscriberBuilder where N2: for<'writer> FormatFields<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.map_fmt_fields(f), } } /// Updates the [`MakeWriter`] by applying a function to the existing [`MakeWriter`]. /// /// This sets the [`MakeWriter`] that the subscriber being built will use to write events. /// /// # Examples /// /// Redirect output to stderr if level is <= WARN: /// /// ```rust /// use tracing::Level; /// use tracing_subscriber::fmt::{self, writer::MakeWriterExt}; /// /// let stderr = std::io::stderr.with_max_level(Level::WARN); /// let layer = tracing_subscriber::fmt() /// .map_writer(move |w| stderr.or_else(w)) /// .finish(); /// ``` pub fn map_writer(self, f: impl FnOnce(W) -> W2) -> SubscriberBuilder where W2: for<'writer> MakeWriter<'writer> + 'static, { SubscriberBuilder { filter: self.filter, inner: self.inner.map_writer(f), } } } /// Install a global tracing subscriber that listens for events and /// filters based on the value of the [`RUST_LOG` environment variable], /// if one is not already set. /// /// If the `tracing-log` feature is enabled, this will also install /// the [`LogTracer`] to convert `log` records into `tracing` `Event`s. /// /// This is shorthand for /// /// ```rust /// # fn doc() -> Result<(), Box> { /// tracing_subscriber::fmt().try_init() /// # } /// ``` /// /// /// # Errors /// /// Returns an Error if the initialization was unsuccessful, /// likely because a global subscriber was already installed by another /// call to `try_init`. /// /// [`LogTracer`]: /// https://docs.rs/tracing-log/0.1.0/tracing_log/struct.LogTracer.html /// [`RUST_LOG` environment variable]: crate::filter::EnvFilter::DEFAULT_ENV pub fn try_init() -> Result<(), Box> { let builder = Subscriber::builder(); #[cfg(feature = "env-filter")] let builder = builder.with_env_filter(crate::EnvFilter::from_default_env()); // If `env-filter` is disabled, remove the default max level filter from the // subscriber; it will be added to the `Targets` filter instead if no filter // is set in `RUST_LOG`. // Replacing the default `LevelFilter` with an `EnvFilter` would imply this, // but we can't replace the builder's filter with a `Targets` filter yet. #[cfg(not(feature = "env-filter"))] let builder = builder.with_max_level(LevelFilter::TRACE); let subscriber = builder.finish(); #[cfg(not(feature = "env-filter"))] let subscriber = { use crate::{filter::Targets, layer::SubscriberExt}; use std::{env, str::FromStr}; let targets = match env::var("RUST_LOG") { Ok(var) => Targets::from_str(&var) .map_err(|e| { eprintln!("Ignoring `RUST_LOG={:?}`: {}", var, e); }) .unwrap_or_default(), Err(env::VarError::NotPresent) => { Targets::new().with_default(Subscriber::DEFAULT_MAX_LEVEL) } Err(e) => { eprintln!("Ignoring `RUST_LOG`: {}", e); Targets::new().with_default(Subscriber::DEFAULT_MAX_LEVEL) } }; subscriber.with(targets) }; subscriber.try_init().map_err(Into::into) } /// Install a global tracing subscriber that listens for events and /// filters based on the value of the [`RUST_LOG` environment variable]. /// /// The configuration of the subscriber initialized by this function /// depends on what [feature flags](crate#feature-flags) are enabled. /// /// If the `tracing-log` feature is enabled, this will also install /// the LogTracer to convert `Log` records into `tracing` `Event`s. /// /// If the `env-filter` feature is enabled, this is shorthand for /// /// ```rust /// # use tracing_subscriber::EnvFilter; /// tracing_subscriber::fmt() /// .with_env_filter(EnvFilter::from_default_env()) /// .init(); /// ``` /// /// # Panics /// Panics if the initialization was unsuccessful, likely because a /// global subscriber was already installed by another call to `try_init`. /// /// [`RUST_LOG` environment variable]: crate::filter::EnvFilter::DEFAULT_ENV pub fn init() { try_init().expect("Unable to install global subscriber") } #[cfg(test)] mod test { use crate::{ filter::LevelFilter, fmt::{ format::{self, Format}, time, writer::MakeWriter, Subscriber, }, }; use std::{ io, sync::{Arc, Mutex, MutexGuard, TryLockError}, }; use tracing_core::dispatcher::Dispatch; pub(crate) struct MockWriter { buf: Arc>>, } impl MockWriter { pub(crate) fn new(buf: Arc>>) -> Self { Self { buf } } pub(crate) fn map_error(err: TryLockError) -> io::Error { match err { TryLockError::WouldBlock => io::Error::from(io::ErrorKind::WouldBlock), TryLockError::Poisoned(_) => io::Error::from(io::ErrorKind::Other), } } pub(crate) fn buf(&self) -> io::Result>> { self.buf.try_lock().map_err(Self::map_error) } } impl io::Write for MockWriter { fn write(&mut self, buf: &[u8]) -> io::Result { self.buf()?.write(buf) } fn flush(&mut self) -> io::Result<()> { self.buf()?.flush() } } #[derive(Clone, Default)] pub(crate) struct MockMakeWriter { buf: Arc>>, } impl MockMakeWriter { pub(crate) fn new(buf: Arc>>) -> Self { Self { buf } } // this is currently only used by the JSON formatter tests. if we need // it elsewhere in the future, feel free to remove the `#[cfg]` // attribute! #[cfg(feature = "json")] pub(crate) fn buf(&self) -> MutexGuard<'_, Vec> { self.buf.lock().unwrap() } pub(crate) fn get_string(&self) -> String { let mut buf = self.buf.lock().expect("lock shouldn't be poisoned"); let string = std::str::from_utf8(&buf[..]) .expect("formatter should not have produced invalid utf-8") .to_owned(); buf.clear(); string } } impl<'a> MakeWriter<'a> for MockMakeWriter { type Writer = MockWriter; fn make_writer(&'a self) -> Self::Writer { MockWriter::new(self.buf.clone()) } } #[test] fn impls() { let f = Format::default().with_timer(time::Uptime::default()); let subscriber = Subscriber::builder().event_format(f).finish(); let _dispatch = Dispatch::new(subscriber); let f = format::Format::default(); let subscriber = Subscriber::builder().event_format(f).finish(); let _dispatch = Dispatch::new(subscriber); let f = format::Format::default().compact(); let subscriber = Subscriber::builder().event_format(f).finish(); let _dispatch = Dispatch::new(subscriber); } #[test] fn subscriber_downcasts() { let subscriber = Subscriber::builder().finish(); let dispatch = Dispatch::new(subscriber); assert!(dispatch.downcast_ref::().is_some()); } #[test] fn subscriber_downcasts_to_parts() { let subscriber = Subscriber::new(); let dispatch = Dispatch::new(subscriber); assert!(dispatch.downcast_ref::().is_some()); assert!(dispatch.downcast_ref::().is_some()); assert!(dispatch.downcast_ref::().is_some()) } #[test] fn is_lookup_span() { fn assert_lookup_span crate::registry::LookupSpan<'a>>(_: T) {} let subscriber = Subscriber::new(); assert_lookup_span(subscriber) } } tracing-subscriber-0.3.18/src/fmt/time/chrono_crate.rs000064400000000000000000000131421046102023000210350ustar 00000000000000use 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()); } } tracing-subscriber-0.3.18/src/fmt/time/datetime.rs000064400000000000000000000337451046102023000201760ustar 00000000000000// musl as a whole is licensed under the following standard MIT license: // // ---------------------------------------------------------------------- // Copyright © 2005-2020 Rich Felker, et al. // // 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. // ---------------------------------------------------------------------- // // Authors/contributors include: // // A. Wilcox // Ada Worcester // Alex Dowad // Alex Suykov // Alexander Monakov // Andre McCurdy // Andrew Kelley // Anthony G. Basile // Aric Belsito // Arvid Picciani // Bartosz Brachaczek // Benjamin Peterson // Bobby Bingham // Boris Brezillon // Brent Cook // Chris Spiegel // Clément Vasseur // Daniel Micay // Daniel Sabogal // Daurnimator // David Carlier // David Edelsohn // Denys Vlasenko // Dmitry Ivanov // Dmitry V. Levin // Drew DeVault // Emil Renner Berthing // Fangrui Song // Felix Fietkau // Felix Janda // Gianluca Anzolin // Hauke Mehrtens // He X // Hiltjo Posthuma // Isaac Dunham // Jaydeep Patil // Jens Gustedt // Jeremy Huntwork // Jo-Philipp Wich // Joakim Sindholt // John Spencer // Julien Ramseier // Justin Cormack // Kaarle Ritvanen // Khem Raj // Kylie McClain // Leah Neukirchen // Luca Barbato // Luka Perkov // M Farkas-Dyck (Strake) // Mahesh Bodapati // Markus Wichmann // Masanori Ogino // Michael Clark // Michael Forney // Mikhail Kremnyov // Natanael Copa // Nicholas J. Kain // orc // Pascal Cuoq // Patrick Oppenlander // Petr Hosek // Petr Skocik // Pierre Carrier // Reini Urban // Rich Felker // Richard Pennington // Ryan Fairfax // Samuel Holland // Segev Finer // Shiz // sin // Solar Designer // Stefan Kristiansson // Stefan O'Rear // Szabolcs Nagy // Timo Teräs // Trutz Behn // Valentin Ochs // Will Dietz // William Haddon // William Pitcock // // Portions of this software are derived from third-party works licensed // under terms compatible with the above MIT license: // // The TRE regular expression implementation (src/regex/reg* and // src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed // under a 2-clause BSD license (license text in the source files). The // included version has been heavily modified by Rich Felker in 2012, in // the interests of size, simplicity, and namespace cleanliness. // // Much of the math library code (src/math/* and src/complex/*) is // Copyright © 1993,2004 Sun Microsystems or // Copyright © 2003-2011 David Schultz or // Copyright © 2003-2009 Steven G. Kargl or // Copyright © 2003-2009 Bruce D. Evans or // Copyright © 2008 Stephen L. Moshier or // Copyright © 2017-2018 Arm Limited // and labelled as such in comments in the individual source files. All // have been licensed under extremely permissive terms. // // The ARM memcpy code (src/string/arm/memcpy.S) is Copyright © 2008 // The Android Open Source Project and is licensed under a two-clause BSD // license. It was taken from Bionic libc, used on Android. // // The AArch64 memcpy and memset code (src/string/aarch64/*) are // Copyright © 1999-2019, Arm Limited. // // The implementation of DES for crypt (src/crypt/crypt_des.c) is // Copyright © 1994 David Burren. It is licensed under a BSD license. // // The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was // originally written by Solar Designer and placed into the public // domain. The code also comes with a fallback permissive license for use // in jurisdictions that may not recognize the public domain. // // The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 // Valentin Ochs and is licensed under an MIT-style license. // // The x86_64 port was written by Nicholas J. Kain and is licensed under // the standard MIT terms. // // The mips and microblaze ports were originally written by Richard // Pennington for use in the ellcc project. The original code was adapted // by Rich Felker for build system and code conventions during upstream // integration. It is licensed under the standard MIT terms. // // The mips64 port was contributed by Imagination Technologies and is // licensed under the standard MIT terms. // // The powerpc port was also originally written by Richard Pennington, // and later supplemented and integrated by John Spencer. It is licensed // under the standard MIT terms. // // All other files which have no copyright comments are original works // produced specifically for use as part of this library, written either // by Rich Felker, the main author of the library, or by one or more // contibutors listed above. Details on authorship of individual files // can be found in the git version control history of the project. The // omission of copyright and license comments in each file is in the // interest of source tree size. // // In addition, permission is hereby granted for all public header files // (include/* and arch/*/bits/*) and crt files intended to be linked into // applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit // the copyright notice and permission notice otherwise required by the // license, and to use these files without any requirement of // attribution. These files include substantial contributions from: // // Bobby Bingham // John Spencer // Nicholas J. Kain // Rich Felker // Richard Pennington // Stefan Kristiansson // Szabolcs Nagy // // all of whom have explicitly granted such permission. // // This file previously contained text expressing a belief that most of // the files covered by the above exception were sufficiently trivial not // to be subject to copyright, resulting in confusion over whether it // negated the permissions granted in the license. In the spirit of // 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 /// formatted string. /// /// Yes, this exists. Before you have a heart attack, understand that the meat of this is musl's /// [`__secs_to_tm`][1] converted to Rust via [c2rust][2] and then cleaned up by hand as part of /// the [kudu-rs project][3], [released under MIT][4]. /// /// [1] http://git.musl-libc.org/cgit/musl/tree/src/time/__secs_to_tm.c /// [2] https://c2rust.com/ /// [3] https://github.com/danburkert/kudu-rs/blob/c9660067e5f4c1a54143f169b5eeb49446f82e54/src/timestamp.rs#L5-L18 /// [4] https://github.com/tokio-rs/tracing/issues/1644#issuecomment-963888244 /// /// All existing `strftime`-like APIs I found were unable to handle the full range of timestamps representable /// by `SystemTime`, including `strftime` itself, since tm.tm_year is an int. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) struct DateTime { year: i64, month: u8, day: u8, hour: u8, minute: u8, second: u8, nanos: u32, } impl fmt::Display for DateTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if self.year > 9999 { write!(f, "+{}", self.year)?; } else if self.year < 0 { write!(f, "{:05}", self.year)?; } else { write!(f, "{:04}", self.year)?; } write!( f, "-{:02}-{:02}T{:02}:{:02}:{:02}.{:06}Z", self.month, self.day, self.hour, self.minute, self.second, self.nanos / 1_000 ) } } 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); (duration.as_secs() as i64, duration.subsec_nanos()) } Err(error) => { let duration = error.duration(); debug_assert!(duration.as_secs() <= std::i64::MAX as u64); let (secs, nanos) = (duration.as_secs() as i64, duration.subsec_nanos()); if nanos == 0 { (-secs, 0) } else { (-secs - 1, 1_000_000_000 - nanos) } } }; // 2000-03-01 (mod 400 year, immediately after feb29 const LEAPOCH: i64 = 946_684_800 + 86400 * (31 + 29); const DAYS_PER_400Y: i32 = 365 * 400 + 97; const DAYS_PER_100Y: i32 = 365 * 100 + 24; const DAYS_PER_4Y: i32 = 365 * 4 + 1; static DAYS_IN_MONTH: [i8; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29]; // Note(dcb): this bit is rearranged slightly to avoid integer overflow. let mut days: i64 = (t / 86_400) - (LEAPOCH / 86_400); let mut remsecs: i32 = (t % 86_400) as i32; if remsecs < 0i32 { remsecs += 86_400; days -= 1 } let mut qc_cycles: i32 = (days / i64::from(DAYS_PER_400Y)) as i32; let mut remdays: i32 = (days % i64::from(DAYS_PER_400Y)) as i32; if remdays < 0 { remdays += DAYS_PER_400Y; qc_cycles -= 1; } let mut c_cycles: i32 = remdays / DAYS_PER_100Y; if c_cycles == 4 { c_cycles -= 1; } remdays -= c_cycles * DAYS_PER_100Y; let mut q_cycles: i32 = remdays / DAYS_PER_4Y; if q_cycles == 25 { q_cycles -= 1; } remdays -= q_cycles * DAYS_PER_4Y; let mut remyears: i32 = remdays / 365; if remyears == 4 { remyears -= 1; } remdays -= remyears * 365; let mut years: i64 = i64::from(remyears) + 4 * i64::from(q_cycles) + 100 * i64::from(c_cycles) + 400 * i64::from(qc_cycles); let mut months: i32 = 0; while i32::from(DAYS_IN_MONTH[months as usize]) <= remdays { remdays -= i32::from(DAYS_IN_MONTH[months as usize]); months += 1 } if months >= 10 { months -= 12; years += 1; } DateTime { year: years + 2000, month: (months + 3) as u8, day: (remdays + 1) as u8, hour: (remsecs / 3600) as u8, minute: (remsecs / 60 % 60) as u8, second: (remsecs % 60) as u8, nanos, } } } #[cfg(test)] mod tests { use std::i32; use std::time::{Duration, UNIX_EPOCH}; use super::*; #[test] fn test_datetime() { let case = |expected: &str, secs: i64, micros: u32| { let timestamp = if secs >= 0 { UNIX_EPOCH + Duration::new(secs as u64, micros * 1_000) } else { (UNIX_EPOCH - Duration::new(!secs as u64 + 1, 0)) + Duration::new(0, micros * 1_000) }; assert_eq!( expected, format!("{}", DateTime::from(timestamp)), "secs: {}, micros: {}", secs, micros ) }; // Mostly generated with: // - date -jur +"%Y-%m-%dT%H:%M:%S.000000Z" // - http://unixtimestamp.50x.eu/ case("1970-01-01T00:00:00.000000Z", 0, 0); case("1970-01-01T00:00:00.000001Z", 0, 1); case("1970-01-01T00:00:00.500000Z", 0, 500_000); case("1970-01-01T00:00:01.000001Z", 1, 1); case("1970-01-01T00:01:01.000001Z", 60 + 1, 1); case("1970-01-01T01:01:01.000001Z", 60 * 60 + 60 + 1, 1); case( "1970-01-02T01:01:01.000001Z", 24 * 60 * 60 + 60 * 60 + 60 + 1, 1, ); case("1969-12-31T23:59:59.000000Z", -1, 0); case("1969-12-31T23:59:59.000001Z", -1, 1); case("1969-12-31T23:59:59.500000Z", -1, 500_000); case("1969-12-31T23:58:59.000001Z", -60 - 1, 1); case("1969-12-31T22:58:59.000001Z", -60 * 60 - 60 - 1, 1); case( "1969-12-30T22:58:59.000001Z", -24 * 60 * 60 - 60 * 60 - 60 - 1, 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("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 // on Windows compared with that of Unix which can cause the following // 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("-292277022657-01-27T08:29:53.000000Z", i64::MIN + 1, 0); } case("1900-01-01T00:00:00.000000Z", -2208988800, 0); case("1899-12-31T23:59:59.000000Z", -2208988801, 0); case("0000-01-01T00:00:00.000000Z", -62167219200, 0); case("-0001-12-31T23:59:59.000000Z", -62167219201, 0); case("1234-05-06T07:08:09.000000Z", -23215049511, 0); case("-1234-05-06T07:08:09.000000Z", -101097651111, 0); case("2345-06-07T08:09:01.000000Z", 11847456541, 0); case("-2345-06-07T08:09:01.000000Z", -136154620259, 0); } } tracing-subscriber-0.3.18/src/fmt/time/mod.rs000064400000000000000000000101741046102023000171500ustar 00000000000000//! Formatters for event timestamps. use crate::fmt::format::Writer; use std::fmt; use std::time::Instant; mod datetime; #[cfg(feature = "time")] mod time_crate; #[cfg(feature = "time")] #[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub use time_crate::UtcTime; #[cfg(feature = "local-time")] #[cfg_attr(docsrs, doc(cfg(all(unsound_local_offset, feature = "local-time"))))] pub use time_crate::LocalTime; #[cfg(feature = "time")] #[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub use time_crate::OffsetTime; /// [`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. /// /// Notable default implementations of this trait are `SystemTime` and `()`. The former prints the /// current time as reported by `std::time::SystemTime`, and the latter does not print the current /// time at all. `FormatTime` is also automatically implemented for any function pointer with the /// appropriate signature. /// /// The full list of provided implementations can be found in [`time`]. /// /// [`time`]: self pub trait FormatTime { /// Measure and write out the current time. /// /// When `format_time` is called, implementors should get the current time using their desired /// mechanism, and write it out to the given `fmt::Write`. Implementors must insert a trailing /// space themselves if they wish to separate the time from subsequent log message text. fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result; } /// Returns a new `SystemTime` timestamp provider. /// /// This can then be configured further to determine how timestamps should be /// configured. /// /// This is equivalent to calling /// ```rust /// # fn timer() -> tracing_subscriber::fmt::time::SystemTime { /// tracing_subscriber::fmt::time::SystemTime::default() /// # } /// ``` pub fn time() -> SystemTime { SystemTime } /// Returns a new `Uptime` timestamp provider. /// /// With this timer, timestamps will be formatted with the amount of time /// elapsed since the timestamp provider was constructed. /// /// This can then be configured further to determine how timestamps should be /// configured. /// /// This is equivalent to calling /// ```rust /// # fn timer() -> tracing_subscriber::fmt::time::Uptime { /// tracing_subscriber::fmt::time::Uptime::default() /// # } /// ``` pub fn uptime() -> Uptime { Uptime::default() } impl<'a, F> FormatTime for &'a F where F: FormatTime, { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { (*self).format_time(w) } } impl FormatTime for () { fn format_time(&self, _: &mut Writer<'_>) -> fmt::Result { Ok(()) } } impl FormatTime for fn(&mut Writer<'_>) -> fmt::Result { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { (*self)(w) } } /// Retrieve and print the current wall-clock time. #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct SystemTime; /// Retrieve and print the relative elapsed wall-clock time since an epoch. /// /// The `Default` implementation for `Uptime` makes the epoch the current time. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub struct Uptime { epoch: Instant, } impl Default for Uptime { fn default() -> Self { Uptime { epoch: Instant::now(), } } } impl From for Uptime { fn from(epoch: Instant) -> Self { Uptime { epoch } } } impl FormatTime for SystemTime { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { write!( w, "{}", datetime::DateTime::from(std::time::SystemTime::now()) ) } } impl FormatTime for Uptime { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { let e = self.epoch.elapsed(); write!(w, "{:4}.{:09}s", e.as_secs(), e.subsec_nanos()) } } tracing-subscriber-0.3.18/src/fmt/time/time_crate.rs000064400000000000000000000424331046102023000205100ustar 00000000000000use crate::fmt::{format::Writer, time::FormatTime, writer::WriteAdaptor}; use std::fmt; use time::{format_description::well_known, formatting::Formattable, OffsetDateTime, UtcOffset}; /// Formats the current [local time] using a [formatter] from the [`time` crate]. /// /// 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.
///
///    Alternatively, [`OffsetTime`] can log with a local offset if it is initialized early.
///
///    See the time
///    documentation for more details.
/// 
/// /// [local time]: time::OffsetDateTime::now_local /// [UTC time]: time::OffsetDateTime::now_utc /// [formatter]: time::formatting::Formattable /// [`time` crate]: time #[derive(Clone, Debug)] #[cfg_attr( docsrs, doc(cfg(all(unsound_local_offset, feature = "time", feature = "local-time"))) )] #[cfg(feature = "local-time")] pub struct LocalTime { format: F, } /// Formats the current [UTC time] using a [formatter] from the [`time` crate]. /// /// To format the current [local time] instead, use the [`LocalTime`] type. /// /// [local time]: time::OffsetDateTime::now_local /// [UTC time]: time::OffsetDateTime::now_utc /// [formatter]: time::formatting::Formattable /// [`time` crate]: time #[cfg_attr(docsrs, doc(cfg(feature = "time")))] #[derive(Clone, Debug)] pub struct UtcTime { format: F, } /// Formats the current time using a fixed offset and a [formatter] from the [`time` crate]. /// /// This is typically used as an alternative to [`LocalTime`]. `LocalTime` determines the offset /// every time it formats a message, which may be unsound or fail. With `OffsetTime`, the offset is /// determined once. This makes it possible to do so while the program is still single-threaded and /// handle any errors. However, this also means the offset cannot change while the program is /// running (the offset will not change across DST changes). /// /// [formatter]: time::formatting::Formattable /// [`time` crate]: time #[derive(Clone, Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "time")))] pub struct OffsetTime { offset: time::UtcOffset, format: F, } // === impl LocalTime === #[cfg(feature = "local-time")] impl LocalTime { /// Returns a formatter that formats the current [local time] in the /// [RFC 3339] format (a subset of the [ISO 8601] timestamp format). /// /// # Examples /// /// ``` /// use tracing_subscriber::fmt::{self, time}; /// /// let subscriber = tracing_subscriber::fmt() /// .with_timer(time::LocalTime::rfc_3339()); /// # drop(subscriber); /// ``` /// /// [local time]: time::OffsetDateTime::now_local /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339 /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601 pub fn rfc_3339() -> Self { Self::new(well_known::Rfc3339) } } #[cfg(feature = "local-time")] impl LocalTime { /// Returns a formatter that formats the current [local time] using the /// [`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]. /// /// If the format description is statically known, then the /// [`format_description!`] macro should be used. This is identical to the /// [`time::format_description::parse`] method, but runs at compile-time, /// throwing an error if the format description is invalid. If the desired format /// is not known statically (e.g., a user is providing a format string), then the /// [`time::format_description::parse`] method should be used. Note that this /// method is fallible. /// /// See the [`time` book] for details on the format description syntax. /// /// # Examples /// /// Using the [`format_description!`] macro: /// /// ``` /// use tracing_subscriber::fmt::{self, time::LocalTime}; /// use time::macros::format_description; /// /// let timer = LocalTime::new(format_description!("[hour]:[minute]:[second]")); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using [`time::format_description::parse`]: /// /// ``` /// use tracing_subscriber::fmt::{self, time::LocalTime}; /// /// let time_format = time::format_description::parse("[hour]:[minute]:[second]") /// .expect("format string should be valid!"); /// let timer = LocalTime::new(time_format); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using the [`format_description!`] macro requires enabling the `time` /// crate's "macros" feature flag. /// /// Using a [well-known format][well-known formats] (this is equivalent to /// [`LocalTime::rfc_3339`]): /// /// ``` /// use tracing_subscriber::fmt::{self, time::LocalTime}; /// /// let timer = LocalTime::new(time::format_description::well_known::Rfc3339); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// [local time]: time::OffsetDateTime::now_local() /// [`time` crate]: time /// [`Formattable`]: time::formatting::Formattable /// [well-known formats]: time::format_description::well_known /// [`format_description!`]: time::macros::format_description! /// [`time::format_description::parse`]: time::format_description::parse() /// [`time` book]: https://time-rs.github.io/book/api/format-description.html pub fn new(format: F) -> Self { Self { format } } } #[cfg(feature = "local-time")] impl FormatTime for LocalTime where F: Formattable, { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { let now = OffsetDateTime::now_local().map_err(|_| fmt::Error)?; format_datetime(now, w, &self.format) } } #[cfg(feature = "local-time")] impl Default for LocalTime where F: Formattable + Default, { fn default() -> Self { Self::new(F::default()) } } // === impl UtcTime === impl UtcTime { /// Returns a formatter that formats the current [UTC time] in the /// [RFC 3339] format, which is a subset of the [ISO 8601] timestamp format. /// /// # Examples /// /// ``` /// use tracing_subscriber::fmt::{self, time}; /// /// let subscriber = tracing_subscriber::fmt() /// .with_timer(time::UtcTime::rfc_3339()); /// # drop(subscriber); /// ``` /// /// [local time]: time::OffsetDateTime::now_utc /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339 /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601 pub fn rfc_3339() -> Self { Self::new(well_known::Rfc3339) } } impl UtcTime { /// Returns a formatter that formats the current [UTC time] using the /// [`time` crate], with the provided provided format. The format may be any /// type that implements the [`Formattable`] trait. /// /// Typically, the format will be a format description string, or one of the /// `time` crate's [well-known formats]. /// /// If the format description is statically known, then the /// [`format_description!`] macro should be used. This is identical to the /// [`time::format_description::parse`] method, but runs at compile-time, /// failing an error if the format description is invalid. If the desired format /// is not known statically (e.g., a user is providing a format string), then the /// [`time::format_description::parse`] method should be used. Note that this /// method is fallible. /// /// See the [`time` book] for details on the format description syntax. /// /// # Examples /// /// Using the [`format_description!`] macro: /// /// ``` /// use tracing_subscriber::fmt::{self, time::UtcTime}; /// use time::macros::format_description; /// /// let timer = UtcTime::new(format_description!("[hour]:[minute]:[second]")); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using the [`format_description!`] macro requires enabling the `time` /// crate's "macros" feature flag. /// /// Using [`time::format_description::parse`]: /// /// ``` /// use tracing_subscriber::fmt::{self, time::UtcTime}; /// /// let time_format = time::format_description::parse("[hour]:[minute]:[second]") /// .expect("format string should be valid!"); /// let timer = UtcTime::new(time_format); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using a [well-known format][well-known formats] (this is equivalent to /// [`UtcTime::rfc_3339`]): /// /// ``` /// use tracing_subscriber::fmt::{self, time::UtcTime}; /// /// let timer = UtcTime::new(time::format_description::well_known::Rfc3339); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// [UTC time]: time::OffsetDateTime::now_utc() /// [`time` crate]: time /// [`Formattable`]: time::formatting::Formattable /// [well-known formats]: time::format_description::well_known /// [`format_description!`]: time::macros::format_description! /// [`time::format_description::parse`]: time::format_description::parse /// [`time` book]: https://time-rs.github.io/book/api/format-description.html pub fn new(format: F) -> Self { Self { format } } } impl FormatTime for UtcTime where F: Formattable, { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { format_datetime(OffsetDateTime::now_utc(), w, &self.format) } } impl Default for UtcTime where F: Formattable + Default, { fn default() -> Self { Self::new(F::default()) } } // === impl OffsetTime === #[cfg(feature = "local-time")] impl OffsetTime { /// Returns a formatter that formats the current time using the [local time offset] in the [RFC /// 3339] format (a subset of the [ISO 8601] timestamp format). /// /// Returns an error if the local time offset cannot be determined. This typically occurs in /// multithreaded programs. To avoid this problem, initialize `OffsetTime` before forking /// threads. When using Tokio, this means initializing `OffsetTime` before the Tokio runtime. /// /// # Examples /// /// ``` /// use tracing_subscriber::fmt::{self, time}; /// /// let subscriber = tracing_subscriber::fmt() /// .with_timer(time::OffsetTime::local_rfc_3339().expect("could not get local offset!")); /// # drop(subscriber); /// ``` /// /// Using `OffsetTime` with Tokio: /// /// ``` /// use tracing_subscriber::fmt::time::OffsetTime; /// /// #[tokio::main] /// async fn run() { /// tracing::info!("runtime initialized"); /// /// // At this point the Tokio runtime is initialized, and we can use both Tokio and Tracing /// // normally. /// } /// /// fn main() { /// // Because we need to get the local offset before Tokio spawns any threads, our `main` /// // function cannot use `tokio::main`. /// tracing_subscriber::fmt() /// .with_timer(OffsetTime::local_rfc_3339().expect("could not get local time offset")) /// .init(); /// /// // Even though `run` is written as an `async fn`, because we used `tokio::main` on it /// // we can call it as a synchronous function. /// run(); /// } /// ``` /// /// [local time offset]: time::UtcOffset::current_local_offset /// [RFC 3339]: https://datatracker.ietf.org/doc/html/rfc3339 /// [ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601 pub fn local_rfc_3339() -> Result { Ok(Self::new( UtcOffset::current_local_offset()?, well_known::Rfc3339, )) } } impl OffsetTime { /// Returns a formatter that formats the current time using the [`time` crate] with the provided /// provided format and [timezone offset]. The format may be any type that implements the /// [`Formattable`] trait. /// /// /// Typically, the offset will be the [local offset], and format will be a format description /// string, or one of the `time` crate's [well-known formats]. /// /// If the format description is statically known, then the /// [`format_description!`] macro should be used. This is identical to the /// [`time::format_description::parse`] method, but runs at compile-time, /// throwing an error if the format description is invalid. If the desired format /// is not known statically (e.g., a user is providing a format string), then the /// [`time::format_description::parse`] method should be used. Note that this /// method is fallible. /// /// See the [`time` book] for details on the format description syntax. /// /// # Examples /// /// Using the [`format_description!`] macro: /// /// ``` /// use tracing_subscriber::fmt::{self, time::OffsetTime}; /// use time::macros::format_description; /// use time::UtcOffset; /// /// let offset = UtcOffset::current_local_offset().expect("should get local offset!"); /// let timer = OffsetTime::new(offset, format_description!("[hour]:[minute]:[second]")); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using [`time::format_description::parse`]: /// /// ``` /// use tracing_subscriber::fmt::{self, time::OffsetTime}; /// use time::UtcOffset; /// /// let offset = UtcOffset::current_local_offset().expect("should get local offset!"); /// let time_format = time::format_description::parse("[hour]:[minute]:[second]") /// .expect("format string should be valid!"); /// let timer = OffsetTime::new(offset, time_format); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// Using the [`format_description!`] macro requires enabling the `time` /// crate's "macros" feature flag. /// /// Using a [well-known format][well-known formats] (this is equivalent to /// [`OffsetTime::local_rfc_3339`]): /// /// ``` /// use tracing_subscriber::fmt::{self, time::OffsetTime}; /// use time::UtcOffset; /// /// let offset = UtcOffset::current_local_offset().expect("should get local offset!"); /// let timer = OffsetTime::new(offset, time::format_description::well_known::Rfc3339); /// let subscriber = tracing_subscriber::fmt() /// .with_timer(timer); /// # drop(subscriber); /// ``` /// /// [`time` crate]: time /// [timezone offset]: time::UtcOffset /// [`Formattable`]: time::formatting::Formattable /// [local offset]: time::UtcOffset::current_local_offset() /// [well-known formats]: time::format_description::well_known /// [`format_description!`]: time::macros::format_description /// [`time::format_description::parse`]: time::format_description::parse /// [`time` book]: https://time-rs.github.io/book/api/format-description.html pub fn new(offset: time::UtcOffset, format: F) -> Self { Self { offset, format } } } impl FormatTime for OffsetTime where F: time::formatting::Formattable, { fn format_time(&self, w: &mut Writer<'_>) -> fmt::Result { let now = OffsetDateTime::now_utc().to_offset(self.offset); format_datetime(now, w, &self.format) } } fn format_datetime( now: OffsetDateTime, into: &mut Writer<'_>, fmt: &impl Formattable, ) -> fmt::Result { let mut into = WriteAdaptor::new(into); now.format_into(&mut into, fmt) .map_err(|_| fmt::Error) .map(|_| ()) } tracing-subscriber-0.3.18/src/fmt/writer.rs000064400000000000000000001315371046102023000167560ustar 00000000000000//! Abstractions for creating [`io::Write`] instances. //! //! [`io::Write`]: std::io::Write use std::{ fmt, io::{self, Write}, sync::{Arc, Mutex, MutexGuard}, }; use tracing_core::Metadata; /// A type that can create [`io::Write`] instances. /// /// `MakeWriter` is used by [`fmt::Layer`] or [`fmt::Subscriber`] to print /// formatted text representations of [`Event`]s. /// /// 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`] when the type inside the mutex implements /// [`io::Write`]. /// /// # Examples /// /// The simplest usage is to pass in a named function that returns a writer. For /// example, to log all events to stderr, we could write: /// ``` /// let subscriber = tracing_subscriber::fmt() /// .with_writer(std::io::stderr) /// .finish(); /// # drop(subscriber); /// ``` /// /// Any function that returns a writer can be used: /// /// ``` /// fn make_my_great_writer() -> impl std::io::Write { /// // ... /// # std::io::stdout() /// } /// /// let subscriber = tracing_subscriber::fmt() /// .with_writer(make_my_great_writer) /// .finish(); /// # drop(subscriber); /// ``` /// /// A closure can be used to introduce arbitrary logic into how the writer is /// created. Consider the (admittedly rather silly) example of sending every 5th /// event to stderr, and all other events to stdout: /// /// ``` /// use std::io; /// use std::sync::atomic::{AtomicUsize, Ordering::Relaxed}; /// /// let n = AtomicUsize::new(0); /// let subscriber = tracing_subscriber::fmt() /// .with_writer(move || -> Box { /// if n.fetch_add(1, Relaxed) % 5 == 0 { /// Box::new(io::stderr()) /// } else { /// Box::new(io::stdout()) /// } /// }) /// .finish(); /// # drop(subscriber); /// ``` /// /// A single instance of a type implementing [`io::Write`] may be used as a /// `MakeWriter` by wrapping it in a [`Mutex`]. For example, we could /// write to a file like so: /// /// ``` /// use std::{fs::File, sync::Mutex}; /// /// # fn docs() -> Result<(), Box> { /// let log_file = File::create("my_cool_trace.log")?; /// let subscriber = tracing_subscriber::fmt() /// .with_writer(Mutex::new(log_file)) /// .finish(); /// # drop(subscriber); /// # Ok(()) /// # } /// ``` /// /// [`io::Write`]: std::io::Write /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber /// [`Event`]: tracing_core::event::Event /// [`io::stdout`]: std::io::stdout() /// [`io::stderr`]: std::io::stderr() /// [`MakeWriter::make_writer_for`]: MakeWriter::make_writer_for /// [`Metadata`]: tracing_core::Metadata /// [levels]: tracing_core::Level /// [targets]: tracing_core::Metadata::target pub trait MakeWriter<'a> { /// The concrete [`io::Write`] implementation returned by [`make_writer`]. /// /// [`io::Write`]: std::io::Write /// [`make_writer`]: MakeWriter::make_writer type Writer: io::Write; /// Returns an instance of [`Writer`]. /// /// # Implementer notes /// /// [`fmt::Layer`] or [`fmt::Subscriber`] will call this method each time an event is recorded. Ensure any state /// that must be saved across writes is not lost when the [`Writer`] instance is dropped. If /// creating a [`io::Write`] instance is expensive, be sure to cache it when implementing /// [`MakeWriter`] to improve performance. /// /// [`Writer`]: MakeWriter::Writer /// [`fmt::Layer`]: crate::fmt::Layer /// [`fmt::Subscriber`]: crate::fmt::Subscriber /// [`io::Write`]: std::io::Write fn make_writer(&'a self) -> Self::Writer; /// Returns a [`Writer`] for writing data from the span or event described /// by the provided [`Metadata`]. /// /// By default, this calls [`self.make_writer()`][make_writer], ignoring /// the provided metadata, but implementations can override this to provide /// metadata-specific behaviors. /// /// This method allows `MakeWriter` implementations to implement different /// behaviors based on the span or event being written. The `MakeWriter` /// type might return different writers based on the provided metadata, or /// might write some values to the writer before or after providing it to /// the caller. /// /// For example, we might want to write data from spans and events at the /// [`ERROR`] and [`WARN`] levels to `stderr`, and data from spans or events /// at lower levels to stdout: /// /// ``` /// use std::io::{self, Stdout, Stderr, StdoutLock, StderrLock}; /// use tracing_subscriber::fmt::writer::MakeWriter; /// use tracing_core::{Metadata, Level}; /// /// pub struct MyMakeWriter { /// stdout: Stdout, /// stderr: Stderr, /// } /// /// /// A lock on either stdout or stderr, depending on the verbosity level /// /// of the event being written. /// pub enum StdioLock<'a> { /// Stdout(StdoutLock<'a>), /// Stderr(StderrLock<'a>), /// } /// /// impl<'a> io::Write for StdioLock<'a> { /// fn write(&mut self, buf: &[u8]) -> io::Result { /// match self { /// StdioLock::Stdout(lock) => lock.write(buf), /// StdioLock::Stderr(lock) => lock.write(buf), /// } /// } /// /// fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { /// // ... /// # match self { /// # StdioLock::Stdout(lock) => lock.write_all(buf), /// # StdioLock::Stderr(lock) => lock.write_all(buf), /// # } /// } /// /// fn flush(&mut self) -> io::Result<()> { /// // ... /// # match self { /// # StdioLock::Stdout(lock) => lock.flush(), /// # StdioLock::Stderr(lock) => lock.flush(), /// # } /// } /// } /// /// impl<'a> MakeWriter<'a> for MyMakeWriter { /// type Writer = StdioLock<'a>; /// /// fn make_writer(&'a self) -> Self::Writer { /// // We must have an implementation of `make_writer` that makes /// // a "default" writer without any configuring metadata. Let's /// // just return stdout in that case. /// StdioLock::Stdout(self.stdout.lock()) /// } /// /// fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { /// // Here's where we can implement our special behavior. We'll /// // check if the metadata's verbosity level is WARN or ERROR, /// // and return stderr in that case. /// if meta.level() <= &Level::WARN { /// return StdioLock::Stderr(self.stderr.lock()); /// } /// /// // Otherwise, we'll return stdout. /// StdioLock::Stdout(self.stdout.lock()) /// } /// } /// ``` /// /// [`Writer`]: MakeWriter::Writer /// [`Metadata`]: tracing_core::Metadata /// [make_writer]: MakeWriter::make_writer /// [`WARN`]: tracing_core::Level::WARN /// [`ERROR`]: tracing_core::Level::ERROR fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { let _ = meta; self.make_writer() } } /// Extension trait adding combinators for working with types implementing /// [`MakeWriter`]. /// /// This is not intended to be implemented directly for user-defined /// [`MakeWriter`]s; instead, it should be imported when the desired methods are /// used. pub trait MakeWriterExt<'a>: MakeWriter<'a> { /// Wraps `self` and returns a [`MakeWriter`] that will only write output /// for events at or below the provided verbosity [`Level`]. For instance, /// `Level::TRACE` is considered to be _more verbose` than `Level::INFO`. /// /// Events whose level is more verbose than `level` will be ignored, and no /// output will be written. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stderr` only if the span or /// // event's level is >= WARN (WARN and ERROR). /// let mk_writer = std::io::stderr.with_max_level(Level::WARN); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// Writing the `ERROR` and `WARN` levels to `stderr`, and everything else /// to `stdout`: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// let mk_writer = std::io::stderr /// .with_max_level(Level::WARN) /// .or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// Writing the `ERROR` level to `stderr`, the `INFO` and `WARN` levels to /// `stdout`, and the `INFO` and DEBUG` levels to a file: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let debug_log = Arc::new(File::create("debug.log")?); /// /// let mk_writer = std::io::stderr /// .with_max_level(Level::ERROR) /// .or_else(std::io::stdout /// .with_max_level(Level::INFO) /// .and(debug_log.with_max_level(Level::DEBUG)) /// ); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) } /// ``` /// /// [`Level`]: tracing_core::Level /// [`io::Write`]: std::io::Write fn with_max_level(self, level: tracing_core::Level) -> WithMaxLevel where Self: Sized, { WithMaxLevel::new(self, level) } /// Wraps `self` and returns a [`MakeWriter`] that will only write output /// for events at or above the provided verbosity [`Level`]. /// /// Events whose level is less verbose than `level` will be ignored, and no /// output will be written. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stdout` only if the span or /// // event's level is <= DEBUG (DEBUG and TRACE). /// let mk_writer = std::io::stdout.with_min_level(Level::DEBUG); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// This can be combined with [`MakeWriterExt::with_max_level`] to write /// only within a range of levels: /// /// ``` /// # use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// // Only write the `DEBUG` and `INFO` levels to stdout. /// let mk_writer = std::io::stdout /// .with_max_level(Level::DEBUG) /// .with_min_level(Level::INFO) /// // Write the `WARN` and `ERROR` levels to stderr. /// .and(std::io::stderr.with_min_level(Level::WARN)); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// [`Level`]: tracing_core::Level /// [`io::Write`]: std::io::Write fn with_min_level(self, level: tracing_core::Level) -> WithMinLevel where Self: Sized, { WithMinLevel::new(self, level) } /// 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`] 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 /// [`make_writer_for`][mwf] will return [`OptionalWriter::none`][own]. /// Otherwise, it calls the wrapped [`MakeWriter`]'s /// [`make_writer_for`][mwf] method, and returns the produced writer. /// /// This can be used to filter an output based on arbitrary [`Metadata`] /// parameters. /// /// # Examples /// /// Writing events with a specific target to an HTTP access log, and other /// events to stdout: /// /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let access_log = Arc::new(File::create("access.log")?); /// /// let mk_writer = access_log /// // Only write events with the target "http::access_log" to the /// // access log file. /// .with_filter(|meta| meta.target() == "http::access_log") /// // Write events with all other targets to stdout. /// .or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) /// # } /// ``` /// /// Conditionally enabling or disabling a log file: /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{ /// sync::{Arc, atomic::{AtomicBool, Ordering}}, /// fs::File, /// }; /// /// static DEBUG_LOG_ENABLED: AtomicBool = AtomicBool::new(false); /// /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// // Create the debug log file /// let debug_file = Arc::new(File::create("debug.log")?) /// // Enable the debug log only if the flag is enabled. /// .with_filter(|_| DEBUG_LOG_ENABLED.load(Ordering::Acquire)); /// /// // Always write to stdout /// let mk_writer = std::io::stdout /// // Write to the debug file if it's enabled /// .and(debug_file); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// /// // ... /// /// // Later, we can toggle on or off the debug log file. /// DEBUG_LOG_ENABLED.store(true, Ordering::Release); /// # Ok(()) /// # } /// ``` /// /// [`Metadata`]: tracing_core::Metadata /// [mwf]: MakeWriter::make_writer_for /// [own]: EitherWriter::none fn with_filter(self, filter: F) -> WithFilter where Self: Sized, F: Fn(&Metadata<'_>) -> bool, { WithFilter::new(self, filter) } /// Combines `self` with another type implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that produces [writers] that write to *both* /// outputs. /// /// If writing to either writer returns an error, the returned writer will /// return that error. However, both writers will still be written to before /// the error is returned, so it is possible for one writer to fail while /// the other is written to successfully. /// /// # Examples /// /// ``` /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Construct a writer that outputs events to `stdout` *and* `stderr`. /// let mk_writer = std::io::stdout.and(std::io::stderr); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// `and` can be used in conjunction with filtering combinators. For /// example, if we want to write to a number of outputs depending on the /// level of an event, we could write: /// /// ``` /// use tracing::Level; /// # use tracing_subscriber::fmt::writer::MakeWriterExt; /// use std::{sync::Arc, fs::File}; /// # // don't actually create the file when running the tests. /// # fn docs() -> std::io::Result<()> { /// let debug_log = Arc::new(File::create("debug.log")?); /// /// // Write everything to the debug log. /// let mk_writer = debug_log /// // Write the `ERROR` and `WARN` levels to stderr. /// .and(std::io::stderr.with_max_level(Level::WARN)) /// // Write `INFO` to `stdout`. /// .and(std::io::stdout /// .with_max_level(Level::INFO) /// .with_min_level(Level::INFO) /// ); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// # Ok(()) } /// ``` /// /// [writers]: std::io::Write fn and(self, other: B) -> Tee where Self: Sized, B: MakeWriter<'a> + Sized, { Tee::new(self, other) } /// Combines `self` with another type implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that calls `other`'s [`make_writer`] if `self`'s /// `make_writer` returns [`OptionalWriter::none`][own]. /// /// # Examples /// /// ``` /// use tracing::Level; /// use tracing_subscriber::fmt::writer::MakeWriterExt; /// /// // Produces a writer that writes to `stderr` if the level is >= WARN, /// // or returns `OptionalWriter::none()` otherwise. /// let stderr = std::io::stderr.with_max_level(Level::WARN); /// /// // If the `stderr` `MakeWriter` is disabled by the max level filter, /// // write to stdout instead: /// let mk_writer = stderr.or_else(std::io::stdout); /// /// tracing_subscriber::fmt().with_writer(mk_writer).init(); /// ``` /// /// [`make_writer`]: MakeWriter::make_writer /// [own]: EitherWriter::none fn or_else(self, other: B) -> OrElse where Self: MakeWriter<'a, Writer = OptionalWriter> + Sized, B: MakeWriter<'a> + Sized, W: Write, { OrElse::new(self, other) } } /// A writer intended to support [`libtest`'s output capturing][capturing] for use in unit tests. /// /// `TestWriter` is used by [`fmt::Subscriber`] or [`fmt::Layer`] to enable capturing support. /// /// `cargo test` can only capture output from the standard library's [`print!`] macro. See /// [`libtest`'s output capturing][capturing] for more details about output capturing. /// /// Writing to [`io::stdout`] and [`io::stderr`] produces the same results as using /// [`libtest`'s `--nocapture` option][nocapture] which may make the results look unreadable. /// /// [`fmt::Subscriber`]: super::Subscriber /// [`fmt::Layer`]: super::Layer /// [capturing]: https://doc.rust-lang.org/book/ch11-02-running-tests.html#showing-function-output /// [nocapture]: https://doc.rust-lang.org/cargo/commands/cargo-test.html /// [`io::stdout`]: std::io::stdout /// [`io::stderr`]: std::io::stderr /// [`print!`]: std::print! #[derive(Default, Debug)] pub struct TestWriter { _p: (), } /// A writer that erases the specific [`io::Write`] and [`MakeWriter`] types being used. /// /// This is useful in cases where the concrete type of the writer cannot be known /// until runtime. /// /// # Examples /// /// A function that returns a [`Subscriber`] that will write to either stdout or stderr: /// /// ```rust /// # use tracing::Subscriber; /// # use tracing_subscriber::fmt::writer::BoxMakeWriter; /// /// fn dynamic_writer(use_stderr: bool) -> impl Subscriber { /// let writer = if use_stderr { /// BoxMakeWriter::new(std::io::stderr) /// } else { /// BoxMakeWriter::new(std::io::stdout) /// }; /// /// tracing_subscriber::fmt().with_writer(writer).finish() /// } /// ``` /// /// [`Subscriber`]: tracing::Subscriber /// [`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`]. /// /// This may be used by [`MakeWriter`] implementations that may conditionally /// return one of two writers. /// /// [writer]: std::io::Write #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum EitherWriter { /// A writer of type `A`. A(A), /// A writer of type `B`. B(B), } /// A [writer] which may or may not be enabled. /// /// This may be used by [`MakeWriter`] implementations that wish to /// conditionally enable or disable the returned writer based on a span or /// event's [`Metadata`]. /// /// [writer]: std::io::Write pub type OptionalWriter = EitherWriter; /// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans /// and events with metadata at or below a specified verbosity [`Level`]. /// /// This is returned by the [`MakeWriterExt::with_max_level`] method. See the /// method documentation for details. /// /// [writer]: std::io::Write /// [`Level`]: tracing_core::Level #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithMaxLevel { make: M, level: tracing_core::Level, } /// A [`MakeWriter`] combinator that only returns an enabled [writer] for spans /// and events with metadata at or above a specified verbosity [`Level`]. /// /// This is returned by the [`MakeWriterExt::with_min_level`] method. See the /// method documentation for details. /// /// [writer]: std::io::Write /// [`Level`]: tracing_core::Level #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithMinLevel { make: M, level: tracing_core::Level, } /// A [`MakeWriter`] combinator that wraps a [`MakeWriter`] with a predicate for /// span and event [`Metadata`], so that the [`MakeWriter::make_writer_for`] /// method returns [`OptionalWriter::some`][ows] when the predicate returns `true`, /// and [`OptionalWriter::none`][own] when the predicate returns `false`. /// /// This is returned by the [`MakeWriterExt::with_filter`] method. See the /// method documentation for details. /// /// [`Metadata`]: tracing_core::Metadata /// [ows]: EitherWriter::some /// [own]: EitherWriter::none #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct WithFilter { make: M, filter: F, } /// Combines a [`MakeWriter`] that returns an [`OptionalWriter`] with another /// [`MakeWriter`], so that the second [`MakeWriter`] is used when the first /// [`MakeWriter`] returns [`OptionalWriter::none`][own]. /// /// This is returned by the [`MakeWriterExt::or_else] method. See the /// method documentation for details. /// /// [own]: EitherWriter::none #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct OrElse { inner: A, or_else: B, } /// Combines two types implementing [`MakeWriter`] (or [`std::io::Write`]) to /// produce a writer that writes to both [`MakeWriter`]'s returned writers. /// /// This is returned by the [`MakeWriterExt::and`] method. See the method /// documentation for details. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Tee { a: A, b: B, } /// A type implementing [`io::Write`] for a [`MutexGuard`] where the type /// inside the [`Mutex`] implements [`io::Write`]. /// /// This is used by the [`MakeWriter`] implementation for [`Mutex`], because /// [`MutexGuard`] itself will not implement [`io::Write`] — instead, it /// _dereferences_ to a type implementing [`io::Write`]. Because [`MakeWriter`] /// requires the `Writer` type to implement [`io::Write`], it's necessary to add /// a newtype that forwards the trait implementation. /// /// [`io::Write`]: std::io::Write /// [`MutexGuard`]: std::sync::MutexGuard /// [`Mutex`]: std::sync::Mutex #[derive(Debug)] pub struct MutexGuardWriter<'a, W>(MutexGuard<'a, W>); /// Implements [`std::io::Write`] for an [`Arc`] where `&W: Write`. /// /// This is an implementation detail of the [`MakeWriter`] impl for [`Arc`]. #[derive(Clone, Debug)] pub struct ArcWriter(Arc); /// A bridge between `fmt::Write` and `io::Write`. /// /// This is used by the timestamp formatting implementation for the `time` /// crate and by the JSON formatter. In both cases, this is needed because /// `tracing-subscriber`'s `FormatEvent`/`FormatTime` traits expect a /// `fmt::Write` implementation, while `serde_json::Serializer` and `time`'s /// `format_into` methods expect an `io::Write`. #[cfg(any(feature = "json", feature = "time"))] pub(in crate::fmt) struct WriteAdaptor<'a> { fmt_write: &'a mut dyn fmt::Write, } impl<'a, F, W> MakeWriter<'a> for F where F: Fn() -> W, W: io::Write, { type Writer = W; fn make_writer(&'a self) -> Self::Writer { (self)() } } impl<'a, W> MakeWriter<'a> for Arc where &'a W: io::Write + 'a, { type Writer = &'a W; fn make_writer(&'a self) -> Self::Writer { self } } impl<'a> MakeWriter<'a> for std::fs::File { type Writer = &'a std::fs::File; fn make_writer(&'a self) -> Self::Writer { self } } // === impl TestWriter === impl TestWriter { /// Returns a new `TestWriter` with the default configuration. pub fn new() -> Self { Self::default() } } impl io::Write for TestWriter { fn write(&mut self, buf: &[u8]) -> io::Result { let out_str = String::from_utf8_lossy(buf); print!("{}", out_str); Ok(buf.len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } impl<'a> MakeWriter<'a> for TestWriter { type Writer = Self; fn make_writer(&'a self) -> Self::Writer { Self::default() } } // === impl BoxMakeWriter === impl BoxMakeWriter { /// Constructs a `BoxMakeWriter` wrapping a type implementing [`MakeWriter`]. /// pub fn new(make_writer: M) -> Self where M: for<'a> MakeWriter<'a> + Send + Sync + 'static, { Self { inner: Box::new(Boxed(make_writer)), name: std::any::type_name::(), } } } impl fmt::Debug for BoxMakeWriter { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("BoxMakeWriter") .field(&format_args!("<{}>", self.name)) .finish() } } impl<'a> MakeWriter<'a> for BoxMakeWriter { type Writer = Box; #[inline] fn make_writer(&'a self) -> Self::Writer { self.inner.make_writer() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { self.inner.make_writer_for(meta) } } struct Boxed(M); impl<'a, M> MakeWriter<'a> for Boxed where M: MakeWriter<'a>, { type Writer = Box; fn make_writer(&'a self) -> Self::Writer { let w = self.0.make_writer(); Box::new(w) } fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { let w = self.0.make_writer_for(meta); Box::new(w) } } // === impl Mutex/MutexGuardWriter === impl<'a, W> MakeWriter<'a> for Mutex where W: io::Write + 'a, { type Writer = MutexGuardWriter<'a, W>; fn make_writer(&'a self) -> Self::Writer { MutexGuardWriter(self.lock().expect("lock poisoned")) } } impl<'a, W> io::Write for MutexGuardWriter<'a, W> where W: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { self.0.write(buf) } #[inline] fn flush(&mut self) -> io::Result<()> { self.0.flush() } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { self.0.write_vectored(bufs) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { self.0.write_all(buf) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { self.0.write_fmt(fmt) } } // === impl EitherWriter === impl io::Write for EitherWriter where A: io::Write, B: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { match self { EitherWriter::A(a) => a.write(buf), EitherWriter::B(b) => b.write(buf), } } #[inline] fn flush(&mut self) -> io::Result<()> { match self { EitherWriter::A(a) => a.flush(), EitherWriter::B(b) => b.flush(), } } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { match self { EitherWriter::A(a) => a.write_vectored(bufs), EitherWriter::B(b) => b.write_vectored(bufs), } } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { match self { EitherWriter::A(a) => a.write_all(buf), EitherWriter::B(b) => b.write_all(buf), } } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { match self { EitherWriter::A(a) => a.write_fmt(fmt), EitherWriter::B(b) => b.write_fmt(fmt), } } } impl OptionalWriter { /// Returns a [disabled writer]. /// /// Any bytes written to the returned writer are discarded. /// /// This is equivalent to returning [`Option::None`]. /// /// [disabled writer]: std::io::sink #[inline] pub fn none() -> Self { EitherWriter::B(std::io::sink()) } /// Returns an enabled writer of type `T`. /// /// This is equivalent to returning [`Option::Some`]. #[inline] pub fn some(t: T) -> Self { EitherWriter::A(t) } } impl From> for OptionalWriter { #[inline] fn from(opt: Option) -> Self { match opt { Some(writer) => Self::some(writer), None => Self::none(), } } } // === impl WithMaxLevel === impl WithMaxLevel { /// Wraps the provided [`MakeWriter`] with a maximum [`Level`], so that it /// returns [`OptionalWriter::none`] for spans and events whose level is /// more verbose than the maximum level. /// /// See [`MakeWriterExt::with_max_level`] for details. /// /// [`Level`]: tracing_core::Level pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMaxLevel { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() <= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } OptionalWriter::none() } } // === impl WithMinLevel === impl WithMinLevel { /// Wraps the provided [`MakeWriter`] with a minimum [`Level`], so that it /// returns [`OptionalWriter::none`] for spans and events whose level is /// less verbose than the maximum level. /// /// See [`MakeWriterExt::with_min_level`] for details. /// /// [`Level`]: tracing_core::Level pub fn new(make: M, level: tracing_core::Level) -> Self { Self { make, level } } } impl<'a, M: MakeWriter<'a>> MakeWriter<'a> for WithMinLevel { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { // If we don't know the level, assume it's disabled. OptionalWriter::none() } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if meta.level() >= &self.level { return OptionalWriter::some(self.make.make_writer_for(meta)); } OptionalWriter::none() } } // ==== impl WithFilter === impl WithFilter { /// Wraps `make` with the provided `filter`, returning a [`MakeWriter`] that /// will call `make.make_writer_for()` when `filter` returns `true` for a /// span or event's [`Metadata`], and returns a [`sink`] otherwise. /// /// See [`MakeWriterExt::with_filter`] for details. /// /// [`Metadata`]: tracing_core::Metadata /// [`sink`]: std::io::sink pub fn new(make: M, filter: F) -> Self where F: Fn(&Metadata<'_>) -> bool, { Self { make, filter } } } impl<'a, M, F> MakeWriter<'a> for WithFilter where M: MakeWriter<'a>, F: Fn(&Metadata<'_>) -> bool, { type Writer = OptionalWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { OptionalWriter::some(self.make.make_writer()) } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { if (self.filter)(meta) { OptionalWriter::some(self.make.make_writer_for(meta)) } else { OptionalWriter::none() } } } // === impl Tee === impl Tee { /// Combines two types implementing [`MakeWriter`], returning /// a new [`MakeWriter`] that produces [writers] that write to *both* /// outputs. /// /// See the documentation for [`MakeWriterExt::and`] for details. /// /// [writers]: std::io::Write pub fn new(a: A, b: B) -> Self { Self { a, b } } } impl<'a, A, B> MakeWriter<'a> for Tee where A: MakeWriter<'a>, B: MakeWriter<'a>, { type Writer = Tee; #[inline] fn make_writer(&'a self) -> Self::Writer { Tee::new(self.a.make_writer(), self.b.make_writer()) } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { Tee::new(self.a.make_writer_for(meta), self.b.make_writer_for(meta)) } } macro_rules! impl_tee { ($self_:ident.$f:ident($($arg:ident),*)) => { { let res_a = $self_.a.$f($($arg),*); let res_b = $self_.b.$f($($arg),*); (res_a?, res_b?) } } } impl io::Write for Tee where A: io::Write, B: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { let (a, b) = impl_tee!(self.write(buf)); Ok(std::cmp::max(a, b)) } #[inline] fn flush(&mut self) -> io::Result<()> { impl_tee!(self.flush()); Ok(()) } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { let (a, b) = impl_tee!(self.write_vectored(bufs)); Ok(std::cmp::max(a, b)) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { impl_tee!(self.write_all(buf)); Ok(()) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { impl_tee!(self.write_fmt(fmt)); Ok(()) } } // === impl OrElse === impl OrElse { /// Combines pub fn new<'a, W>(inner: A, or_else: B) -> Self where A: MakeWriter<'a, Writer = OptionalWriter>, B: MakeWriter<'a>, W: Write, { Self { inner, or_else } } } impl<'a, A, B, W> MakeWriter<'a> for OrElse where A: MakeWriter<'a, Writer = OptionalWriter>, B: MakeWriter<'a>, W: io::Write, { type Writer = EitherWriter; #[inline] fn make_writer(&'a self) -> Self::Writer { match self.inner.make_writer() { EitherWriter::A(writer) => EitherWriter::A(writer), EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer()), } } #[inline] fn make_writer_for(&'a self, meta: &Metadata<'_>) -> Self::Writer { match self.inner.make_writer_for(meta) { EitherWriter::A(writer) => EitherWriter::A(writer), EitherWriter::B(_) => EitherWriter::B(self.or_else.make_writer_for(meta)), } } } // === impl ArcWriter === impl io::Write for ArcWriter where for<'a> &'a W: io::Write, { #[inline] fn write(&mut self, buf: &[u8]) -> io::Result { (&*self.0).write(buf) } #[inline] fn flush(&mut self) -> io::Result<()> { (&*self.0).flush() } #[inline] fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { (&*self.0).write_vectored(bufs) } #[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { (&*self.0).write_all(buf) } #[inline] fn write_fmt(&mut self, fmt: std::fmt::Arguments<'_>) -> io::Result<()> { (&*self.0).write_fmt(fmt) } } // === impl WriteAdaptor === #[cfg(any(feature = "json", feature = "time"))] impl<'a> WriteAdaptor<'a> { pub(in crate::fmt) fn new(fmt_write: &'a mut dyn fmt::Write) -> Self { Self { fmt_write } } } #[cfg(any(feature = "json", feature = "time"))] impl<'a> io::Write for WriteAdaptor<'a> { fn write(&mut self, buf: &[u8]) -> io::Result { let s = std::str::from_utf8(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; self.fmt_write .write_str(s) .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; Ok(s.as_bytes().len()) } fn flush(&mut self) -> io::Result<()> { Ok(()) } } #[cfg(any(feature = "json", feature = "time"))] impl<'a> fmt::Debug for WriteAdaptor<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("WriteAdaptor { .. }") } } // === blanket impls === impl<'a, M> MakeWriterExt<'a> for M where M: MakeWriter<'a> {} #[cfg(test)] mod test { use super::*; use crate::fmt::format::Format; use crate::fmt::test::{MockMakeWriter, MockWriter}; use crate::fmt::Subscriber; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use tracing::{debug, error, info, trace, warn, Level}; use tracing_core::dispatcher::{self, Dispatch}; fn test_writer(make_writer: T, msg: &str, buf: &Mutex>) where T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static, { let subscriber = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .finish() }; let dispatch = Dispatch::from(subscriber); dispatcher::with_default(&dispatch, || { error!("{}", msg); }); let expected = format!("ERROR {}: {}\n", module_path!(), msg); let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); assert!(actual.contains(expected.as_str())); } fn has_lines(buf: &Mutex>, msgs: &[(tracing::Level, &str)]) { let actual = String::from_utf8(buf.try_lock().unwrap().to_vec()).unwrap(); let mut expected_lines = msgs.iter(); for line in actual.lines() { let line = dbg!(line).trim(); let (level, msg) = expected_lines .next() .unwrap_or_else(|| panic!("expected no more lines, but got: {:?}", line)); let expected = format!("{} {}: {}", level, module_path!(), msg); assert_eq!(line, expected.as_str()); } } #[test] fn custom_writer_closure() { let buf = Arc::new(Mutex::new(Vec::new())); let buf2 = buf.clone(); let make_writer = move || MockWriter::new(buf2.clone()); let msg = "my custom writer closure error"; test_writer(make_writer, msg, &buf); } #[test] fn custom_writer_struct() { let buf = Arc::new(Mutex::new(Vec::new())); let make_writer = MockMakeWriter::new(buf.clone()); let msg = "my custom writer struct error"; test_writer(make_writer, msg, &buf); } #[test] fn custom_writer_mutex() { let buf = Arc::new(Mutex::new(Vec::new())); let writer = MockWriter::new(buf.clone()); let make_writer = Mutex::new(writer); let msg = "my mutex writer error"; test_writer(make_writer, msg, &buf); } #[test] fn combinators_level_filters() { let info_buf = Arc::new(Mutex::new(Vec::new())); let info = MockMakeWriter::new(info_buf.clone()); let debug_buf = Arc::new(Mutex::new(Vec::new())); let debug = MockMakeWriter::new(debug_buf.clone()); let warn_buf = Arc::new(Mutex::new(Vec::new())); let warn = MockMakeWriter::new(warn_buf.clone()); let err_buf = Arc::new(Mutex::new(Vec::new())); let err = MockMakeWriter::new(err_buf.clone()); let make_writer = info .with_max_level(Level::INFO) .and(debug.with_max_level(Level::DEBUG)) .and(warn.with_max_level(Level::WARN)) .and(err.with_max_level(Level::ERROR)); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); trace!("trace"); debug!("debug"); info!("info"); warn!("warn"); error!("error"); let all_lines = [ (Level::TRACE, "trace"), (Level::DEBUG, "debug"), (Level::INFO, "info"), (Level::WARN, "warn"), (Level::ERROR, "error"), ]; println!("max level debug"); has_lines(&debug_buf, &all_lines[1..]); println!("max level info"); has_lines(&info_buf, &all_lines[2..]); println!("max level warn"); has_lines(&warn_buf, &all_lines[3..]); println!("max level error"); has_lines(&err_buf, &all_lines[4..]); } #[test] fn combinators_or_else() { let some_buf = Arc::new(Mutex::new(Vec::new())); let some = MockMakeWriter::new(some_buf.clone()); let or_else_buf = Arc::new(Mutex::new(Vec::new())); let or_else = MockMakeWriter::new(or_else_buf.clone()); let return_some = AtomicBool::new(true); let make_writer = move || { if return_some.swap(false, Ordering::Relaxed) { OptionalWriter::some(some.make_writer()) } else { OptionalWriter::none() } }; let make_writer = make_writer.or_else(or_else); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); info!("hello"); info!("world"); info!("goodbye"); has_lines(&some_buf, &[(Level::INFO, "hello")]); has_lines( &or_else_buf, &[(Level::INFO, "world"), (Level::INFO, "goodbye")], ); } #[test] fn combinators_or_else_chain() { let info_buf = Arc::new(Mutex::new(Vec::new())); let info = MockMakeWriter::new(info_buf.clone()); let debug_buf = Arc::new(Mutex::new(Vec::new())); let debug = MockMakeWriter::new(debug_buf.clone()); let warn_buf = Arc::new(Mutex::new(Vec::new())); let warn = MockMakeWriter::new(warn_buf.clone()); let err_buf = Arc::new(Mutex::new(Vec::new())); let err = MockMakeWriter::new(err_buf.clone()); let make_writer = err.with_max_level(Level::ERROR).or_else( warn.with_max_level(Level::WARN).or_else( info.with_max_level(Level::INFO) .or_else(debug.with_max_level(Level::DEBUG)), ), ); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); trace!("trace"); debug!("debug"); info!("info"); warn!("warn"); error!("error"); println!("max level debug"); has_lines(&debug_buf, &[(Level::DEBUG, "debug")]); println!("max level info"); has_lines(&info_buf, &[(Level::INFO, "info")]); println!("max level warn"); has_lines(&warn_buf, &[(Level::WARN, "warn")]); println!("max level error"); has_lines(&err_buf, &[(Level::ERROR, "error")]); } #[test] fn combinators_and() { let a_buf = Arc::new(Mutex::new(Vec::new())); let a = MockMakeWriter::new(a_buf.clone()); let b_buf = Arc::new(Mutex::new(Vec::new())); let b = MockMakeWriter::new(b_buf.clone()); let lines = &[(Level::INFO, "hello"), (Level::INFO, "world")]; let make_writer = a.and(b); let c = { #[cfg(feature = "ansi")] let f = Format::default().without_time().with_ansi(false); #[cfg(not(feature = "ansi"))] let f = Format::default().without_time(); Subscriber::builder() .event_format(f) .with_writer(make_writer) .with_max_level(Level::TRACE) .finish() }; let _s = tracing::subscriber::set_default(c); info!("hello"); info!("world"); has_lines(&a_buf, &lines[..]); has_lines(&b_buf, &lines[..]); } } tracing-subscriber-0.3.18/src/layer/context.rs000064400000000000000000000406611046102023000174510ustar 00000000000000use tracing_core::{metadata::Metadata, span, subscriber::Subscriber, Event}; use crate::registry::{self, LookupSpan, SpanRef}; #[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; /// Represents information about the current context provided to [`Layer`]s by the /// wrapped [`Subscriber`]. /// /// To access [stored data] keyed by a span ID, implementors of the `Layer` /// trait should ensure that the `Subscriber` type parameter is *also* bound by the /// [`LookupSpan`]: /// /// ```rust /// use tracing::Subscriber; /// use tracing_subscriber::{Layer, registry::LookupSpan}; /// /// pub struct MyLayer; /// /// impl Layer for MyLayer /// where /// S: Subscriber + for<'a> LookupSpan<'a>, /// { /// // ... /// } /// ``` /// /// [`Layer`]: super::Layer /// [`Subscriber`]: tracing_core::Subscriber /// [stored data]: crate::registry::SpanRef /// [`LookupSpan`]: crate::registry::LookupSpan #[derive(Debug)] pub struct Context<'a, S> { subscriber: Option<&'a S>, /// The bitmask of all [`Filtered`] layers that currently apply in this /// context. If there is only a single [`Filtered`] wrapping the layer that /// produced this context, then this is that filter's ID. Otherwise, if we /// are in a nested tree with multiple filters, this is produced by /// [`and`]-ing together the [`FilterId`]s of each of the filters that wrap /// the current layer. /// /// [`Filtered`]: crate::filter::Filtered /// [`FilterId`]: crate::filter::FilterId /// [`and`]: crate::filter::FilterId::and #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } // === impl Context === impl<'a, S> Context<'a, S> where S: Subscriber, { pub(super) fn new(subscriber: &'a S) -> Self { Self { subscriber: Some(subscriber), #[cfg(feature = "registry")] filter: FilterId::none(), } } /// Returns the wrapped subscriber's view of the current span. #[inline] pub fn current_span(&self) -> span::Current { self.subscriber .map(Subscriber::current_span) // TODO: this would be more correct as "unknown", so perhaps // `tracing-core` should make `Current::unknown()` public? .unwrap_or_else(span::Current::none) } /// Returns whether the wrapped subscriber would enable the current span. #[inline] pub fn enabled(&self, metadata: &Metadata<'_>) -> bool { self.subscriber .map(|subscriber| subscriber.enabled(metadata)) // If this context is `None`, we are registering a callsite, so // return `true` so that the layer does not incorrectly assume that // the inner subscriber has disabled this metadata. // TODO(eliza): would it be more correct for this to return an `Option`? .unwrap_or(true) } /// Records the provided `event` with the wrapped subscriber. /// /// # Notes /// /// - The subscriber is free to expect that the event's callsite has been /// [registered][register], and may panic or fail to observe the event if this is /// not the case. The `tracing` crate's macros ensure that all events are /// registered, but if the event is constructed through other means, the /// user is responsible for ensuring that [`register_callsite`][register] /// has been called prior to calling this method. /// - This does _not_ call [`enabled`] on the inner subscriber. If the /// caller wishes to apply the wrapped subscriber's filter before choosing /// whether to record the event, it may first call [`Context::enabled`] to /// check whether the event would be enabled. This allows `Layer`s to /// elide constructing the event if it would not be recorded. /// /// [register]: tracing_core::subscriber::Subscriber::register_callsite() /// [`enabled`]: tracing_core::subscriber::Subscriber::enabled() /// [`Context::enabled`]: Context::enabled() #[inline] pub fn event(&self, event: &Event<'_>) { if let Some(subscriber) = self.subscriber { subscriber.event(event); } } /// Returns a [`SpanRef`] for the parent span of the given [`Event`], if /// it has a parent. /// /// If the event has an explicitly overridden parent, this method returns /// a reference to that span. If the event's parent is the current span, /// this returns a reference to the current span, if there is one. If this /// returns `None`, then either the event's parent was explicitly set to /// `None`, or the event's parent was defined contextually, but no span /// is currently entered. /// /// Compared to [`Context::current_span`] and [`Context::lookup_current`], /// this respects overrides provided by the [`Event`]. /// /// Compared to [`Event::parent`], this automatically falls back to the contextual /// span, if required. /// /// ```rust /// use tracing::{Event, Subscriber}; /// use tracing_subscriber::{ /// layer::{Context, Layer}, /// prelude::*, /// registry::LookupSpan, /// }; /// /// struct PrintingLayer; /// impl Layer for PrintingLayer /// where /// S: Subscriber + for<'lookup> LookupSpan<'lookup>, /// { /// fn on_event(&self, event: &Event, ctx: Context) { /// let span = ctx.event_span(event); /// println!("Event in span: {:?}", span.map(|s| s.name())); /// } /// } /// /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { /// tracing::info!("no span"); /// // Prints: Event in span: None /// /// let span = tracing::info_span!("span"); /// tracing::info!(parent: &span, "explicitly specified"); /// // Prints: Event in span: Some("span") /// /// let _guard = span.enter(); /// tracing::info!("contextual span"); /// // Prints: Event in span: Some("span") /// }); /// ``` /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
#[inline] pub fn event_span(&self, event: &Event<'_>) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { if event.is_root() { None } else if event.is_contextual() { self.lookup_current() } else { // TODO(eliza): this should handle parent IDs event.parent().and_then(|id| self.span(id)) } } /// Returns metadata for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). #[inline] pub fn metadata(&self, id: &span::Id) -> Option<&'static Metadata<'static>> where S: for<'lookup> LookupSpan<'lookup>, { let span = self.span(id)?; Some(span.metadata()) } /// Returns [stored data] for the span with the given `id`, if it exists. /// /// If this returns `None`, then no span exists for that ID (either it has /// closed or the ID is invalid). /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef #[inline] pub fn span(&self, id: &span::Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { let span = self.subscriber.as_ref()?.span(id)?; #[cfg(all(feature = "registry", feature = "std"))] return span.try_with_filter(self.filter); #[cfg(not(feature = "registry"))] Some(span) } /// Returns `true` if an active span exists for the given `Id`. /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
#[inline] pub fn exists(&self, id: &span::Id) -> bool where S: for<'lookup> LookupSpan<'lookup>, { self.subscriber.as_ref().and_then(|s| s.span(id)).is_some() } /// Returns [stored data] for the span that the wrapped subscriber considers /// to be the current. /// /// If this returns `None`, then we are not currently within a span. /// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef #[inline] pub fn lookup_current(&self) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { let subscriber = *self.subscriber.as_ref()?; let current = subscriber.current_span(); let id = current.id()?; let span = subscriber.span(id); debug_assert!( span.is_some(), "the subscriber should have data for the current span ({:?})!", id, ); // If we found a span, and our per-layer filter enables it, return that // span! #[cfg(all(feature = "registry", feature = "std"))] { if let Some(span) = span?.try_with_filter(self.filter) { Some(span) } else { // Otherwise, the span at the *top* of the stack is disabled by // per-layer filtering, but there may be additional spans in the stack. // // Currently, `LookupSpan` doesn't have a nice way of exposing access to // the whole span stack. However, if we can downcast the innermost // subscriber to a a `Registry`, we can iterate over its current span // stack. // // TODO(eliza): when https://github.com/tokio-rs/tracing/issues/1459 is // implemented, change this to use that instead... self.lookup_current_filtered(subscriber) } } #[cfg(not(feature = "registry"))] span } /// Slow path for when the current span is disabled by PLF and we have a /// registry. // This is called by `lookup_current` in the case that per-layer filtering // is in use. `lookup_current` is allowed to be inlined, but this method is // factored out to prevent the loop and (potentially-recursive) subscriber // downcasting from being inlined if `lookup_current` is inlined. #[inline(never)] #[cfg(all(feature = "registry", feature = "std"))] fn lookup_current_filtered<'lookup>( &self, subscriber: &'lookup S, ) -> Option> where S: LookupSpan<'lookup>, { let registry = (subscriber as &dyn Subscriber).downcast_ref::()?; registry .span_stack() .iter() .find_map(|id| subscriber.span(id)?.try_with_filter(self.filter)) } /// 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. /// ///
    /// Note: Compared to scope this
    /// returns the spans in reverse order (from leaf to root). Use
    /// Scope::from_root
    /// in case root-to-leaf ordering is desired.
    /// 
/// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef pub fn span_scope(&self, id: &span::Id) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { Some(self.span(id)?.scope()) } /// Returns an iterator over the [stored data] for all the spans in the /// current context, starting with the parent span of the specified event, /// and ending with the root of the trace tree and ending with the current span. /// ///
    /// Note: Compared to scope this
    /// returns the spans in reverse order (from leaf to root). Use
    /// Scope::from_root
    /// in case root-to-leaf ordering is desired.
    /// 
/// ///
    ///     Note: This requires the wrapped subscriber to
    ///     implement the 
    ///     LookupSpan trait. See the documentation on
    ///     Context's
    ///     declaration for details.
    /// 
/// /// [stored data]: crate::registry::SpanRef pub fn event_scope(&self, event: &Event<'_>) -> Option> where S: for<'lookup> LookupSpan<'lookup>, { Some(self.event_span(event)?.scope()) } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn with_filter(self, filter: FilterId) -> Self { // If we already have our own `FilterId`, combine it with the provided // one. That way, the new `FilterId` will consider a span to be disabled // if it was disabled by the given `FilterId` *or* any `FilterId`s for // layers "above" us in the stack. // // See the doc comment for `FilterId::and` for details. let filter = self.filter.and(filter); Self { filter, ..self } } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn is_enabled_for(&self, span: &span::Id, filter: FilterId) -> bool where S: for<'lookup> LookupSpan<'lookup>, { self.is_enabled_inner(span, filter).unwrap_or(false) } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn if_enabled_for(self, span: &span::Id, filter: FilterId) -> Option where S: for<'lookup> LookupSpan<'lookup>, { if self.is_enabled_inner(span, filter)? { Some(self.with_filter(filter)) } else { None } } #[cfg(all(feature = "registry", feature = "std"))] fn is_enabled_inner(&self, span: &span::Id, filter: FilterId) -> Option where S: for<'lookup> LookupSpan<'lookup>, { Some(self.span(span)?.is_enabled_for(filter)) } } impl<'a, S> Context<'a, S> { pub(crate) fn none() -> Self { Self { subscriber: None, #[cfg(feature = "registry")] filter: FilterId::none(), } } } impl<'a, S> Clone for Context<'a, S> { #[inline] fn clone(&self) -> Self { let subscriber = self.subscriber.as_ref().copied(); Context { subscriber, #[cfg(all(feature = "registry", feature = "std"))] filter: self.filter, } } } tracing-subscriber-0.3.18/src/layer/layered.rs000064400000000000000000000454671046102023000174230ustar 00000000000000use tracing_core::{metadata::Metadata, span, Dispatch, Event, Interest, LevelFilter, Subscriber}; use crate::{ filter, layer::{Context, Layer}, registry::LookupSpan, }; #[cfg(all(feature = "registry", feature = "std"))] use crate::{filter::FilterId, registry::Registry}; use core::{ any::{Any, TypeId}, cmp, fmt, marker::PhantomData, }; /// A [`Subscriber`] composed of a `Subscriber` wrapped by one or more /// [`Layer`]s. /// /// [`Layer`]: crate::Layer /// [`Subscriber`]: tracing_core::Subscriber #[derive(Clone)] pub struct Layered { /// The layer. layer: L, /// The inner value that `self.layer` was layered onto. /// /// If this is also a `Layer`, then this `Layered` will implement `Layer`. /// If this is a `Subscriber`, then this `Layered` will implement /// `Subscriber` instead. inner: I, // These booleans are used to determine how to combine `Interest`s and max // level hints when per-layer filters are in use. /// Is `self.inner` a `Registry`? /// /// If so, when combining `Interest`s, we want to "bubble up" its /// `Interest`. inner_is_registry: bool, /// Does `self.layer` have per-layer filters? /// /// This will be true if: /// - `self.inner` is a `Filtered`. /// - `self.inner` is a tree of `Layered`s where _all_ arms of those /// `Layered`s have per-layer filters. /// /// Otherwise, if it's a `Layered` with one per-layer filter in one branch, /// but a non-per-layer-filtered layer in the other branch, this will be /// _false_, because the `Layered` is already handling the combining of /// per-layer filter `Interest`s and max level hints with its non-filtered /// `Layer`. has_layer_filter: bool, /// Does `self.inner` have per-layer filters? /// /// This is determined according to the same rules as /// `has_layer_filter` above. inner_has_layer_filter: bool, _s: PhantomData, } // === impl Layered === impl Layered where L: Layer, S: Subscriber, { /// Returns `true` if this [`Subscriber`] is the same type as `T`. pub fn is(&self) -> bool { self.downcast_ref::().is_some() } /// Returns some reference to this [`Subscriber`] value if it is of type `T`, /// or `None` if it isn't. pub fn downcast_ref(&self) -> Option<&T> { unsafe { let raw = self.downcast_raw(TypeId::of::())?; if raw.is_null() { None } else { Some(&*(raw as *const T)) } } } } impl Subscriber for Layered where L: Layer, S: Subscriber, { fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.pick_interest(self.layer.register_callsite(metadata), || { self.inner.register_callsite(metadata) }) } fn enabled(&self, metadata: &Metadata<'_>) -> bool { if self.layer.enabled(metadata, self.ctx()) { // if the outer layer enables the callsite metadata, ask the subscriber. self.inner.enabled(metadata) } else { // otherwise, the callsite is disabled by the layer // If per-layer filters are in use, and we are short-circuiting // (rather than calling into the inner type), clear the current // per-layer filter `enabled` state. #[cfg(feature = "registry")] filter::FilterState::clear_enabled(); false } } fn max_level_hint(&self) -> Option { self.pick_level_hint( self.layer.max_level_hint(), self.inner.max_level_hint(), super::subscriber_is_none(&self.inner), ) } fn new_span(&self, span: &span::Attributes<'_>) -> span::Id { let id = self.inner.new_span(span); self.layer.on_new_span(span, &id, self.ctx()); id } fn record(&self, span: &span::Id, values: &span::Record<'_>) { self.inner.record(span, values); self.layer.on_record(span, values, self.ctx()); } fn record_follows_from(&self, span: &span::Id, follows: &span::Id) { self.inner.record_follows_from(span, follows); self.layer.on_follows_from(span, follows, self.ctx()); } fn event_enabled(&self, event: &Event<'_>) -> bool { if self.layer.event_enabled(event, self.ctx()) { // if the outer layer enables the event, ask the inner subscriber. self.inner.event_enabled(event) } else { // otherwise, the event is disabled by this layer false } } fn event(&self, event: &Event<'_>) { self.inner.event(event); self.layer.on_event(event, self.ctx()); } fn enter(&self, span: &span::Id) { self.inner.enter(span); self.layer.on_enter(span, self.ctx()); } fn exit(&self, span: &span::Id) { self.inner.exit(span); self.layer.on_exit(span, self.ctx()); } fn clone_span(&self, old: &span::Id) -> span::Id { let new = self.inner.clone_span(old); if &new != old { self.layer.on_id_change(old, &new, self.ctx()) }; new } #[inline] fn drop_span(&self, id: span::Id) { self.try_close(id); } fn try_close(&self, id: span::Id) -> bool { #[cfg(all(feature = "registry", feature = "std"))] let subscriber = &self.inner as &dyn Subscriber; #[cfg(all(feature = "registry", feature = "std"))] let mut guard = subscriber .downcast_ref::() .map(|registry| registry.start_close(id.clone())); if self.inner.try_close(id.clone()) { // If we have a registry's close guard, indicate that the span is // closing. #[cfg(all(feature = "registry", feature = "std"))] { if let Some(g) = guard.as_mut() { g.set_closing() }; } self.layer.on_close(id, self.ctx()); true } else { false } } #[inline] fn current_span(&self) -> span::Current { self.inner.current_span() } #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { // Unlike the implementation of `Layer` for `Layered`, we don't have to // handle the "magic PLF downcast marker" here. If a `Layered` // implements `Subscriber`, we already know that the `inner` branch is // going to contain something that doesn't have per-layer filters (the // actual root `Subscriber`). Thus, a `Layered` that implements // `Subscriber` will always be propagating the root subscriber's // `Interest`/level hint, even if it includes a `Layer` that has // per-layer filters, because it will only ever contain layers where // _one_ child has per-layer filters. // // The complex per-layer filter detection logic is only relevant to // *trees* of layers, which involve the `Layer` implementation for // `Layered`, not *lists* of layers, where every `Layered` implements // `Subscriber`. Of course, a linked list can be thought of as a // degenerate tree...but luckily, we are able to make a type-level // distinction between individual `Layered`s that are definitely // list-shaped (their inner child implements `Subscriber`), and // `Layered`s that might be tree-shaped (the inner child is also a // `Layer`). // If downcasting to `Self`, return a pointer to `self`. if id == TypeId::of::() { return Some(self as *const _ as *const ()); } self.layer .downcast_raw(id) .or_else(|| self.inner.downcast_raw(id)) } } impl Layer for Layered where A: Layer, B: Layer, S: Subscriber, { fn on_register_dispatch(&self, subscriber: &Dispatch) { self.layer.on_register_dispatch(subscriber); self.inner.on_register_dispatch(subscriber); } fn on_layer(&mut self, subscriber: &mut S) { self.layer.on_layer(subscriber); self.inner.on_layer(subscriber); } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.pick_interest(self.layer.register_callsite(metadata), || { self.inner.register_callsite(metadata) }) } fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { if self.layer.enabled(metadata, ctx.clone()) { // if the outer subscriber enables the callsite metadata, ask the inner layer. self.inner.enabled(metadata, ctx) } else { // otherwise, the callsite is disabled by this layer false } } fn max_level_hint(&self) -> Option { self.pick_level_hint( self.layer.max_level_hint(), self.inner.max_level_hint(), super::layer_is_none(&self.inner), ) } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { self.inner.on_new_span(attrs, id, ctx.clone()); self.layer.on_new_span(attrs, id, ctx); } #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { self.inner.on_record(span, values, ctx.clone()); self.layer.on_record(span, values, ctx); } #[inline] fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { self.inner.on_follows_from(span, follows, ctx.clone()); self.layer.on_follows_from(span, follows, ctx); } #[inline] fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { if self.layer.event_enabled(event, ctx.clone()) { // if the outer layer enables the event, ask the inner subscriber. self.inner.event_enabled(event, ctx) } else { // otherwise, the event is disabled by this layer false } } #[inline] fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { self.inner.on_event(event, ctx.clone()); self.layer.on_event(event, ctx); } #[inline] fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { self.inner.on_enter(id, ctx.clone()); self.layer.on_enter(id, ctx); } #[inline] fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { self.inner.on_exit(id, ctx.clone()); self.layer.on_exit(id, ctx); } #[inline] fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { self.inner.on_close(id.clone(), ctx.clone()); self.layer.on_close(id, ctx); } #[inline] fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { self.inner.on_id_change(old, new, ctx.clone()); self.layer.on_id_change(old, new, ctx); } #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { match id { // If downcasting to `Self`, return a pointer to `self`. id if id == TypeId::of::() => Some(self as *const _ as *const ()), // Oh, we're looking for per-layer filters! // // This should only happen if we are inside of another `Layered`, // and it's trying to determine how it should combine `Interest`s // and max level hints. // // In that case, this `Layered` should be considered to be // "per-layer filtered" if *both* the outer layer and the inner // layer/subscriber have per-layer filters. Otherwise, this `Layered // should *not* be considered per-layer filtered (even if one or the // other has per layer filters). If only one `Layer` is per-layer // filtered, *this* `Layered` will handle aggregating the `Interest` // and level hints on behalf of its children, returning the // aggregate (which is the value from the &non-per-layer-filtered* // child). // // Yes, this rule *is* slightly counter-intuitive, but it's // necessary due to a weird edge case that can occur when two // `Layered`s where one side is per-layer filtered and the other // isn't are `Layered` together to form a tree. If we didn't have // this rule, we would actually end up *ignoring* `Interest`s from // the non-per-layer-filtered layers, since both branches would // claim to have PLF. // // If you don't understand this...that's fine, just don't mess with // it. :) id if filter::is_plf_downcast_marker(id) => { self.layer.downcast_raw(id).and(self.inner.downcast_raw(id)) } // Otherwise, try to downcast both branches normally... _ => self .layer .downcast_raw(id) .or_else(|| self.inner.downcast_raw(id)), } } } impl<'a, L, S> LookupSpan<'a> for Layered where S: Subscriber + LookupSpan<'a>, { type Data = S::Data; fn span_data(&'a self, id: &span::Id) -> Option { self.inner.span_data(id) } #[cfg(all(feature = "registry", feature = "std"))] fn register_filter(&mut self) -> FilterId { self.inner.register_filter() } } impl Layered where S: Subscriber, { fn ctx(&self) -> Context<'_, S> { Context::new(&self.inner) } } impl Layered where A: Layer, S: Subscriber, { pub(super) fn new(layer: A, inner: B, inner_has_layer_filter: bool) -> Self { #[cfg(all(feature = "registry", feature = "std"))] let inner_is_registry = TypeId::of::() == TypeId::of::(); #[cfg(not(all(feature = "registry", feature = "std")))] let inner_is_registry = false; let inner_has_layer_filter = inner_has_layer_filter || inner_is_registry; let has_layer_filter = filter::layer_has_plf(&layer); Self { layer, inner, has_layer_filter, inner_has_layer_filter, inner_is_registry, _s: PhantomData, } } fn pick_interest(&self, outer: Interest, inner: impl FnOnce() -> Interest) -> Interest { if self.has_layer_filter { return inner(); } // If the outer layer has disabled the callsite, return now so that // the inner layer/subscriber doesn't get its hopes up. if outer.is_never() { // If per-layer filters are in use, and we are short-circuiting // (rather than calling into the inner type), clear the current // per-layer filter interest state. #[cfg(feature = "registry")] filter::FilterState::take_interest(); return outer; } // The `inner` closure will call `inner.register_callsite()`. We do this // before the `if` statement to ensure that the inner subscriber is // informed that the callsite exists regardless of the outer layer's // filtering decision. let inner = inner(); if outer.is_sometimes() { // if this interest is "sometimes", return "sometimes" to ensure that // filters are reevaluated. return outer; } // If there is a per-layer filter in the `inner` stack, and it returns // `never`, change the interest to `sometimes`, because the `outer` // layer didn't return `never`. This means that _some_ layer still wants // to see that callsite, even though the inner stack's per-layer filter // didn't want it. Therefore, returning `sometimes` will ensure // `enabled` is called so that the per-layer filter can skip that // span/event, while the `outer` layer still gets to see it. if inner.is_never() && self.inner_has_layer_filter { return Interest::sometimes(); } // otherwise, allow the inner subscriber or subscriber to weigh in. inner } fn pick_level_hint( &self, outer_hint: Option, inner_hint: Option, inner_is_none: bool, ) -> Option { if self.inner_is_registry { return outer_hint; } if self.has_layer_filter && self.inner_has_layer_filter { return Some(cmp::max(outer_hint?, inner_hint?)); } if self.has_layer_filter && inner_hint.is_none() { return None; } if self.inner_has_layer_filter && outer_hint.is_none() { return None; } // If the layer is `Option::None`, then we // want to short-circuit the layer underneath, if it // returns `None`, to override the `None` layer returning // `Some(OFF)`, which should ONLY apply when there are // no other layers that return `None`. Note this // `None` does not == `Some(TRACE)`, it means // something more like: "whatever all the other // layers agree on, default to `TRACE` if none // have an opinion". We also choose do this AFTER // we check for per-layer filters, which // have their own logic. // // Also note that this does come at some perf cost, but // this function is only called on initialization and // subscriber reloading. if super::layer_is_none(&self.layer) { return cmp::max(outer_hint, Some(inner_hint?)); } // Similarly, if the layer on the inside is `None` and it returned an // `Off` hint, we want to override that with the outer hint. if inner_is_none && inner_hint == Some(LevelFilter::OFF) { return outer_hint; } cmp::max(outer_hint, inner_hint) } } impl fmt::Debug for Layered where A: fmt::Debug, B: fmt::Debug, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(all(feature = "registry", feature = "std"))] let alt = f.alternate(); let mut s = f.debug_struct("Layered"); // These additional fields are more verbose and usually only necessary // for internal debugging purposes, so only print them if alternate mode // is enabled. #[cfg(all(feature = "registry", feature = "std"))] { if alt { s.field("inner_is_registry", &self.inner_is_registry) .field("has_layer_filter", &self.has_layer_filter) .field("inner_has_layer_filter", &self.inner_has_layer_filter); } } s.field("layer", &self.layer) .field("inner", &self.inner) .finish() } } tracing-subscriber-0.3.18/src/layer/mod.rs000064400000000000000000002224731046102023000165470ustar 00000000000000//! The [`Layer`] trait, a composable abstraction for building [`Subscriber`]s. //! //! The [`Subscriber`] trait in `tracing-core` represents the _complete_ set of //! functionality required to consume `tracing` instrumentation. This means that //! a single `Subscriber` instance is a self-contained implementation of a //! complete strategy for collecting traces; but it _also_ means that the //! `Subscriber` trait cannot easily be composed with other `Subscriber`s. //! //! In particular, [`Subscriber`]s are responsible for generating [span IDs] and //! assigning them to spans. Since these IDs must uniquely identify a span //! within the context of the current trace, this means that there may only be //! a single `Subscriber` for a given thread at any point in time — //! otherwise, there would be no authoritative source of span IDs. //! //! On the other hand, the majority of the [`Subscriber`] trait's functionality //! is composable: any number of subscribers may _observe_ events, span entry //! and exit, and so on, provided that there is a single authoritative source of //! span IDs. The [`Layer`] trait represents this composable subset of the //! [`Subscriber`] behavior; it can _observe_ events and spans, but does not //! assign IDs. //! //! # Composing Layers //! //! Since a [`Layer`] does not implement a complete strategy for collecting //! traces, it must be composed with a `Subscriber` in order to be used. The //! [`Layer`] trait is generic over a type parameter (called `S` in the trait //! definition), representing the types of `Subscriber` they can be composed //! with. Thus, a [`Layer`] may be implemented that will only compose with a //! particular `Subscriber` implementation, or additional trait bounds may be //! added to constrain what types implementing `Subscriber` a `Layer` can wrap. //! //! `Layer`s may be added to a `Subscriber` by using the [`SubscriberExt::with`] //! method, which is provided by `tracing-subscriber`'s [prelude]. This method //! returns a [`Layered`] struct that implements `Subscriber` by composing the //! `Layer` with the `Subscriber`. //! //! For example: //! ```rust //! use tracing_subscriber::Layer; //! use tracing_subscriber::prelude::*; //! use tracing::Subscriber; //! //! pub struct MyLayer { //! // ... //! } //! //! impl Layer for MyLayer { //! // ... //! } //! //! pub struct MySubscriber { //! // ... //! } //! //! # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event}; //! impl Subscriber for MySubscriber { //! // ... //! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) } //! # fn record(&self, _: &Id, _: &Record) {} //! # fn event(&self, _: &Event) {} //! # fn record_follows_from(&self, _: &Id, _: &Id) {} //! # fn enabled(&self, _: &Metadata) -> bool { false } //! # fn enter(&self, _: &Id) {} //! # fn exit(&self, _: &Id) {} //! } //! # impl MyLayer { //! # fn new() -> Self { Self {} } //! # } //! # impl MySubscriber { //! # fn new() -> Self { Self { }} //! # } //! //! let subscriber = MySubscriber::new() //! .with(MyLayer::new()); //! //! tracing::subscriber::set_global_default(subscriber); //! ``` //! //! Multiple `Layer`s may be composed in the same manner: //! ```rust //! # use tracing_subscriber::{Layer, layer::SubscriberExt}; //! # use tracing::Subscriber; //! pub struct MyOtherLayer { //! // ... //! } //! //! impl Layer for MyOtherLayer { //! // ... //! } //! //! pub struct MyThirdLayer { //! // ... //! } //! //! impl Layer for MyThirdLayer { //! // ... //! } //! # pub struct MyLayer {} //! # impl Layer for MyLayer {} //! # pub struct MySubscriber { } //! # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event}; //! # impl Subscriber for MySubscriber { //! # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) } //! # fn record(&self, _: &Id, _: &Record) {} //! # fn event(&self, _: &Event) {} //! # fn record_follows_from(&self, _: &Id, _: &Id) {} //! # fn enabled(&self, _: &Metadata) -> bool { false } //! # fn enter(&self, _: &Id) {} //! # fn exit(&self, _: &Id) {} //! } //! # impl MyLayer { //! # fn new() -> Self { Self {} } //! # } //! # impl MyOtherLayer { //! # fn new() -> Self { Self {} } //! # } //! # impl MyThirdLayer { //! # fn new() -> Self { Self {} } //! # } //! # impl MySubscriber { //! # fn new() -> Self { Self { }} //! # } //! //! let subscriber = MySubscriber::new() //! .with(MyLayer::new()) //! .with(MyOtherLayer::new()) //! .with(MyThirdLayer::new()); //! //! tracing::subscriber::set_global_default(subscriber); //! ``` //! //! The [`Layer::with_subscriber`] constructs the [`Layered`] type from a //! [`Layer`] and [`Subscriber`], and is called by [`SubscriberExt::with`]. In //! general, it is more idiomatic to use [`SubscriberExt::with`], and treat //! [`Layer::with_subscriber`] as an implementation detail, as `with_subscriber` //! calls must be nested, leading to less clear code for the reader. //! //! ## Runtime Configuration With `Layer`s //! //! In some cases, a particular [`Layer`] may be enabled or disabled based on //! runtime configuration. This can introduce challenges, because the type of a //! layered [`Subscriber`] depends on which layers are added to it: if an `if` //! or `match` expression adds some [`Layer`] implementation in one branch, //! and other layers in another, the [`Subscriber`] values returned by those //! branches will have different types. For example, the following _will not_ //! work: //! //! ```compile_fail //! # fn docs() -> Result<(), Box> { //! # struct Config { //! # is_prod: bool, //! # path: &'static str, //! # } //! # let cfg = Config { is_prod: false, path: "debug.log" }; //! use std::fs::File; //! use tracing_subscriber::{Registry, prelude::*}; //! //! let stdout_log = tracing_subscriber::fmt::layer().pretty(); //! let subscriber = Registry::default().with(stdout_log); //! //! // The compile error will occur here because the if and else //! // branches have different (and therefore incompatible) types. //! let subscriber = if cfg.is_prod { //! let file = File::create(cfg.path)?; //! let layer = tracing_subscriber::fmt::layer() //! .json() //! .with_writer(Arc::new(file)); //! layer.with(subscriber) //! } else { //! layer //! }; //! //! tracing::subscriber::set_global_default(subscriber) //! .expect("Unable to set global subscriber"); //! # Ok(()) } //! ``` //! //! However, a [`Layer`] wrapped in an [`Option`] [also implements the `Layer` //! trait][option-impl]. This allows individual layers to be enabled or disabled at //! runtime while always producing a [`Subscriber`] of the same type. For //! example: //! //! ``` //! # fn docs() -> Result<(), Box> { //! # struct Config { //! # is_prod: bool, //! # path: &'static str, //! # } //! # let cfg = Config { is_prod: false, path: "debug.log" }; //! use std::fs::File; //! use tracing_subscriber::{Registry, prelude::*}; //! //! let stdout_log = tracing_subscriber::fmt::layer().pretty(); //! let subscriber = Registry::default().with(stdout_log); //! //! // if `cfg.is_prod` is true, also log JSON-formatted logs to a file. //! let json_log = if cfg.is_prod { //! let file = File::create(cfg.path)?; //! let json_log = tracing_subscriber::fmt::layer() //! .json() //! .with_writer(file); //! Some(json_log) //! } else { //! None //! }; //! //! // If `cfg.is_prod` is false, then `json` will be `None`, and this layer //! // will do nothing. However, the subscriber will still have the same type //! // regardless of whether the `Option`'s value is `None` or `Some`. //! let subscriber = subscriber.with(json_log); //! //! tracing::subscriber::set_global_default(subscriber) //! .expect("Unable to set global subscriber"); //! # Ok(()) } //! ``` //! //! If a [`Layer`] may be one of several different types, note that [`Box + Send + Sync>` implements `Layer`][box-impl]. //! This may be used to erase the type of a [`Layer`]. //! //! For example, a function that configures a [`Layer`] to log to one of //! several outputs might return a `Box + Send + Sync + 'static>`: //! ``` //! use tracing_subscriber::{ //! Layer, //! registry::LookupSpan, //! prelude::*, //! }; //! use std::{path::PathBuf, fs::File, io}; //! //! /// Configures whether logs are emitted to a file, to stdout, or to stderr. //! pub enum LogConfig { //! File(PathBuf), //! Stdout, //! Stderr, //! } //! //! impl LogConfig { //! pub fn layer(self) -> Box + Send + Sync + 'static> //! where //! S: tracing_core::Subscriber, //! for<'a> S: LookupSpan<'a>, //! { //! // Shared configuration regardless of where logs are output to. //! let fmt = tracing_subscriber::fmt::layer() //! .with_target(true) //! .with_thread_names(true); //! //! // Configure the writer based on the desired log target: //! match self { //! LogConfig::File(path) => { //! let file = File::create(path).expect("failed to create log file"); //! Box::new(fmt.with_writer(file)) //! }, //! LogConfig::Stdout => Box::new(fmt.with_writer(io::stdout)), //! LogConfig::Stderr => Box::new(fmt.with_writer(io::stderr)), //! } //! } //! } //! //! let config = LogConfig::Stdout; //! tracing_subscriber::registry() //! .with(config.layer()) //! .init(); //! ``` //! //! The [`Layer::boxed`] method is provided to make boxing a `Layer` //! more convenient, but [`Box::new`] may be used as well. //! //! When the number of `Layer`s varies at runtime, note that a //! [`Vec where L: Layer` also implements `Layer`][vec-impl]. This //! can be used to add a variable number of `Layer`s to a `Subscriber`: //! //! ``` //! use tracing_subscriber::{Layer, prelude::*}; //! struct MyLayer { //! // ... //! } //! # impl MyLayer { fn new() -> Self { Self {} }} //! //! impl Layer for MyLayer { //! // ... //! } //! //! /// Returns how many layers we need //! fn how_many_layers() -> usize { //! // ... //! # 3 //! } //! //! // Create a variable-length `Vec` of layers //! let mut layers = Vec::new(); //! for _ in 0..how_many_layers() { //! layers.push(MyLayer::new()); //! } //! //! tracing_subscriber::registry() //! .with(layers) //! .init(); //! ``` //! //! If a variable number of `Layer` is needed and those `Layer`s have //! different types, a `Vec` of [boxed `Layer` trait objects][box-impl] may //! be used. For example: //! //! ``` //! use tracing_subscriber::{filter::LevelFilter, Layer, prelude::*}; //! use std::fs::File; //! # fn main() -> Result<(), Box> { //! struct Config { //! enable_log_file: bool, //! enable_stdout: bool, //! enable_stderr: bool, //! // ... //! } //! # impl Config { //! # fn from_config_file()-> Result> { //! # // don't enable the log file so that the example doesn't actually create it //! # Ok(Self { enable_log_file: false, enable_stdout: true, enable_stderr: true }) //! # } //! # } //! //! let cfg = Config::from_config_file()?; //! //! // Based on our dynamically loaded config file, create any number of layers: //! let mut layers = Vec::new(); //! //! if cfg.enable_log_file { //! let file = File::create("myapp.log")?; //! let layer = tracing_subscriber::fmt::layer() //! .with_thread_names(true) //! .with_target(true) //! .json() //! .with_writer(file) //! // Box the layer as a type-erased trait object, so that it can //! // be pushed to the `Vec`. //! .boxed(); //! layers.push(layer); //! } //! //! if cfg.enable_stdout { //! let layer = tracing_subscriber::fmt::layer() //! .pretty() //! .with_filter(LevelFilter::INFO) //! // Box the layer as a type-erased trait object, so that it can //! // be pushed to the `Vec`. //! .boxed(); //! layers.push(layer); //! } //! //! if cfg.enable_stdout { //! let layer = tracing_subscriber::fmt::layer() //! .with_target(false) //! .with_filter(LevelFilter::WARN) //! // Box the layer as a type-erased trait object, so that it can //! // be pushed to the `Vec`. //! .boxed(); //! layers.push(layer); //! } //! //! tracing_subscriber::registry() //! .with(layers) //! .init(); //!# Ok(()) } //! ``` //! //! Finally, if the number of layers _changes_ at runtime, a `Vec` of //! subscribers can be used alongside the [`reload`](crate::reload) module to //! add or remove subscribers dynamically at runtime. //! //! [option-impl]: Layer#impl-Layer-for-Option //! [box-impl]: Layer#impl-Layer%3CS%3E-for-Box%3Cdyn%20Layer%3CS%3E%20+%20Send%20+%20Sync%3E //! [vec-impl]: Layer#impl-Layer-for-Vec //! [prelude]: crate::prelude //! //! # Recording Traces //! //! The [`Layer`] trait defines a set of methods for consuming notifications from //! tracing instrumentation, which are generally equivalent to the similarly //! named methods on [`Subscriber`]. Unlike [`Subscriber`], the methods on //! `Layer` are additionally passed a [`Context`] type, which exposes additional //! information provided by the wrapped subscriber (such as [the current span]) //! to the layer. //! //! # Filtering with `Layer`s //! //! As well as strategies for handling trace events, the `Layer` trait may also //! be used to represent composable _filters_. This allows the determination of //! what spans and events should be recorded to be decoupled from _how_ they are //! recorded: a filtering layer can be applied to other layers or //! subscribers. `Layer`s can be used to implement _global filtering_, where a //! `Layer` provides a filtering strategy for the entire subscriber. //! Additionally, individual recording `Layer`s or sets of `Layer`s may be //! combined with _per-layer filters_ that control what spans and events are //! recorded by those layers. //! //! ## Global Filtering //! //! A `Layer` that implements a filtering strategy should override the //! [`register_callsite`] and/or [`enabled`] methods. It may also choose to implement //! methods such as [`on_enter`], if it wishes to filter trace events based on //! the current span context. //! //! Note that the [`Layer::register_callsite`] and [`Layer::enabled`] methods //! determine whether a span or event is enabled *globally*. Thus, they should //! **not** be used to indicate whether an individual layer wishes to record a //! particular span or event. Instead, if a layer is only interested in a subset //! of trace data, but does *not* wish to disable other spans and events for the //! rest of the layer stack should ignore those spans and events in its //! notification methods. //! //! The filtering methods on a stack of `Layer`s are evaluated in a top-down //! order, starting with the outermost `Layer` and ending with the wrapped //! [`Subscriber`]. If any layer returns `false` from its [`enabled`] method, or //! [`Interest::never()`] from its [`register_callsite`] method, filter //! evaluation will short-circuit and the span or event will be disabled. //! //! ### Enabling Interest //! //! Whenever an tracing event (or span) is emitted, it goes through a number of //! steps to determine how and how much it should be processed. The earlier an //! event is disabled, the less work has to be done to process the event, so //! `Layer`s that implement filtering should attempt to disable unwanted //! events as early as possible. In order, each event checks: //! //! - [`register_callsite`], once per callsite (roughly: once per time that //! `event!` or `span!` is written in the source code; this is cached at the //! callsite). See [`Subscriber::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 //! [`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 //! of the actual `Event`/`Span` to be skipped. //! - For events only (and not spans), [`event_enabled`] is called just before //! processing the event. This gives layers one last chance to say that //! an event should be filtered out, now that the event's fields are known. //! //! ## Per-Layer Filtering //! //! **Note**: per-layer filtering APIs currently require the [`"registry"` crate //! feature flag][feat] to be enabled. //! //! Sometimes, it may be desirable for one `Layer` to record a particular subset //! of spans and events, while a different subset of spans and events are //! recorded by other `Layer`s. For example: //! //! - A layer that records metrics may wish to observe only events including //! particular tracked values, while a logging layer ignores those events. //! - If recording a distributed trace is expensive, it might be desirable to //! only send spans with `INFO` and lower verbosity to the distributed tracing //! system, while logging more verbose spans to a file. //! - Spans and events with a particular target might be recorded differently //! from others, such as by generating an HTTP access log from a span that //! tracks the lifetime of an HTTP request. //! //! The [`Filter`] trait is used to control what spans and events are //! observed by an individual `Layer`, while still allowing other `Layer`s to //! potentially record them. The [`Layer::with_filter`] method combines a //! `Layer` with a [`Filter`], returning a [`Filtered`] layer. //! //! 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 //! 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, Layer}; //! # use tracing_core::{Metadata, subscriber::Subscriber}; //! # struct MyLayer(std::marker::PhantomData); //! # impl MyLayer { fn new() -> Self { Self(std::marker::PhantomData)} } //! # impl Layer for MyLayer {} //! # fn my_filter(_: &str) -> impl Fn(&Metadata) -> bool { |_| true } //! fn setup_tracing(filter_config: Option<&str>) { //! let layer = MyLayer::::new() //! .with_filter(filter_config.map(|config| filter_fn(my_filter(config)))); //! //... //! } //! ``` //! //!
//!     Warning: Currently, the 
//!     Registry type defined in this crate is the only root
//!     Subscriber capable of supporting Layers with
//!     per-layer filters. In the future, new APIs will be added to allow other
//!     root Subscribers to support per-layer filters.
//! 
//! //! For example, to generate an HTTP access log based on spans with //! the `http_access` target, while logging other spans and events to //! standard out, a [`Filter`] can be added to the access log layer: //! //! ``` //! use tracing_subscriber::{filter, prelude::*}; //! //! // Generates an HTTP access log. //! let access_log = // ... //! # filter::LevelFilter::INFO; //! //! // Add a filter to the access log layer so that it only observes //! // spans and events with the `http_access` target. //! let access_log = access_log.with_filter(filter::filter_fn(|metadata| { //! // Returns `true` if and only if the span or event's target is //! // "http_access". //! metadata.target() == "http_access" //! })); //! //! // A general-purpose logging layer. //! let fmt_layer = tracing_subscriber::fmt::layer(); //! //! // Build a subscriber that combines the access log and stdout log //! // layers. //! tracing_subscriber::registry() //! .with(fmt_layer) //! .with(access_log) //! .init(); //! ``` //! //! Multiple layers can have their own, separate per-layer filters. A span or //! event will be recorded if it is enabled by _any_ per-layer filter, but it //! will be skipped by the layers whose filters did not enable it. Building on //! the previous example: //! //! ``` //! use tracing_subscriber::{filter::{filter_fn, LevelFilter}, prelude::*}; //! //! let access_log = // ... //! # LevelFilter::INFO; //! let fmt_layer = tracing_subscriber::fmt::layer(); //! //! tracing_subscriber::registry() //! // Add the filter for the "http_access" target to the access //! // log layer, like before. //! .with(access_log.with_filter(filter_fn(|metadata| { //! metadata.target() == "http_access" //! }))) //! // Add a filter for spans and events with the INFO level //! // and below to the logging layer. //! .with(fmt_layer.with_filter(LevelFilter::INFO)) //! .init(); //! //! // Neither layer will observe this event //! tracing::debug!(does_anyone_care = false, "a tree fell in the forest"); //! //! // This event will be observed by the logging layer, but not //! // by the access log layer. //! tracing::warn!(dose_roentgen = %3.8, "not great, but not terrible"); //! //! // This event will be observed only by the access log layer. //! tracing::trace!(target: "http_access", "HTTP request started"); //! //! // Both layers will observe this event. //! tracing::error!(target: "http_access", "HTTP request failed with a very bad error!"); //! ``` //! //! A per-layer filter can be applied to multiple [`Layer`]s at a time, by //! combining them into a [`Layered`] layer using [`Layer::and_then`], and then //! calling [`Layer::with_filter`] on the resulting [`Layered`] layer. //! //! Consider the following: //! - `layer_a` and `layer_b`, which should only receive spans and events at //! the [`INFO`] [level] and above. //! - A third layer, `layer_c`, which should receive spans and events at //! the [`DEBUG`] [level] as well. //! The layers and filters would be composed thusly: //! //! ``` //! use tracing_subscriber::{filter::LevelFilter, prelude::*}; //! //! let layer_a = // ... //! # LevelFilter::INFO; //! let layer_b = // ... //! # LevelFilter::INFO; //! let layer_c = // ... //! # LevelFilter::INFO; //! //! let info_layers = layer_a //! // Combine `layer_a` and `layer_b` into a `Layered` layer: //! .and_then(layer_b) //! // ...and then add an `INFO` `LevelFilter` to that layer: //! .with_filter(LevelFilter::INFO); //! //! tracing_subscriber::registry() //! // Add `layer_c` with a `DEBUG` filter. //! .with(layer_c.with_filter(LevelFilter::DEBUG)) //! .with(info_layers) //! .init(); //!``` //! //! If a [`Filtered`] [`Layer`] is combined with another [`Layer`] //! [`Layer::and_then`], and a filter is added to the [`Layered`] layer, that //! layer will be filtered by *both* the inner filter and the outer filter. //! Only spans and events that are enabled by *both* filters will be //! observed by that layer. This can be used to implement complex filtering //! trees. //! //! As an example, consider the following constraints: //! - Suppose that a particular [target] is used to indicate events that //! should be counted as part of a metrics system, which should be only //! observed by a layer that collects metrics. //! - A log of high-priority events ([`INFO`] and above) should be logged //! to stdout, while more verbose events should be logged to a debugging log file. //! - Metrics-focused events should *not* be included in either log output. //! //! In that case, it is possible to apply a filter to both logging layers to //! exclude the metrics events, while additionally adding a [`LevelFilter`] //! to the stdout log: //! //! ``` //! # // wrap this in a function so we don't actually create `debug.log` when //! # // running the doctests.. //! # fn docs() -> Result<(), Box> { //! use tracing_subscriber::{filter, prelude::*}; //! use std::{fs::File, sync::Arc}; //! //! // A layer that logs events to stdout using the human-readable "pretty" //! // format. //! let stdout_log = tracing_subscriber::fmt::layer() //! .pretty(); //! //! // A layer that logs events to a file. //! let file = File::create("debug.log")?; //! let debug_log = tracing_subscriber::fmt::layer() //! .with_writer(Arc::new(file)); //! //! // A layer that collects metrics using specific events. //! let metrics_layer = /* ... */ filter::LevelFilter::INFO; //! //! tracing_subscriber::registry() //! .with( //! stdout_log //! // Add an `INFO` filter to the stdout logging layer //! .with_filter(filter::LevelFilter::INFO) //! // Combine the filtered `stdout_log` layer with the //! // `debug_log` layer, producing a new `Layered` layer. //! .and_then(debug_log) //! // Add a filter to *both* layers that rejects spans and //! // events whose targets start with `metrics`. //! .with_filter(filter::filter_fn(|metadata| { //! !metadata.target().starts_with("metrics") //! })) //! ) //! .with( //! // Add a filter to the metrics label that *only* enables //! // events whose targets start with `metrics`. //! metrics_layer.with_filter(filter::filter_fn(|metadata| { //! metadata.target().starts_with("metrics") //! })) //! ) //! .init(); //! //! // This event will *only* be recorded by the metrics layer. //! tracing::info!(target: "metrics::cool_stuff_count", value = 42); //! //! // This event will only be seen by the debug log file layer: //! tracing::debug!("this is a message, and part of a system of messages"); //! //! // This event will be seen by both the stdout log layer *and* //! // the debug log file layer, but not by the metrics layer. //! tracing::warn!("the message is a warning about danger!"); //! # Ok(()) } //! ``` //! //! [`Subscriber`]: tracing_core::subscriber::Subscriber //! [span IDs]: tracing_core::span::Id //! [the current span]: Context::current_span //! [`register_callsite`]: Layer::register_callsite //! [`enabled`]: Layer::enabled //! [`event_enabled`]: Layer::event_enabled //! [`on_enter`]: Layer::on_enter //! [`Layer::register_callsite`]: Layer::register_callsite //! [`Layer::enabled`]: Layer::enabled //! [`Interest::never()`]: tracing_core::subscriber::Interest::never() //! [`Filtered`]: crate::filter::Filtered //! [`filter`]: crate::filter //! [`Targets`]: crate::filter::Targets //! [`FilterFn`]: crate::filter::FilterFn //! [`DynFilterFn`]: crate::filter::DynFilterFn //! [level]: tracing_core::Level //! [`INFO`]: tracing_core::Level::INFO //! [`DEBUG`]: tracing_core::Level::DEBUG //! [target]: tracing_core::Metadata::target //! [`LevelFilter`]: crate::filter::LevelFilter //! [feat]: crate#feature-flags use crate::filter; use tracing_core::{ metadata::Metadata, span, subscriber::{Interest, Subscriber}, Dispatch, Event, LevelFilter, }; use core::any::TypeId; feature! { #![feature = "alloc"] use alloc::boxed::Box; use core::ops::{Deref, DerefMut}; } mod context; mod layered; pub use self::{context::*, layered::*}; // The `tests` module is `pub(crate)` because it contains test utilities used by // other modules. #[cfg(test)] pub(crate) mod tests; /// A composable handler for `tracing` events. /// /// A `Layer` implements a behavior for recording or collecting traces that can /// be composed together with other `Layer`s to build a [`Subscriber`]. See the /// [module-level documentation](crate::layer) for details. /// /// [`Subscriber`]: tracing_core::Subscriber #[cfg_attr(docsrs, doc(notable_trait))] pub trait Layer where S: Subscriber, Self: 'static, { /// Performs late initialization when installing this layer as a /// [`Subscriber`]. /// /// ## Avoiding Memory Leaks /// /// `Layer`s should not store the [`Dispatch`] pointing to the [`Subscriber`] /// that they are a part of. Because the `Dispatch` owns the `Subscriber`, /// storing the `Dispatch` within the `Subscriber` will create a reference /// count cycle, preventing the `Dispatch` from ever being dropped. /// /// Instead, when it is necessary to store a cyclical reference to the /// `Dispatch` within a `Layer`, use [`Dispatch::downgrade`] to convert a /// `Dispatch` into a [`WeakDispatch`]. This type is analogous to /// [`std::sync::Weak`], and does not create a reference count cycle. A /// [`WeakDispatch`] can be stored within a subscriber without causing a /// memory leak, and can be [upgraded] into a `Dispatch` temporarily when /// the `Dispatch` must be accessed by the subscriber. /// /// [`WeakDispatch`]: tracing_core::dispatcher::WeakDispatch /// [upgraded]: tracing_core::dispatcher::WeakDispatch::upgrade /// [`Subscriber`]: tracing_core::Subscriber fn on_register_dispatch(&self, subscriber: &Dispatch) { let _ = subscriber; } /// Performs late initialization when attaching a `Layer` to a /// [`Subscriber`]. /// /// This is a callback that is called when the `Layer` is added to a /// [`Subscriber`] (e.g. in [`Layer::with_subscriber`] and /// [`SubscriberExt::with`]). Since this can only occur before the /// [`Subscriber`] has been set as the default, both the `Layer` and /// [`Subscriber`] are passed to this method _mutably_. This gives the /// `Layer` the opportunity to set any of its own fields with values /// recieved by method calls on the [`Subscriber`]. /// /// For example, [`Filtered`] layers implement `on_layer` to call the /// [`Subscriber`]'s [`register_filter`] method, and store the returned /// [`FilterId`] as a field. /// /// **Note** In most cases, `Layer` implementations will not need to /// implement this method. However, in cases where a type implementing /// `Layer` wraps one or more other types that implement `Layer`, like the /// [`Layered`] and [`Filtered`] types in this crate, that type MUST ensure /// that the inner `Layer`s' `on_layer` methods are called. Otherwise, /// functionality that relies on `on_layer`, such as [per-layer filtering], /// may not work correctly. /// /// [`Filtered`]: crate::filter::Filtered /// [`register_filter`]: crate::registry::LookupSpan::register_filter /// [per-layer filtering]: #per-layer-filtering /// [`FilterId`]: crate::filter::FilterId fn on_layer(&mut self, subscriber: &mut S) { let _ = subscriber; } /// Registers a new callsite with this layer, returning whether or not /// the layer is interested in being notified about the callsite, similarly /// to [`Subscriber::register_callsite`]. /// /// By default, this returns [`Interest::always()`] if [`self.enabled`] returns /// true, or [`Interest::never()`] if it returns false. /// ///
    /// Note: This method (and 
    /// Layer::enabled) determine whether a span or event is
    /// globally enabled, not whether the individual layer will be
    /// notified about that span or event. This is intended to be used
    /// by layers that implement filtering for the entire stack. Layers which do
    /// not wish to be notified about certain spans or events but do not wish to
    /// globally disable them should ignore those spans or events in their
    /// on_event,
    /// on_enter,
    /// on_exit, and other notification
    /// methods.
    /// 
/// /// See [the trait-level documentation] for more information on filtering /// with `Layer`s. /// /// Layers may also implement this method to perform any behaviour that /// should be run once per callsite. If the layer wishes to use /// `register_callsite` for per-callsite behaviour, but does not want to /// globally enable or disable those callsites, it should always return /// [`Interest::always()`]. /// /// [`Interest`]: tracing_core::Interest /// [`Subscriber::register_callsite`]: tracing_core::Subscriber::register_callsite() /// [`Interest::never()`]: tracing_core::subscriber::Interest::never() /// [`Interest::always()`]: tracing_core::subscriber::Interest::always() /// [`self.enabled`]: Layer::enabled() /// [`Layer::enabled`]: Layer::enabled() /// [`on_event`]: Layer::on_event() /// [`on_enter`]: Layer::on_enter() /// [`on_exit`]: Layer::on_exit() /// [the trait-level documentation]: #filtering-with-layers fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { if self.enabled(metadata, Context::none()) { Interest::always() } else { Interest::never() } } /// Returns `true` if this layer is interested in a span or event with the /// given `metadata` in the current [`Context`], similarly to /// [`Subscriber::enabled`]. /// /// By default, this always returns `true`, allowing the wrapped subscriber /// to choose to disable the span. /// ///
    /// Note: This method (and 
    /// Layer::register_callsite) determine whether a span or event is
    /// globally enabled, not whether the individual layer will be
    /// notified about that span or event. This is intended to be used
    /// by layers that implement filtering for the entire stack. Layers which do
    /// not wish to be notified about certain spans or events but do not wish to
    /// globally disable them should ignore those spans or events in their
    /// on_event,
    /// on_enter,
    /// on_exit, and other notification
    /// methods.
    /// 
/// /// /// See [the trait-level documentation] for more information on filtering /// with `Layer`s. /// /// [`Interest`]: tracing_core::Interest /// [`Subscriber::enabled`]: tracing_core::Subscriber::enabled() /// [`Layer::register_callsite`]: Layer::register_callsite() /// [`on_event`]: Layer::on_event() /// [`on_enter`]: Layer::on_enter() /// [`on_exit`]: Layer::on_exit() /// [the trait-level documentation]: #filtering-with-layers fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { let _ = (metadata, ctx); true } /// Notifies this layer that a new span was constructed with the given /// `Attributes` and `Id`. fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { let _ = (attrs, id, ctx); } // TODO(eliza): do we want this to be a public API? If we end up moving // filtering layers to a separate trait, we may no longer want `Layer`s to // be able to participate in max level hinting... #[doc(hidden)] fn max_level_hint(&self) -> Option { None } /// Notifies this layer that a span with the given `Id` recorded the given /// `values`. // Note: it's unclear to me why we'd need the current span in `record` (the // only thing the `Context` type currently provides), but passing it in anyway // seems like a good future-proofing measure as it may grow other methods later... fn on_record(&self, _span: &span::Id, _values: &span::Record<'_>, _ctx: Context<'_, S>) {} /// Notifies this layer that a span with the ID `span` recorded that it /// follows from the span with the ID `follows`. // Note: it's unclear to me why we'd need the current span in `record` (the // only thing the `Context` type currently provides), but passing it in anyway // seems like a good future-proofing measure as it may grow other methods later... fn on_follows_from(&self, _span: &span::Id, _follows: &span::Id, _ctx: Context<'_, S>) {} /// Called before [`on_event`], to determine if `on_event` should be called. /// ///
///
    ///
    /// **Note**: This method determines whether an event is globally enabled,
    /// *not* whether the individual `Layer` will be notified about the
    /// event. This is intended to be used by `Layer`s that implement
    /// filtering for the entire stack. `Layer`s 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].
    ///
    /// 
/// /// See [the trait-level documentation] for more information on filtering /// with `Layer`s. /// /// [`on_event`]: Self::on_event /// [`Interest`]: tracing_core::Interest /// [the trait-level documentation]: #filtering-with-layers #[inline] // collapse this to a constant please mrs optimizer fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, S>) -> bool { true } /// Notifies this layer that an event has occurred. fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {} /// Notifies this layer that a span with the given ID was entered. fn on_enter(&self, _id: &span::Id, _ctx: Context<'_, S>) {} /// Notifies this layer that the span with the given ID was exited. fn on_exit(&self, _id: &span::Id, _ctx: Context<'_, S>) {} /// Notifies this layer that the span with the given ID has been closed. fn on_close(&self, _id: span::Id, _ctx: Context<'_, S>) {} /// Notifies this layer that a span ID has been cloned, and that the /// subscriber returned a different ID. fn on_id_change(&self, _old: &span::Id, _new: &span::Id, _ctx: Context<'_, S>) {} /// Composes this layer around the given `Layer`, returning a `Layered` /// struct implementing `Layer`. /// /// The returned `Layer` will call the methods on this `Layer` and then /// those of the new `Layer`, before calling the methods on the subscriber /// it wraps. For example: /// /// ```rust /// # use tracing_subscriber::layer::Layer; /// # use tracing_core::Subscriber; /// pub struct FooLayer { /// // ... /// } /// /// pub struct BarLayer { /// // ... /// } /// /// pub struct MySubscriber { /// // ... /// } /// /// impl Layer for FooLayer { /// // ... /// } /// /// impl Layer for BarLayer { /// // ... /// } /// /// # impl FooLayer { /// # fn new() -> Self { Self {} } /// # } /// # impl BarLayer { /// # fn new() -> Self { Self { }} /// # } /// # impl MySubscriber { /// # fn new() -> Self { Self { }} /// # } /// # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event}; /// # impl tracing_core::Subscriber for MySubscriber { /// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) } /// # fn record(&self, _: &Id, _: &Record) {} /// # fn event(&self, _: &Event) {} /// # fn record_follows_from(&self, _: &Id, _: &Id) {} /// # fn enabled(&self, _: &Metadata) -> bool { false } /// # fn enter(&self, _: &Id) {} /// # fn exit(&self, _: &Id) {} /// # } /// let subscriber = FooLayer::new() /// .and_then(BarLayer::new()) /// .with_subscriber(MySubscriber::new()); /// ``` /// /// Multiple layers may be composed in this manner: /// /// ```rust /// # use tracing_subscriber::layer::Layer; /// # use tracing_core::Subscriber; /// # pub struct FooLayer {} /// # pub struct BarLayer {} /// # pub struct MySubscriber {} /// # impl Layer for FooLayer {} /// # impl Layer for BarLayer {} /// # impl FooLayer { /// # fn new() -> Self { Self {} } /// # } /// # impl BarLayer { /// # fn new() -> Self { Self { }} /// # } /// # impl MySubscriber { /// # fn new() -> Self { Self { }} /// # } /// # use tracing_core::{span::{Id, Attributes, Record}, Metadata, Event}; /// # impl tracing_core::Subscriber for MySubscriber { /// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(1) } /// # fn record(&self, _: &Id, _: &Record) {} /// # fn event(&self, _: &Event) {} /// # fn record_follows_from(&self, _: &Id, _: &Id) {} /// # fn enabled(&self, _: &Metadata) -> bool { false } /// # fn enter(&self, _: &Id) {} /// # fn exit(&self, _: &Id) {} /// # } /// pub struct BazLayer { /// // ... /// } /// /// impl Layer for BazLayer { /// // ... /// } /// # impl BazLayer { fn new() -> Self { BazLayer {} } } /// /// let subscriber = FooLayer::new() /// .and_then(BarLayer::new()) /// .and_then(BazLayer::new()) /// .with_subscriber(MySubscriber::new()); /// ``` fn and_then(self, layer: L) -> Layered where L: Layer, Self: Sized, { let inner_has_layer_filter = filter::layer_has_plf(&self); Layered::new(layer, self, inner_has_layer_filter) } /// Composes this `Layer` with the given [`Subscriber`], returning a /// `Layered` struct that implements [`Subscriber`]. /// /// The returned `Layered` subscriber will call the methods on this `Layer` /// and then those of the wrapped subscriber. /// /// For example: /// ```rust /// # use tracing_subscriber::layer::Layer; /// # use tracing_core::Subscriber; /// pub struct FooLayer { /// // ... /// } /// /// pub struct MySubscriber { /// // ... /// } /// /// impl Layer for FooLayer { /// // ... /// } /// /// # impl FooLayer { /// # fn new() -> Self { Self {} } /// # } /// # impl MySubscriber { /// # fn new() -> Self { Self { }} /// # } /// # use tracing_core::{span::{Id, Attributes, Record}, Metadata}; /// # impl tracing_core::Subscriber for MySubscriber { /// # fn new_span(&self, _: &Attributes) -> Id { Id::from_u64(0) } /// # fn record(&self, _: &Id, _: &Record) {} /// # fn event(&self, _: &tracing_core::Event) {} /// # fn record_follows_from(&self, _: &Id, _: &Id) {} /// # fn enabled(&self, _: &Metadata) -> bool { false } /// # fn enter(&self, _: &Id) {} /// # fn exit(&self, _: &Id) {} /// # } /// let subscriber = FooLayer::new() /// .with_subscriber(MySubscriber::new()); ///``` /// /// [`Subscriber`]: tracing_core::Subscriber fn with_subscriber(mut self, mut inner: S) -> Layered where Self: Sized, { let inner_has_layer_filter = filter::subscriber_has_plf(&inner); self.on_layer(&mut inner); Layered::new(self, inner, inner_has_layer_filter) } /// Combines `self` with a [`Filter`], returning a [`Filtered`] layer. /// /// The [`Filter`] will control which spans and events are enabled for /// this layer. See [the trait-level documentation][plf] for details on /// per-layer filtering. /// /// [`Filtered`]: crate::filter::Filtered /// [plf]: crate::layer#per-layer-filtering #[cfg(all(feature = "registry", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] fn with_filter(self, filter: F) -> filter::Filtered where Self: Sized, F: Filter, { filter::Filtered::new(self, filter) } /// Erases the type of this [`Layer`], returning a [`Box`]ed `dyn /// Layer` trait object. /// /// This can be used when a function returns a `Layer` which may be of /// one of several types, or when a `Layer` subscriber has a very long type /// signature. /// /// # Examples /// /// The following example will *not* compile, because the value assigned to /// `log_layer` may have one of several different types: /// /// ```compile_fail /// # fn main() -> Result<(), Box> { /// use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*}; /// use std::{path::PathBuf, fs::File, io}; /// /// /// Configures whether logs are emitted to a file, to stdout, or to stderr. /// pub enum LogConfig { /// File(PathBuf), /// Stdout, /// Stderr, /// } /// /// let config = // ... /// # LogConfig::Stdout; /// /// // Depending on the config, construct a layer of one of several types. /// let log_layer = match config { /// // If logging to a file, use a maximally-verbose configuration. /// LogConfig::File(path) => { /// let file = File::create(path)?; /// tracing_subscriber::fmt::layer() /// .with_thread_ids(true) /// .with_thread_names(true) /// // Selecting the JSON logging format changes the layer's /// // type. /// .json() /// .with_span_list(true) /// // Setting the writer to use our log file changes the /// // layer's type again. /// .with_writer(file) /// }, /// /// // If logging to stdout, use a pretty, human-readable configuration. /// LogConfig::Stdout => tracing_subscriber::fmt::layer() /// // Selecting the "pretty" logging format changes the /// // layer's type! /// .pretty() /// .with_writer(io::stdout) /// // Add a filter based on the RUST_LOG environment variable; /// // this changes the type too! /// .and_then(tracing_subscriber::EnvFilter::from_default_env()), /// /// // If logging to stdout, only log errors and warnings. /// LogConfig::Stderr => tracing_subscriber::fmt::layer() /// // Changing the writer changes the layer's type /// .with_writer(io::stderr) /// // Only log the `WARN` and `ERROR` levels. Adding a filter /// // changes the layer's type to `Filtered`. /// .with_filter(LevelFilter::WARN), /// }; /// /// tracing_subscriber::registry() /// .with(log_layer) /// .init(); /// # Ok(()) } /// ``` /// /// However, adding a call to `.boxed()` after each match arm erases the /// layer's type, so this code *does* compile: /// /// ``` /// # fn main() -> Result<(), Box> { /// # use tracing_subscriber::{Layer, filter::LevelFilter, prelude::*}; /// # use std::{path::PathBuf, fs::File, io}; /// # pub enum LogConfig { /// # File(PathBuf), /// # Stdout, /// # Stderr, /// # } /// # let config = LogConfig::Stdout; /// let log_layer = match config { /// LogConfig::File(path) => { /// let file = File::create(path)?; /// tracing_subscriber::fmt::layer() /// .with_thread_ids(true) /// .with_thread_names(true) /// .json() /// .with_span_list(true) /// .with_writer(file) /// // Erase the type by boxing the layer /// .boxed() /// }, /// /// LogConfig::Stdout => tracing_subscriber::fmt::layer() /// .pretty() /// .with_writer(io::stdout) /// .and_then(tracing_subscriber::EnvFilter::from_default_env()) /// // Erase the type by boxing the layer /// .boxed(), /// /// LogConfig::Stderr => tracing_subscriber::fmt::layer() /// .with_writer(io::stderr) /// .with_filter(LevelFilter::WARN) /// // Erase the type by boxing the layer /// .boxed(), /// }; /// /// tracing_subscriber::registry() /// .with(log_layer) /// .init(); /// # Ok(()) } /// ``` #[cfg(any(feature = "alloc", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "alloc", feature = "std"))))] fn boxed(self) -> Box + Send + Sync + 'static> where Self: Sized, Self: Layer + Send + Sync + 'static, S: Subscriber, { Box::new(self) } #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { if id == TypeId::of::() { Some(self as *const _ as *const ()) } else { None } } } feature! { #![all(feature = "registry", feature = "std")] /// A per-[`Layer`] filter that determines whether a span or event is enabled /// for an individual layer. /// /// See [the module-level documentation][plf] for details on using [`Filter`]s. /// /// [plf]: crate::layer#per-layer-filtering #[cfg_attr(docsrs, doc(notable_trait))] pub trait Filter { /// Returns `true` if this layer is interested in a span or event with the /// given [`Metadata`] in the current [`Context`], similarly to /// [`Subscriber::enabled`]. /// /// If this returns `false`, the span or event will be disabled _for the /// wrapped [`Layer`]_. Unlike [`Layer::enabled`], the span or event will /// still be recorded if any _other_ layers choose to enable it. However, /// the layer [filtered] by this filter will skip recording that span or /// event. /// /// If all layers indicate that they do not wish to see this span or event, /// it will be disabled. /// /// [`metadata`]: tracing_core::Metadata /// [`Subscriber::enabled`]: tracing_core::Subscriber::enabled /// [filtered]: crate::filter::Filtered fn enabled(&self, meta: &Metadata<'_>, cx: &Context<'_, S>) -> bool; /// Returns an [`Interest`] indicating whether this layer will [always], /// [sometimes], or [never] be interested in the given [`Metadata`]. /// /// When a given callsite will [always] or [never] be enabled, the results /// of evaluating the filter may be cached for improved performance. /// Therefore, if a filter is capable of determining that it will always or /// never enable a particular callsite, providing an implementation of this /// function is recommended. /// ///
        /// 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
        /// occurs within a particular span), it must return
        /// Interest::sometimes() from this method. If it returns
        /// Interest::always() or Interest::never(), the
        /// enabled method may not be called when a particular instance
        /// of that span or event is recorded.
        /// 
/// /// This method is broadly similar to [`Subscriber::register_callsite`]; /// however, since the returned value represents only the interest of /// *this* layer, the resulting behavior is somewhat different. /// /// If a [`Subscriber`] returns [`Interest::always()`][always] or /// [`Interest::never()`][never] for a given [`Metadata`], its [`enabled`] /// method is then *guaranteed* to never be called for that callsite. On the /// other hand, when a `Filter` returns [`Interest::always()`][always] or /// [`Interest::never()`][never] for a callsite, _other_ [`Layer`]s may have /// differing interests in that callsite. If this is the case, the callsite /// will recieve [`Interest::sometimes()`][sometimes], and the [`enabled`] /// method will still be called for that callsite when it records a span or /// event. /// /// Returning [`Interest::always()`][always] or [`Interest::never()`][never] from /// `Filter::callsite_enabled` will permanently enable or disable a /// callsite (without requiring subsequent calls to [`enabled`]) if and only /// if the following is true: /// /// - all [`Layer`]s that comprise the subscriber include `Filter`s /// (this includes a tree of [`Layered`] layers that share the same /// `Filter`) /// - all those `Filter`s return the same [`Interest`]. /// /// For example, if a [`Subscriber`] consists of two [`Filtered`] layers, /// and both of those layers return [`Interest::never()`][never], that /// callsite *will* never be enabled, and the [`enabled`] methods of those /// [`Filter`]s will not be called. /// /// ## Default Implementation /// /// The default implementation of this method assumes that the /// `Filter`'s [`enabled`] method _may_ perform dynamic filtering, and /// returns [`Interest::sometimes()`][sometimes], to ensure that [`enabled`] /// is called to determine whether a particular _instance_ of the callsite /// is enabled in the current context. If this is *not* the case, and the /// `Filter`'s [`enabled`] method will always return the same result /// for a particular [`Metadata`], this method can be overridden as /// follows: /// /// ``` /// use tracing_subscriber::layer; /// use tracing_core::{Metadata, subscriber::Interest}; /// /// struct MyFilter { /// // ... /// } /// /// impl MyFilter { /// // The actual logic for determining whether a `Metadata` is enabled /// // must be factored out from the `enabled` method, so that it can be /// // called without a `Context` (which is not provided to the /// // `callsite_enabled` method). /// fn is_enabled(&self, metadata: &Metadata<'_>) -> bool { /// // ... /// # drop(metadata); true /// } /// } /// /// impl layer::Filter for MyFilter { /// fn enabled(&self, metadata: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { /// // Even though we are implementing `callsite_enabled`, we must still provide a /// // working implementation of `enabled`, as returning `Interest::always()` or /// // `Interest::never()` will *allow* caching, but will not *guarantee* it. /// // Other filters may still return `Interest::sometimes()`, so we may be /// // asked again in `enabled`. /// self.is_enabled(metadata) /// } /// /// fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { /// // The result of `self.enabled(metadata, ...)` will always be /// // the same for any given `Metadata`, so we can convert it into /// // an `Interest`: /// if self.is_enabled(metadata) { /// Interest::always() /// } else { /// Interest::never() /// } /// } /// } /// ``` /// /// [`Metadata`]: tracing_core::Metadata /// [`Interest`]: tracing_core::Interest /// [always]: tracing_core::Interest::always /// [sometimes]: tracing_core::Interest::sometimes /// [never]: tracing_core::Interest::never /// [`Subscriber::register_callsite`]: tracing_core::Subscriber::register_callsite /// [`Subscriber`]: tracing_core::Subscriber /// [`enabled`]: Filter::enabled /// [`Filtered`]: crate::filter::Filtered fn callsite_enabled(&self, meta: &'static Metadata<'static>) -> Interest { let _ = meta; Interest::sometimes() } /// Called before the filtered [`Layer]'s [`on_event`], to determine if /// `on_event` should be called. /// /// This gives a chance to filter events based on their fields. Note, /// however, that this *does not* override [`enabled`], and is not even /// called if [`enabled`] returns `false`. /// /// ## Default Implementation /// /// By default, this method returns `true`, indicating that no events are /// filtered out based on their fields. /// /// [`enabled`]: crate::layer::Filter::enabled /// [`on_event`]: crate::layer::Layer::on_event #[inline] // collapse this to a constant please mrs optimizer fn event_enabled(&self, event: &Event<'_>, cx: &Context<'_, S>) -> bool { let _ = (event, cx); true } /// Returns an optional hint of the highest [verbosity level][level] that /// this `Filter` will enable. /// /// If this method returns a [`LevelFilter`], it will be used as a hint to /// determine the most verbose level that will be enabled. This will allow /// spans and events which are more verbose than that level to be skipped /// more efficiently. An implementation of this method is optional, but /// strongly encouraged. /// /// If the maximum level the `Filter` will enable can change over the /// course of its lifetime, it is free to return a different value from /// 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. /// 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. /// /// ## Default Implementation /// /// By default, this method returns `None`, indicating that the maximum /// level is unknown. /// /// [level]: tracing_core::metadata::Level /// [`LevelFilter`]: crate::filter::LevelFilter /// [`Interest`]: tracing_core::subscriber::Interest /// [rebuild]: tracing_core::callsite::rebuild_interest_cache fn max_level_hint(&self) -> Option { None } /// Notifies this filter that a new span was constructed with the given /// `Attributes` and `Id`. /// /// By default, this method does nothing. `Filter` implementations that /// need to be notified when new spans are created can override this /// method. fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { let _ = (attrs, id, ctx); } /// Notifies this filter that a span with the given `Id` recorded the given /// `values`. /// /// By default, this method does nothing. `Filter` implementations that /// need to be notified when new spans are created can override this /// method. fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { let _ = (id, values, ctx); } /// Notifies this filter that a span with the given ID was entered. /// /// By default, this method does nothing. `Filter` implementations that /// need to be notified when a span is entered can override this method. fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { let _ = (id, ctx); } /// Notifies this filter that a span with the given ID was exited. /// /// By default, this method does nothing. `Filter` implementations that /// need to be notified when a span is exited can override this method. fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { let _ = (id, ctx); } /// Notifies this filter that a span with the given ID has been closed. /// /// By default, this method does nothing. `Filter` implementations that /// need to be notified when a span is closed can override this method. fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { let _ = (id, ctx); } } } /// Extension trait adding a `with(Layer)` combinator to `Subscriber`s. pub trait SubscriberExt: Subscriber + crate::sealed::Sealed { /// Wraps `self` with the provided `layer`. fn with(self, layer: L) -> Layered where L: Layer, Self: Sized, { layer.with_subscriber(self) } } /// A layer that does nothing. #[derive(Clone, Debug, Default)] pub struct Identity { _p: (), } // === impl Layer === #[derive(Clone, Copy)] pub(crate) struct NoneLayerMarker(()); static NONE_LAYER_MARKER: NoneLayerMarker = NoneLayerMarker(()); /// Is a type implementing `Layer` `Option::<_>::None`? pub(crate) fn layer_is_none(layer: &L) -> bool where L: Layer, S: Subscriber, { unsafe { // Safety: we're not actually *doing* anything with this pointer --- // this only care about the `Option`, which is essentially being used // as a bool. We can rely on the pointer being valid, because it is // a crate-private type, and is only returned by the `Layer` impl // for `Option`s. However, even if the layer *does* decide to be // evil and give us an invalid pointer here, that's fine, because we'll // never actually dereference it. layer.downcast_raw(TypeId::of::()) } .is_some() } /// Is a type implementing `Subscriber` `Option::<_>::None`? pub(crate) fn subscriber_is_none(subscriber: &S) -> bool where S: Subscriber, { unsafe { // Safety: we're not actually *doing* anything with this pointer --- // this only care about the `Option`, which is essentially being used // as a bool. We can rely on the pointer being valid, because it is // a crate-private type, and is only returned by the `Layer` impl // for `Option`s. However, even if the subscriber *does* decide to be // evil and give us an invalid pointer here, that's fine, because we'll // never actually dereference it. subscriber.downcast_raw(TypeId::of::()) } .is_some() } impl Layer for Option where L: Layer, S: Subscriber, { fn on_layer(&mut self, subscriber: &mut S) { if let Some(ref mut layer) = self { layer.on_layer(subscriber) } } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_new_span(attrs, id, ctx) } } #[inline] fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { match self { Some(ref inner) => inner.register_callsite(metadata), None => Interest::always(), } } #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { match self { Some(ref inner) => inner.enabled(metadata, ctx), None => true, } } #[inline] fn max_level_hint(&self) -> Option { match self { Some(ref inner) => inner.max_level_hint(), None => { // There is no inner layer, so this layer will // never enable anything. Some(LevelFilter::OFF) } } } #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_record(span, values, ctx); } } #[inline] fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_follows_from(span, follows, ctx); } } #[inline] fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { match self { Some(ref inner) => inner.event_enabled(event, ctx), None => true, } } #[inline] fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_event(event, ctx); } } #[inline] fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_enter(id, ctx); } } #[inline] fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_exit(id, ctx); } } #[inline] fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_close(id, ctx); } } #[inline] fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { if let Some(ref inner) = self { inner.on_id_change(old, new, ctx) } } #[doc(hidden)] #[inline] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { if id == TypeId::of::() { Some(self as *const _ as *const ()) } else if id == TypeId::of::() && self.is_none() { Some(&NONE_LAYER_MARKER as *const _ as *const ()) } else { self.as_ref().and_then(|inner| inner.downcast_raw(id)) } } } feature! { #![any(feature = "std", feature = "alloc")] #[cfg(not(feature = "std"))] use alloc::vec::Vec; macro_rules! layer_impl_body { () => { #[inline] fn on_register_dispatch(&self, subscriber: &Dispatch) { self.deref().on_register_dispatch(subscriber); } #[inline] fn on_layer(&mut self, subscriber: &mut S) { self.deref_mut().on_layer(subscriber); } #[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 register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { self.deref().register_callsite(metadata) } #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { self.deref().enabled(metadata, ctx) } #[inline] fn max_level_hint(&self) -> Option { self.deref().max_level_hint() } #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { self.deref().on_record(span, values, ctx) } #[inline] fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { self.deref().on_follows_from(span, follows, ctx) } #[inline] fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { self.deref().event_enabled(event, ctx) } #[inline] fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { self.deref().on_event(event, 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) } #[inline] fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: Context<'_, S>) { self.deref().on_id_change(old, new, ctx) } #[doc(hidden)] #[inline] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { self.deref().downcast_raw(id) } }; } impl Layer for Box where L: Layer, S: Subscriber, { layer_impl_body! {} } impl Layer for Box + Send + Sync> where S: Subscriber, { layer_impl_body! {} } impl Layer for Vec where L: Layer, S: Subscriber, { fn on_layer(&mut self, subscriber: &mut S) { for l in self { l.on_layer(subscriber); } } fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { // Return highest level of interest. let mut interest = Interest::never(); for l in self { let new_interest = l.register_callsite(metadata); if (interest.is_sometimes() && new_interest.is_always()) || (interest.is_never() && !new_interest.is_never()) { interest = new_interest; } } interest } fn enabled(&self, metadata: &Metadata<'_>, ctx: Context<'_, S>) -> bool { self.iter().all(|l| l.enabled(metadata, ctx.clone())) } fn event_enabled(&self, event: &Event<'_>, ctx: Context<'_, S>) -> bool { self.iter().all(|l| l.event_enabled(event, ctx.clone())) } fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) { for l in self { l.on_new_span(attrs, id, ctx.clone()); } } fn max_level_hint(&self) -> Option { // Default to `OFF` if there are no inner layers. let mut max_level = LevelFilter::OFF; for l in self { // NOTE(eliza): this is slightly subtle: if *any* layer // returns `None`, we have to return `None`, assuming there is // no max level hint, since that particular layer cannot // provide a hint. let hint = l.max_level_hint()?; max_level = core::cmp::max(hint, max_level); } Some(max_level) } fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) { for l in self { l.on_record(span, values, ctx.clone()) } } fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: Context<'_, S>) { for l in self { l.on_follows_from(span, follows, ctx.clone()); } } fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { for l in self { l.on_event(event, ctx.clone()); } } fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { for l in self { l.on_enter(id, ctx.clone()); } } fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) { for l in self { l.on_exit(id, ctx.clone()); } } fn on_close(&self, id: span::Id, ctx: Context<'_, S>) { for l in self { l.on_close(id.clone(), ctx.clone()); } } #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { // If downcasting to `Self`, return a pointer to `self`. if id == TypeId::of::() { return Some(self as *const _ as *const ()); } // Someone is looking for per-layer filters. But, this `Vec` // might contain layers with per-layer filters *and* // layers without filters. It should only be treated as a // per-layer-filtered layer if *all* its layers have // per-layer filters. // XXX(eliza): it's a bummer we have to do this linear search every // time. It would be nice if this could be cached, but that would // require replacing the `Vec` impl with an impl for a newtype... if filter::is_plf_downcast_marker(id) && self.iter().any(|s| s.downcast_raw(id).is_none()) { return None; } // Otherwise, return the first child of `self` that downcaasts to // the selected type, if any. // XXX(eliza): hope this is reasonable lol self.iter().find_map(|l| l.downcast_raw(id)) } } } // === impl SubscriberExt === impl crate::sealed::Sealed for S {} impl SubscriberExt for S {} // === impl Identity === impl Layer for Identity {} impl Identity { /// Returns a new `Identity` layer. pub fn new() -> Self { Self { _p: () } } } tracing-subscriber-0.3.18/src/layer/tests.rs000064400000000000000000000247701046102023000171320ustar 00000000000000use super::*; use tracing_core::subscriber::NoSubscriber; #[derive(Debug)] pub(crate) struct NopLayer; impl Layer for NopLayer {} #[allow(dead_code)] struct NopLayer2; impl Layer for NopLayer2 {} /// A layer that holds a string. /// /// Used to test that pointers returned by downcasting are actually valid. struct StringLayer(&'static str); impl Layer for StringLayer {} struct StringLayer2(&'static str); impl Layer for StringLayer2 {} struct StringLayer3(&'static str); impl Layer for StringLayer3 {} pub(crate) struct StringSubscriber(&'static str); impl Subscriber for StringSubscriber { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { Interest::never() } fn enabled(&self, _: &Metadata<'_>) -> bool { false } 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 assert_subscriber(_s: impl Subscriber) {} fn assert_layer(_l: &impl Layer) {} #[test] fn layer_is_subscriber() { let s = NopLayer.with_subscriber(NoSubscriber::default()); assert_subscriber(s) } #[test] fn two_layers_are_subscriber() { let s = NopLayer .and_then(NopLayer) .with_subscriber(NoSubscriber::default()); assert_subscriber(s) } #[test] fn three_layers_are_subscriber() { let s = NopLayer .and_then(NopLayer) .and_then(NopLayer) .with_subscriber(NoSubscriber::default()); assert_subscriber(s) } #[test] fn three_layers_are_layer() { let layers = NopLayer.and_then(NopLayer).and_then(NopLayer); assert_layer(&layers); let _ = layers.with_subscriber(NoSubscriber::default()); } #[test] #[cfg(feature = "alloc")] fn box_layer_is_layer() { use alloc::boxed::Box; let l: Box + Send + Sync> = Box::new(NopLayer); assert_layer(&l); l.with_subscriber(NoSubscriber::default()); } #[test] fn downcasts_to_subscriber() { let s = NopLayer .and_then(NopLayer) .and_then(NopLayer) .with_subscriber(StringSubscriber("subscriber")); let subscriber = ::downcast_ref::(&s).expect("subscriber should downcast"); assert_eq!(subscriber.0, "subscriber"); } #[test] fn downcasts_to_layer() { let s = StringLayer("layer_1") .and_then(StringLayer2("layer_2")) .and_then(StringLayer3("layer_3")) .with_subscriber(NoSubscriber::default()); let layer = ::downcast_ref::(&s).expect("layer 1 should downcast"); assert_eq!(layer.0, "layer_1"); let layer = ::downcast_ref::(&s).expect("layer 2 should downcast"); assert_eq!(layer.0, "layer_2"); let layer = ::downcast_ref::(&s).expect("layer 3 should downcast"); assert_eq!(layer.0, "layer_3"); } #[cfg(all(feature = "registry", feature = "std"))] mod registry_tests { use super::*; use crate::registry::LookupSpan; #[test] fn context_event_span() { use std::sync::{Arc, Mutex}; let last_event_span = Arc::new(Mutex::new(None)); struct RecordingLayer { last_event_span: Arc>>, } impl Layer for RecordingLayer where S: Subscriber + for<'lookup> LookupSpan<'lookup>, { fn on_event(&self, event: &Event<'_>, ctx: Context<'_, S>) { let span = ctx.event_span(event); *self.last_event_span.lock().unwrap() = span.map(|s| s.name()); } } tracing::subscriber::with_default( crate::registry().with(RecordingLayer { last_event_span: last_event_span.clone(), }), || { tracing::info!("no span"); assert_eq!(*last_event_span.lock().unwrap(), None); let parent = tracing::info_span!("explicit"); tracing::info!(parent: &parent, "explicit span"); assert_eq!(*last_event_span.lock().unwrap(), Some("explicit")); let _guard = tracing::info_span!("contextual").entered(); tracing::info!("contextual span"); assert_eq!(*last_event_span.lock().unwrap(), Some("contextual")); }, ); } /// Tests for how max-level hints are calculated when combining layers /// with and without per-layer filtering. mod max_level_hints { use super::*; use crate::filter::*; #[test] fn mixed_with_unfiltered() { let subscriber = crate::registry() .with(NopLayer) .with(NopLayer.with_filter(LevelFilter::INFO)); assert_eq!(subscriber.max_level_hint(), None); } #[test] fn mixed_with_unfiltered_layered() { let subscriber = crate::registry().with(NopLayer).with( NopLayer .with_filter(LevelFilter::INFO) .and_then(NopLayer.with_filter(LevelFilter::TRACE)), ); assert_eq!(dbg!(subscriber).max_level_hint(), None); } #[test] fn mixed_interleaved() { let subscriber = crate::registry() .with(NopLayer) .with(NopLayer.with_filter(LevelFilter::INFO)) .with(NopLayer) .with(NopLayer.with_filter(LevelFilter::INFO)); assert_eq!(dbg!(subscriber).max_level_hint(), None); } #[test] fn mixed_layered() { let subscriber = crate::registry() .with(NopLayer.with_filter(LevelFilter::INFO).and_then(NopLayer)) .with(NopLayer.and_then(NopLayer.with_filter(LevelFilter::INFO))); assert_eq!(dbg!(subscriber).max_level_hint(), None); } #[test] fn plf_only_unhinted() { let subscriber = crate::registry() .with(NopLayer.with_filter(LevelFilter::INFO)) .with(NopLayer.with_filter(filter_fn(|_| true))); assert_eq!(dbg!(subscriber).max_level_hint(), None); } #[test] fn plf_only_unhinted_nested_outer() { // if a nested tree of per-layer filters has an _outer_ filter with // no max level hint, it should return `None`. let subscriber = crate::registry() .with( NopLayer .with_filter(LevelFilter::INFO) .and_then(NopLayer.with_filter(LevelFilter::WARN)), ) .with( NopLayer .with_filter(filter_fn(|_| true)) .and_then(NopLayer.with_filter(LevelFilter::DEBUG)), ); assert_eq!(dbg!(subscriber).max_level_hint(), None); } #[test] fn plf_only_unhinted_nested_inner() { // If a nested tree of per-layer filters has an _inner_ filter with // no max-level hint, but the _outer_ filter has a max level hint, // it should pick the outer hint. This is because the outer filter // will disable the spans/events before they make it to the inner // filter. let subscriber = dbg!(crate::registry().with( NopLayer .with_filter(filter_fn(|_| true)) .and_then(NopLayer.with_filter(filter_fn(|_| true))) .with_filter(LevelFilter::INFO), )); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); } #[test] fn unhinted_nested_inner() { let subscriber = dbg!(crate::registry() .with(NopLayer.and_then(NopLayer).with_filter(LevelFilter::INFO)) .with( NopLayer .with_filter(filter_fn(|_| true)) .and_then(NopLayer.with_filter(filter_fn(|_| true))) .with_filter(LevelFilter::WARN), )); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); } #[test] fn unhinted_nested_inner_mixed() { let subscriber = dbg!(crate::registry() .with( NopLayer .and_then(NopLayer.with_filter(filter_fn(|_| true))) .with_filter(LevelFilter::INFO) ) .with( NopLayer .with_filter(filter_fn(|_| true)) .and_then(NopLayer.with_filter(filter_fn(|_| true))) .with_filter(LevelFilter::WARN), )); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::INFO)); } #[test] fn plf_only_picks_max() { let subscriber = crate::registry() .with(NopLayer.with_filter(LevelFilter::WARN)) .with(NopLayer.with_filter(LevelFilter::DEBUG)); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); } #[test] fn many_plf_only_picks_max() { let subscriber = crate::registry() .with(NopLayer.with_filter(LevelFilter::WARN)) .with(NopLayer.with_filter(LevelFilter::DEBUG)) .with(NopLayer.with_filter(LevelFilter::INFO)) .with(NopLayer.with_filter(LevelFilter::ERROR)); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); } #[test] fn nested_plf_only_picks_max() { let subscriber = crate::registry() .with( NopLayer.with_filter(LevelFilter::INFO).and_then( NopLayer .with_filter(LevelFilter::WARN) .and_then(NopLayer.with_filter(LevelFilter::DEBUG)), ), ) .with( NopLayer .with_filter(LevelFilter::INFO) .and_then(NopLayer.with_filter(LevelFilter::ERROR)), ); assert_eq!(dbg!(subscriber).max_level_hint(), Some(LevelFilter::DEBUG)); } } } tracing-subscriber-0.3.18/src/lib.rs000064400000000000000000000223251046102023000154140ustar 00000000000000//! Utilities for implementing and composing [`tracing`] subscribers. //! //! [`tracing`] is a framework for instrumenting Rust programs to collect //! scoped, structured, and async-aware diagnostics. The [`Subscriber`] trait //! represents the functionality necessary to collect this trace data. This //! crate contains tools for composing subscribers out of smaller units of //! behaviour, and batteries-included implementations of common subscriber //! functionality. //! //! `tracing-subscriber` is intended for use by both `Subscriber` authors and //! application authors using `tracing` to instrument their applications. //! //! *Compiler support: [requires `rustc` 1.63+][msrv]* //! //! [msrv]: #supported-rust-versions //! //! ## `Layer`s and `Filter`s //! //! The most important component of the `tracing-subscriber` API is the //! [`Layer`] trait, which provides a composable abstraction for building //! [`Subscriber`]s. Like the [`Subscriber`] trait, a [`Layer`] defines a //! particular behavior for collecting trace data. Unlike [`Subscriber`]s, //! which implement a *complete* strategy for how trace data is collected, //! [`Layer`]s provide *modular* implementations of specific behaviors. //! Therefore, they can be [composed together] to form a [`Subscriber`] which is //! capable of recording traces in a variety of ways. See the [`layer` module's //! documentation][layer] for details on using [`Layer`]s. //! //! In addition, the [`Filter`] trait defines an interface for filtering what //! spans and events are recorded by a particular layer. This allows different //! [`Layer`]s to handle separate subsets of the trace data emitted by a //! program. See the [documentation on per-layer filtering][plf] for more //! information on using [`Filter`]s. //! //! [`Layer`]: crate::layer::Layer //! [composed together]: crate::layer#composing-layers //! [layer]: crate::layer //! [`Filter`]: crate::layer::Filter //! [plf]: crate::layer#per-layer-filtering //! //! ## Included Subscribers //! //! The following `Subscriber`s are provided for application authors: //! //! - [`fmt`] - Formats and logs tracing data (requires the `fmt` feature flag) //! //! ## Feature Flags //! //! - `std`: Enables APIs that depend on the Rust standard library //! (enabled by default). //! - `alloc`: Depend on [`liballoc`] (enabled by "std"). //! - `env-filter`: Enables the [`EnvFilter`] type, which implements filtering //! 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 "registry" and "std"**. //! - `ansi`: Enables `fmt` support for ANSI terminal colors. Enabled by //! default. //! - `registry`: enables the [`registry`] module. Enabled by default. //! **Requires "std"**. //! - `json`: Enables `fmt` support for JSON output. In JSON output, the ANSI //! feature does nothing. **Requires "fmt" and "std"**. //! - `local-time`: Enables local time formatting when using the [`time` //! crate]'s timestamp formatters with the `fmt` subscriber. //! //! [`registry`]: mod@registry //! //! ### Optional Dependencies //! //! - [`tracing-log`]: Enables better formatting for events emitted by `log` //! macros in the `fmt` subscriber. Enabled by default. //! - [`time`][`time` crate]: Enables support for using the [`time` crate] for timestamp //! formatting in the `fmt` subscriber. //! - [`smallvec`]: Causes the `EnvFilter` type to use the `smallvec` crate (rather //! than `Vec`) as a performance optimization. Enabled by default. //! - [`parking_lot`]: Use the `parking_lot` crate's `RwLock` implementation //! rather than the Rust standard library's implementation. //! //! ### `no_std` Support //! //! In embedded systems and other bare-metal applications, `tracing` can be //! used without requiring the Rust standard library, although some features are //! disabled. Although most of the APIs provided by `tracing-subscriber`, such //! as [`fmt`] and [`EnvFilter`], require the standard library, some //! functionality, such as the [`Layer`] trait, can still be used in //! `no_std` environments. //! //! The dependency on the standard library is controlled by two crate feature //! flags, "std", which enables the dependency on [`libstd`], and "alloc", which //! enables the dependency on [`liballoc`] (and is enabled by the "std" //! feature). These features are enabled by default, but `no_std` users can //! disable them using: //! //! ```toml //! # Cargo.toml //! tracing-subscriber = { version = "0.3", default-features = false } //! ``` //! //! Additional APIs are available when [`liballoc`] is available. To enable //! `liballoc` but not `std`, use: //! //! ```toml //! # Cargo.toml //! tracing-subscriber = { version = "0.3", default-features = false, features = ["alloc"] } //! ``` //! //! ### Unstable Features //! //! These feature flags enable **unstable** features. The public API may break in 0.1.x //! releases. To enable these features, the `--cfg tracing_unstable` must be passed to //! `rustc` when compiling. //! //! The following unstable feature flags are currently available: //! //! * `valuable`: Enables support for serializing values recorded using the //! [`valuable`] crate as structured JSON in the [`format::Json`] formatter. //! //! #### Enabling Unstable Features //! //! The easiest way to set the `tracing_unstable` cfg is to use the `RUSTFLAGS` //! env variable when running `cargo` commands: //! //! ```shell //! RUSTFLAGS="--cfg tracing_unstable" cargo build //! ``` //! Alternatively, the following can be added to the `.cargo/config` file in a //! project to automatically enable the cfg flag for that project: //! //! ```toml //! [build] //! rustflags = ["--cfg", "tracing_unstable"] //! ``` //! //! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section //! [`valuable`]: https://crates.io/crates/valuable //! [`format::Json`]: crate::fmt::format::Json //! //! ## Supported Rust Versions //! //! Tracing is built against the latest stable release. The minimum supported //! version is 1.63. The current Tracing version is not guaranteed to build on //! Rust 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.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. //! //! [`Subscriber`]: tracing_core::subscriber::Subscriber //! [`tracing`]: https://docs.rs/tracing/latest/tracing //! [`EnvFilter`]: filter::EnvFilter //! [`fmt`]: mod@fmt //! [`tracing-log`]: https://crates.io/crates/tracing-log //! [`smallvec`]: https://crates.io/crates/smallvec //! [`env_logger` crate]: https://crates.io/crates/env_logger //! [`parking_lot`]: https://crates.io/crates/parking_lot //! [`time` crate]: https://crates.io/crates/time //! [`liballoc`]: https://doc.rust-lang.org/alloc/index.html //! [`libstd`]: https://doc.rust-lang.org/std/index.html #![doc( html_logo_url = "https://raw.githubusercontent.com/tokio-rs/tracing/master/assets/logo-type.png", 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), // 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), )] #![warn( missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub, bad_style, dead_code, improper_ctypes, non_shorthand_field_patterns, no_mangle_generic_items, overflowing_literals, path_statements, patterns_in_fns_without_body, private_in_public, unconditional_recursion, unused, unused_allocation, unused_comparisons, unused_parens, while_true )] // Using struct update syntax when a struct has no additional fields avoids // a potential source change if additional fields are added to the struct in the // future, reducing diff noise. Allow this even though clippy considers it // "needless". #![allow(clippy::needless_update)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "alloc")] extern crate alloc; #[macro_use] mod macros; pub mod field; pub mod filter; pub mod prelude; pub mod registry; pub mod layer; pub mod util; feature! { #![feature = "std"] pub mod reload; pub(crate) mod sync; } feature! { #![all(feature = "fmt", feature = "std")] pub mod fmt; pub use fmt::fmt; pub use fmt::Subscriber as FmtSubscriber; } feature! { #![all(feature = "env-filter", feature = "std")] pub use filter::EnvFilter; } pub use layer::Layer; feature! { #![all(feature = "registry", feature = "std")] pub use registry::Registry; /// pub fn registry() -> Registry { Registry::default() } } mod sealed { pub trait Sealed {} } tracing-subscriber-0.3.18/src/macros.rs000064400000000000000000000010661046102023000161310ustar 00000000000000#[cfg(feature = "std")] macro_rules! try_lock { ($lock:expr) => { try_lock!($lock, else return) }; ($lock:expr, else $els:expr) => { if let ::core::result::Result::Ok(l) = $lock { l } else if std::thread::panicking() { $els } else { panic!("lock poisoned") } }; } macro_rules! feature { ( #![$meta:meta] $($item:item)* ) => { $( #[cfg($meta)] #[cfg_attr(docsrs, doc(cfg($meta)))] $item )* } } tracing-subscriber-0.3.18/src/prelude.rs000064400000000000000000000011101046102023000162730ustar 00000000000000//! The `tracing-subscriber` prelude. //! //! This brings into scope a number of extension traits that define methods on //! types defined here and in other crates. pub use crate::field::{ MakeExt as __tracing_subscriber_field_MakeExt, RecordFields as __tracing_subscriber_field_RecordFields, }; pub use crate::layer::{ Layer as __tracing_subscriber_Layer, SubscriberExt as __tracing_subscriber_SubscriberExt, }; pub use crate::util::SubscriberInitExt as _; feature! { #![all(feature = "fmt", feature = "std")] pub use crate::fmt::writer::MakeWriterExt as _; } tracing-subscriber-0.3.18/src/registry/extensions.rs000064400000000000000000000204431046102023000207140ustar 00000000000000// taken from https://github.com/hyperium/http/blob/master/src/extensions.rs. use crate::sync::{RwLockReadGuard, RwLockWriteGuard}; use std::{ any::{Any, TypeId}, collections::HashMap, fmt, hash::{BuildHasherDefault, Hasher}, }; #[allow(warnings)] type AnyMap = HashMap, BuildHasherDefault>; /// With TypeIds as keys, there's no need to hash them. They are already hashes /// themselves, coming from the compiler. The IdHasher holds the u64 of /// the TypeId, and then returns it, instead of doing any bit fiddling. #[derive(Default, Debug)] struct IdHasher(u64); impl Hasher for IdHasher { fn write(&mut self, _: &[u8]) { unreachable!("TypeId calls write_u64"); } #[inline] fn write_u64(&mut self, id: u64) { self.0 = id; } #[inline] fn finish(&self) -> u64 { self.0 } } /// An immutable, read-only reference to a Span's extensions. #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct Extensions<'a> { inner: RwLockReadGuard<'a, ExtensionsInner>, } impl<'a> Extensions<'a> { #[cfg(feature = "registry")] pub(crate) fn new(inner: RwLockReadGuard<'a, ExtensionsInner>) -> Self { Self { inner } } /// Immutably borrows a type previously inserted into this `Extensions`. pub fn get(&self) -> Option<&T> { self.inner.get::() } } /// An mutable reference to a Span's extensions. #[derive(Debug)] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub struct ExtensionsMut<'a> { inner: RwLockWriteGuard<'a, ExtensionsInner>, } impl<'a> ExtensionsMut<'a> { #[cfg(feature = "registry")] pub(crate) fn new(inner: RwLockWriteGuard<'a, ExtensionsInner>) -> Self { Self { inner } } /// Insert a type into this `Extensions`. /// /// Note that extensions are _not_ /// `Layer`-specific—they are _span_-specific. This means that /// other layers can access and mutate extensions that /// a different Layer recorded. For example, an application might /// have a layer that records execution timings, alongside a layer /// that reports spans and events to a distributed /// tracing system that requires timestamps for spans. /// Ideally, if one layer records a timestamp _x_, the other layer /// should be able to reuse timestamp _x_. /// /// Therefore, extensions should generally be newtypes, rather than common /// types like [`String`](std::string::String), to avoid accidental /// cross-`Layer` clobbering. /// /// ## Panics /// /// If `T` is already present in `Extensions`, then this method will panic. pub fn insert(&mut self, val: T) { assert!(self.replace(val).is_none()) } /// Replaces an existing `T` into this extensions. /// /// If `T` is not present, `Option::None` will be returned. pub fn replace(&mut self, val: T) -> Option { self.inner.insert(val) } /// Get a mutable reference to a type previously inserted on this `ExtensionsMut`. pub fn get_mut(&mut self) -> Option<&mut T> { self.inner.get_mut::() } /// Remove a type from this `Extensions`. /// /// If a extension of this type existed, it will be returned. pub fn remove(&mut self) -> Option { self.inner.remove::() } } /// A type map of span extensions. /// /// [ExtensionsInner] is used by `SpanData` to store and /// span-specific data. A given `Layer` can read and write /// data that it is interested in recording and emitting. #[derive(Default)] pub(crate) struct ExtensionsInner { map: AnyMap, } impl ExtensionsInner { /// Create an empty `Extensions`. #[cfg(any(test, feature = "registry"))] #[inline] #[cfg(any(test, feature = "registry"))] pub(crate) fn new() -> ExtensionsInner { ExtensionsInner { map: AnyMap::default(), } } /// Insert a type into this `Extensions`. /// /// If a extension of this type already existed, it will /// be returned. pub(crate) fn insert(&mut self, val: T) -> Option { self.map .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| { #[allow(warnings)] { (boxed as Box) .downcast() .ok() .map(|boxed| *boxed) } }) } /// Get a reference to a type previously inserted on this `Extensions`. pub(crate) fn get(&self) -> Option<&T> { self.map .get(&TypeId::of::()) .and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref()) } /// Get a mutable reference to a type previously inserted on this `Extensions`. pub(crate) fn get_mut(&mut self) -> Option<&mut T> { self.map .get_mut(&TypeId::of::()) .and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut()) } /// Remove a type from this `Extensions`. /// /// If a extension of this type existed, it will be returned. pub(crate) fn remove(&mut self) -> Option { self.map.remove(&TypeId::of::()).and_then(|boxed| { #[allow(warnings)] { (boxed as Box) .downcast() .ok() .map(|boxed| *boxed) } }) } /// Clear the `ExtensionsInner` in-place, dropping any elements in the map but /// retaining allocated capacity. /// /// This permits the hash map allocation to be pooled by the registry so /// that future spans will not need to allocate new hashmaps. #[cfg(any(test, feature = "registry"))] pub(crate) fn clear(&mut self) { self.map.clear(); } } impl fmt::Debug for ExtensionsInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Extensions") .field("len", &self.map.len()) .field("capacity", &self.map.capacity()) .finish() } } #[cfg(test)] mod tests { use super::*; #[derive(Debug, PartialEq)] struct MyType(i32); #[test] fn test_extensions() { let mut extensions = ExtensionsInner::new(); extensions.insert(5i32); extensions.insert(MyType(10)); assert_eq!(extensions.get(), Some(&5i32)); assert_eq!(extensions.get_mut(), Some(&mut 5i32)); assert_eq!(extensions.remove::(), Some(5i32)); assert!(extensions.get::().is_none()); assert_eq!(extensions.get::(), None); assert_eq!(extensions.get(), Some(&MyType(10))); } #[test] fn clear_retains_capacity() { let mut extensions = ExtensionsInner::new(); extensions.insert(5i32); extensions.insert(MyType(10)); extensions.insert(true); assert_eq!(extensions.map.len(), 3); let prev_capacity = extensions.map.capacity(); extensions.clear(); assert_eq!( extensions.map.len(), 0, "after clear(), extensions map should have length 0" ); assert_eq!( extensions.map.capacity(), prev_capacity, "after clear(), extensions map should retain prior capacity" ); } #[test] fn clear_drops_elements() { use std::sync::Arc; struct DropMePlease(Arc<()>); struct DropMeTooPlease(Arc<()>); let mut extensions = ExtensionsInner::new(); let val1 = DropMePlease(Arc::new(())); let val2 = DropMeTooPlease(Arc::new(())); let val1_dropped = Arc::downgrade(&val1.0); let val2_dropped = Arc::downgrade(&val2.0); extensions.insert(val1); extensions.insert(val2); assert!(val1_dropped.upgrade().is_some()); assert!(val2_dropped.upgrade().is_some()); extensions.clear(); assert!( val1_dropped.upgrade().is_none(), "after clear(), val1 should be dropped" ); assert!( val2_dropped.upgrade().is_none(), "after clear(), val2 should be dropped" ); } } tracing-subscriber-0.3.18/src/registry/mod.rs000064400000000000000000000466251046102023000173060ustar 00000000000000//! Storage for span data shared by multiple [`Layer`]s. //! //! ## Using the Span Registry //! //! This module provides the [`Registry`] type, a [`Subscriber`] implementation //! which tracks per-span data and exposes it to [`Layer`]s. When a `Registry` //! is used as the base `Subscriber` of a `Layer` stack, the //! [`layer::Context`][ctx] type will provide methods allowing `Layer`s to //! [look up span data][lookup] stored in the registry. While [`Registry`] is a //! reasonable default for storing spans and events, other stores that implement //! [`LookupSpan`] and [`Subscriber`] themselves (with [`SpanData`] implemented //! by the per-span data they store) can be used as a drop-in replacement. //! //! For example, we might create a `Registry` and add multiple `Layer`s like so: //! ```rust //! use tracing_subscriber::{registry::Registry, Layer, prelude::*}; //! # use tracing_core::Subscriber; //! # pub struct FooLayer {} //! # pub struct BarLayer {} //! # impl Layer for FooLayer {} //! # impl Layer for BarLayer {} //! # impl FooLayer { //! # fn new() -> Self { Self {} } //! # } //! # impl BarLayer { //! # fn new() -> Self { Self {} } //! # } //! //! let subscriber = Registry::default() //! .with(FooLayer::new()) //! .with(BarLayer::new()); //! ``` //! //! If a type implementing `Layer` depends on the functionality of a `Registry` //! implementation, it should bound its `Subscriber` type parameter with the //! [`LookupSpan`] trait, like so: //! //! ```rust //! use tracing_subscriber::{registry, Layer}; //! use tracing_core::Subscriber; //! //! pub struct MyLayer { //! // ... //! } //! //! impl Layer for MyLayer //! where //! S: Subscriber + for<'a> registry::LookupSpan<'a>, //! { //! // ... //! } //! ``` //! When this bound is added, the `Layer` implementation will be guaranteed //! access to the [`Context`][ctx] methods, such as [`Context::span`][lookup], that //! require the root subscriber to be a registry. //! //! [`Layer`]: crate::layer::Layer //! [`Subscriber`]: tracing_core::Subscriber //! [ctx]: crate::layer::Context //! [lookup]: crate::layer::Context::span() use tracing_core::{field::FieldSet, span::Id, Metadata}; feature! { #![feature = "std"] /// A module containing a type map of span extensions. mod extensions; pub use extensions::{Extensions, ExtensionsMut}; } feature! { #![all(feature = "registry", feature = "std")] mod sharded; mod stack; pub use sharded::Data; pub use sharded::Registry; use crate::filter::FilterId; } /// Provides access to stored span data. /// /// Subscribers which store span data and associate it with span IDs should /// implement this trait; if they do, any [`Layer`]s wrapping them can look up /// metadata via the [`Context`] type's [`span()`] method. /// /// [`Layer`]: super::layer::Layer /// [`Context`]: super::layer::Context /// [`span()`]: super::layer::Context::span pub trait LookupSpan<'a> { /// The type of span data stored in this registry. type Data: SpanData<'a>; /// Returns the [`SpanData`] for a given `Id`, if it exists. /// ///
    /// Note: users of the LookupSpan trait should
    /// typically call the span method rather
    /// than this method. The span method is implemented by
    /// calling span_data, but returns a reference which is
    /// capable of performing more sophisiticated queries.
    /// 
/// fn span_data(&'a self, id: &Id) -> Option; /// Returns a [`SpanRef`] for the span with the given `Id`, if it exists. /// /// A `SpanRef` is similar to [`SpanData`], but it allows performing /// additional lookups against the registryr that stores the wrapped data. /// /// In general, _users_ of the `LookupSpan` trait should use this method /// rather than the [`span_data`] method; while _implementors_ of this trait /// should only implement `span_data`. /// /// [`span_data`]: LookupSpan::span_data() fn span(&'a self, id: &Id) -> Option> where Self: Sized, { let data = self.span_data(id)?; Some(SpanRef { registry: self, data, #[cfg(feature = "registry")] filter: FilterId::none(), }) } /// Registers a [`Filter`] for [per-layer filtering] with this /// [`Subscriber`]. /// /// The [`Filter`] can then use the returned [`FilterId`] to /// [check if it previously enabled a span][check]. /// /// # Panics /// /// If this `Subscriber` does not support [per-layer filtering]. /// /// [`Filter`]: crate::layer::Filter /// [per-layer filtering]: crate::layer::Layer#per-layer-filtering /// [`Subscriber`]: tracing_core::Subscriber /// [`FilterId`]: crate::filter::FilterId /// [check]: SpanData::is_enabled_for #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] fn register_filter(&mut self) -> FilterId { panic!( "{} does not currently support filters", std::any::type_name::() ) } } /// A stored representation of data associated with a span. pub trait SpanData<'a> { /// Returns this span's ID. fn id(&self) -> Id; /// Returns a reference to the span's `Metadata`. fn metadata(&self) -> &'static Metadata<'static>; /// Returns a reference to the ID fn parent(&self) -> Option<&Id>; /// Returns a reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn extensions(&self) -> Extensions<'_>; /// Returns a mutable reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn extensions_mut(&self) -> ExtensionsMut<'_>; /// Returns `true` if this span is enabled for the [per-layer filter][plf] /// corresponding to the provided [`FilterId`]. /// /// ## Default Implementation /// /// By default, this method assumes that the [`LookupSpan`] implementation /// does not support [per-layer filtering][plf], and always returns `true`. /// /// [plf]: crate::layer::Layer#per-layer-filtering /// [`FilterId`]: crate::filter::FilterId #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(feature = "registry")))] fn is_enabled_for(&self, filter: FilterId) -> bool { let _ = filter; true } } /// A reference to [span data] and the associated [registry]. /// /// This type implements all the same methods as [`SpanData`], and provides /// additional methods for querying the registry based on values from the span. /// /// [registry]: LookupSpan #[derive(Debug)] pub struct SpanRef<'a, R: LookupSpan<'a>> { registry: &'a R, data: R::Data, #[cfg(feature = "registry")] filter: FilterId, } /// An iterator over the parents of a span, ordered from leaf to root. /// /// This is returned by the [`SpanRef::scope`] method. #[derive(Debug)] pub struct Scope<'a, R> { registry: &'a R, next: Option, #[cfg(all(feature = "registry", feature = "std"))] filter: FilterId, } feature! { #![any(feature = "alloc", feature = "std")] #[cfg(not(feature = "smallvec"))] use alloc::vec::{self, Vec}; use core::{fmt,iter}; /// An iterator over the parents of a span, ordered from root to leaf. /// /// This is returned by the [`Scope::from_root`] method. pub struct ScopeFromRoot<'a, R> where R: LookupSpan<'a>, { #[cfg(feature = "smallvec")] spans: iter::Rev>>, #[cfg(not(feature = "smallvec"))] spans: iter::Rev>>, } #[cfg(feature = "smallvec")] type SpanRefVecArray<'span, L> = [SpanRef<'span, L>; 16]; impl<'a, R> Scope<'a, R> where R: LookupSpan<'a>, { /// Flips the order of the iterator, so that it is ordered from root to leaf. /// /// The iterator will first return the root span, then that span's immediate child, /// and so on until it finally returns the span that [`SpanRef::scope`] was called on. /// /// If any items were consumed from the [`Scope`] before calling this method then they /// will *not* be returned from the [`ScopeFromRoot`]. /// /// **Note**: this will allocate if there are many spans remaining, or if the /// "smallvec" feature flag is not enabled. #[allow(clippy::wrong_self_convention)] pub fn from_root(self) -> ScopeFromRoot<'a, R> { #[cfg(feature = "smallvec")] type Buf = smallvec::SmallVec; #[cfg(not(feature = "smallvec"))] type Buf = Vec; ScopeFromRoot { spans: self.collect::>().into_iter().rev(), } } } impl<'a, R> Iterator for ScopeFromRoot<'a, R> where R: LookupSpan<'a>, { type Item = SpanRef<'a, R>; #[inline] fn next(&mut self) -> Option { self.spans.next() } #[inline] fn size_hint(&self) -> (usize, Option) { self.spans.size_hint() } } impl<'a, R> fmt::Debug for ScopeFromRoot<'a, R> where R: LookupSpan<'a>, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.pad("ScopeFromRoot { .. }") } } } impl<'a, R> Iterator for Scope<'a, R> where R: LookupSpan<'a>, { type Item = SpanRef<'a, R>; fn next(&mut self) -> Option { loop { let curr = self.registry.span(self.next.as_ref()?)?; #[cfg(all(feature = "registry", feature = "std"))] let curr = curr.with_filter(self.filter); self.next = curr.data.parent().cloned(); // If the `Scope` is filtered, check if the current span is enabled // by the selected filter ID. #[cfg(all(feature = "registry", feature = "std"))] { if !curr.is_enabled_for(self.filter) { // The current span in the chain is disabled for this // filter. Try its parent. continue; } } return Some(curr); } } } impl<'a, R> SpanRef<'a, R> where R: LookupSpan<'a>, { /// Returns this span's ID. pub fn id(&self) -> Id { self.data.id() } /// Returns a static reference to the span's metadata. pub fn metadata(&self) -> &'static Metadata<'static> { self.data.metadata() } /// Returns the span's name, pub fn name(&self) -> &'static str { self.data.metadata().name() } /// Returns a list of [fields] defined by the span. /// /// [fields]: tracing_core::field pub fn fields(&self) -> &FieldSet { self.data.metadata().fields() } /// Returns a `SpanRef` describing this span's parent, or `None` if this /// span is the root of its trace tree. pub fn parent(&self) -> Option { let id = self.data.parent()?; let data = self.registry.span_data(id)?; #[cfg(all(feature = "registry", feature = "std"))] { // move these into mut bindings if the registry feature is enabled, // since they may be mutated in the loop. let mut data = data; loop { // Is this parent enabled by our filter? if data.is_enabled_for(self.filter) { return Some(Self { registry: self.registry, filter: self.filter, data, }); } // It's not enabled. If the disabled span has a parent, try that! let id = data.parent()?; data = self.registry.span_data(id)?; } } #[cfg(not(all(feature = "registry", feature = "std")))] Some(Self { registry: self.registry, data, }) } /// Returns an iterator over all parents of this span, starting with this span, /// ordered from leaf to root. /// /// The iterator will first return the span, then the span's immediate parent, /// followed by that span's parent, and so on, until it reaches a root span. /// /// ```rust /// use tracing::{span, Subscriber}; /// use tracing_subscriber::{ /// layer::{Context, Layer}, /// prelude::*, /// registry::LookupSpan, /// }; /// /// struct PrintingLayer; /// impl Layer for PrintingLayer /// where /// S: Subscriber + for<'lookup> LookupSpan<'lookup>, /// { /// fn on_enter(&self, id: &span::Id, ctx: Context) { /// let span = ctx.span(id).unwrap(); /// let scope = span.scope().map(|span| span.name()).collect::>(); /// println!("Entering span: {:?}", scope); /// } /// } /// /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { /// let _root = tracing::info_span!("root").entered(); /// // Prints: Entering span: ["root"] /// let _child = tracing::info_span!("child").entered(); /// // Prints: Entering span: ["child", "root"] /// let _leaf = tracing::info_span!("leaf").entered(); /// // Prints: Entering span: ["leaf", "child", "root"] /// }); /// ``` /// /// If the opposite order (from the root to this span) is desired, calling [`Scope::from_root`] on /// the returned iterator reverses the order. /// /// ```rust /// # use tracing::{span, Subscriber}; /// # use tracing_subscriber::{ /// # layer::{Context, Layer}, /// # prelude::*, /// # registry::LookupSpan, /// # }; /// # struct PrintingLayer; /// impl Layer for PrintingLayer /// where /// S: Subscriber + for<'lookup> LookupSpan<'lookup>, /// { /// fn on_enter(&self, id: &span::Id, ctx: Context) { /// let span = ctx.span(id).unwrap(); /// let scope = span.scope().from_root().map(|span| span.name()).collect::>(); /// println!("Entering span: {:?}", scope); /// } /// } /// /// tracing::subscriber::with_default(tracing_subscriber::registry().with(PrintingLayer), || { /// let _root = tracing::info_span!("root").entered(); /// // Prints: Entering span: ["root"] /// let _child = tracing::info_span!("child").entered(); /// // Prints: Entering span: ["root", "child"] /// let _leaf = tracing::info_span!("leaf").entered(); /// // Prints: Entering span: ["root", "child", "leaf"] /// }); /// ``` pub fn scope(&self) -> Scope<'a, R> { Scope { registry: self.registry, next: Some(self.id()), #[cfg(feature = "registry")] filter: self.filter, } } /// Returns a reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn extensions(&self) -> Extensions<'_> { self.data.extensions() } /// Returns a mutable reference to this span's `Extensions`. /// /// The extensions may be used by `Layer`s to store additional data /// describing the span. #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] pub fn extensions_mut(&self) -> ExtensionsMut<'_> { self.data.extensions_mut() } #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn try_with_filter(self, filter: FilterId) -> Option { if self.is_enabled_for(filter) { return Some(self.with_filter(filter)); } None } #[inline] #[cfg(all(feature = "registry", feature = "std"))] pub(crate) fn is_enabled_for(&self, filter: FilterId) -> bool { self.data.is_enabled_for(filter) } #[inline] #[cfg(all(feature = "registry", feature = "std"))] fn with_filter(self, filter: FilterId) -> Self { Self { filter, ..self } } } #[cfg(all(test, feature = "registry", feature = "std"))] mod tests { use crate::{ layer::{Context, Layer}, prelude::*, registry::LookupSpan, }; use std::sync::{Arc, Mutex}; use tracing::{span, Subscriber}; #[test] fn spanref_scope_iteration_order() { let last_entered_scope = Arc::new(Mutex::new(Vec::new())); #[derive(Default)] struct PrintingLayer { last_entered_scope: Arc>>, } impl Layer for PrintingLayer where S: Subscriber + for<'lookup> LookupSpan<'lookup>, { fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { let span = ctx.span(id).unwrap(); let scope = span.scope().map(|span| span.name()).collect::>(); *self.last_entered_scope.lock().unwrap() = scope; } } let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer { last_entered_scope: last_entered_scope.clone(), })); let _root = tracing::info_span!("root").entered(); assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]); let _child = tracing::info_span!("child").entered(); assert_eq!(&*last_entered_scope.lock().unwrap(), &["child", "root"]); let _leaf = tracing::info_span!("leaf").entered(); assert_eq!( &*last_entered_scope.lock().unwrap(), &["leaf", "child", "root"] ); } #[test] fn spanref_scope_fromroot_iteration_order() { let last_entered_scope = Arc::new(Mutex::new(Vec::new())); #[derive(Default)] struct PrintingLayer { last_entered_scope: Arc>>, } impl Layer for PrintingLayer where S: Subscriber + for<'lookup> LookupSpan<'lookup>, { fn on_enter(&self, id: &span::Id, ctx: Context<'_, S>) { let span = ctx.span(id).unwrap(); let scope = span .scope() .from_root() .map(|span| span.name()) .collect::>(); *self.last_entered_scope.lock().unwrap() = scope; } } let _guard = tracing::subscriber::set_default(crate::registry().with(PrintingLayer { last_entered_scope: last_entered_scope.clone(), })); let _root = tracing::info_span!("root").entered(); assert_eq!(&*last_entered_scope.lock().unwrap(), &["root"]); let _child = tracing::info_span!("child").entered(); assert_eq!(&*last_entered_scope.lock().unwrap(), &["root", "child",]); let _leaf = tracing::info_span!("leaf").entered(); assert_eq!( &*last_entered_scope.lock().unwrap(), &["root", "child", "leaf"] ); } } tracing-subscriber-0.3.18/src/registry/sharded.rs000064400000000000000000000767321046102023000201430ustar 00000000000000use sharded_slab::{pool::Ref, Clear, Pool}; use thread_local::ThreadLocal; use super::stack::SpanStack; use crate::{ filter::{FilterId, FilterMap, FilterState}, registry::{ extensions::{Extensions, ExtensionsInner, ExtensionsMut}, LookupSpan, SpanData, }, sync::RwLock, }; use std::{ cell::{self, Cell, RefCell}, sync::atomic::{fence, AtomicUsize, Ordering}, }; use tracing_core::{ dispatcher::{self, Dispatch}, span::{self, Current, Id}, Event, Interest, Metadata, Subscriber, }; /// A shared, reusable store for spans. /// /// A `Registry` is a [`Subscriber`] around which multiple [`Layer`]s /// implementing various behaviors may be [added]. Unlike other types /// implementing `Subscriber`, `Registry` does not actually record traces itself: /// instead, it collects and stores span data that is exposed to any [`Layer`]s /// wrapping it through implementations of the [`LookupSpan`] trait. /// The `Registry` is responsible for storing span metadata, recording /// relationships between spans, and tracking which spans are active and which /// are closed. In addition, it provides a mechanism for [`Layer`]s to store /// user-defined per-span data, called [extensions], in the registry. This /// allows [`Layer`]-specific data to benefit from the `Registry`'s /// high-performance concurrent storage. /// /// This registry is implemented using a [lock-free sharded slab][slab], and is /// highly optimized for concurrent access. /// /// # Span ID Generation /// /// Span IDs are not globally unique, but the registry ensures that /// no two currently active spans have the same ID within a process. /// /// One of the primary responsibilities of the registry is to generate [span /// IDs]. Therefore, it's important for other code that interacts with the /// registry, such as [`Layer`]s, to understand the guarantees of the /// span IDs that are generated. /// /// The registry's span IDs are guaranteed to be unique **at a given point /// in time**. This means that an active span will never be assigned the /// same ID as another **currently active** span. However, the registry /// **will** eventually reuse the IDs of [closed] spans, although an ID /// will never be reassigned immediately after a span has closed. /// /// Spans are not [considered closed] by the `Registry` until *every* /// [`Span`] reference with that ID has been dropped. /// /// Thus: span IDs generated by the registry should be considered unique /// only at a given point in time, and only relative to other spans /// generated by the same process. Two spans with the same ID will not exist /// in the same process concurrently. However, if historical span data is /// being stored, the same ID may occur for multiple spans times in that /// data. If spans must be uniquely identified in historical data, the user /// code storing this data must assign its own unique identifiers to those /// spans. A counter is generally sufficient for this. /// /// Similarly, span IDs generated by the registry are not unique outside of /// a given process. Distributed tracing systems may require identifiers /// that are unique across multiple processes on multiple machines (for /// example, [OpenTelemetry's `SpanId`s and `TraceId`s][ot]). `tracing` span /// IDs generated by the registry should **not** be used for this purpose. /// Instead, code which integrates with a distributed tracing system should /// generate and propagate its own IDs according to the rules specified by /// the distributed tracing system. These IDs can be associated with /// `tracing` spans using [fields] and/or [stored span data]. /// /// [span IDs]: tracing_core::span::Id /// [slab]: sharded_slab /// [`Layer`]: crate::Layer /// [added]: crate::layer::Layer#composing-layers /// [extensions]: super::Extensions /// [closed]: https://docs.rs/tracing/latest/tracing/span/index.html#closing-spans /// [considered closed]: tracing_core::subscriber::Subscriber::try_close() /// [`Span`]: https://docs.rs/tracing/latest/tracing/span/struct.Span.html /// [ot]: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#spancontext /// [fields]: tracing_core::field /// [stored span data]: crate::registry::SpanData::extensions_mut #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] #[derive(Debug)] pub struct Registry { spans: Pool, current_spans: ThreadLocal>, next_filter_id: u8, } /// Span data stored in a [`Registry`]. /// /// The registry stores well-known data defined by tracing: span relationships, /// metadata and reference counts. Additional user-defined data provided by /// [`Layer`s], such as formatted fields, metrics, or distributed traces should /// be stored in the [extensions] typemap. /// /// [`Layer`s]: crate::layer::Layer /// [extensions]: Extensions #[cfg(feature = "registry")] #[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] #[derive(Debug)] pub struct Data<'a> { /// Immutable reference to the pooled `DataInner` entry. inner: Ref<'a, DataInner>, } /// Stored data associated with a span. /// /// This type is pooled using [`sharded_slab::Pool`]; when a span is /// dropped, the `DataInner` entry at that span's slab index is cleared /// in place and reused by a future span. Thus, the `Default` and /// [`sharded_slab::Clear`] implementations for this type are /// load-bearing. #[derive(Debug)] struct DataInner { filter_map: FilterMap, metadata: &'static Metadata<'static>, parent: Option, ref_count: AtomicUsize, // The span's `Extensions` typemap. Allocations for the `HashMap` backing // this are pooled and reused in place. pub(crate) extensions: RwLock, } // === impl Registry === impl Default for Registry { fn default() -> Self { Self { spans: Pool::new(), current_spans: ThreadLocal::new(), next_filter_id: 0, } } } #[inline] fn idx_to_id(idx: usize) -> Id { Id::from_u64(idx as u64 + 1) } #[inline] fn id_to_idx(id: &Id) -> usize { id.into_u64() as usize - 1 } /// A guard that tracks how many [`Registry`]-backed `Layer`s have /// processed an `on_close` event. /// /// This is needed to enable a [`Registry`]-backed Layer to access span /// data after the `Layer` has recieved the `on_close` callback. /// /// Once all `Layer`s have processed this event, the [`Registry`] knows /// that is able to safely remove the span tracked by `id`. `CloseGuard` /// accomplishes this through a two-step process: /// 1. Whenever a [`Registry`]-backed `Layer::on_close` method is /// called, `Registry::start_close` is closed. /// `Registry::start_close` increments a thread-local `CLOSE_COUNT` /// by 1 and returns a `CloseGuard`. /// 2. The `CloseGuard` is dropped at the end of `Layer::on_close`. On /// drop, `CloseGuard` checks thread-local `CLOSE_COUNT`. If /// `CLOSE_COUNT` is 0, the `CloseGuard` removes the span with the /// `id` from the registry, as all `Layers` that might have seen the /// `on_close` notification have processed it. If `CLOSE_COUNT` is /// greater than 0, `CloseGuard` decrements the counter by one and /// _does not_ remove the span from the [`Registry`]. /// pub(crate) struct CloseGuard<'a> { id: Id, registry: &'a Registry, is_closing: bool, } impl Registry { fn get(&self, id: &Id) -> Option> { self.spans.get(id_to_idx(id)) } /// Returns a guard which tracks how many `Layer`s have /// processed an `on_close` notification via the `CLOSE_COUNT` thread-local. /// For additional details, see [`CloseGuard`]. /// pub(crate) fn start_close(&self, id: Id) -> CloseGuard<'_> { CLOSE_COUNT.with(|count| { let c = count.get(); count.set(c + 1); }); CloseGuard { id, registry: self, is_closing: false, } } pub(crate) fn has_per_layer_filters(&self) -> bool { self.next_filter_id > 0 } pub(crate) fn span_stack(&self) -> cell::Ref<'_, SpanStack> { self.current_spans.get_or_default().borrow() } } thread_local! { /// `CLOSE_COUNT` is the thread-local counter used by `CloseGuard` to /// track how many layers have processed the close. /// For additional details, see [`CloseGuard`]. /// static CLOSE_COUNT: Cell = Cell::new(0); } impl Subscriber for Registry { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { if self.has_per_layer_filters() { return FilterState::take_interest().unwrap_or_else(Interest::always); } Interest::always() } fn enabled(&self, _: &Metadata<'_>) -> bool { if self.has_per_layer_filters() { return FilterState::event_enabled(); } true } #[inline] fn new_span(&self, attrs: &span::Attributes<'_>) -> span::Id { let parent = if attrs.is_root() { None } else if attrs.is_contextual() { self.current_span().id().map(|id| self.clone_span(id)) } else { attrs.parent().map(|id| self.clone_span(id)) }; let id = self .spans // Check out a `DataInner` entry from the pool for the new span. If // there are free entries already allocated in the pool, this will // preferentially reuse one; otherwise, a new `DataInner` is // allocated and added to the pool. .create_with(|data| { data.metadata = attrs.metadata(); data.parent = parent; data.filter_map = crate::filter::FILTERING.with(|filtering| filtering.filter_map()); #[cfg(debug_assertions)] { if data.filter_map != FilterMap::default() { debug_assert!(self.has_per_layer_filters()); } } let refs = data.ref_count.get_mut(); debug_assert_eq!(*refs, 0); *refs = 1; }) .expect("Unable to allocate another span"); idx_to_id(id) } /// This is intentionally not implemented, as recording fields /// on a span is the responsibility of layers atop of this registry. #[inline] fn record(&self, _: &span::Id, _: &span::Record<'_>) {} fn record_follows_from(&self, _span: &span::Id, _follows: &span::Id) {} fn event_enabled(&self, _event: &Event<'_>) -> bool { if self.has_per_layer_filters() { return FilterState::event_enabled(); } true } /// This is intentionally not implemented, as recording events /// is the responsibility of layers atop of this registry. fn event(&self, _: &Event<'_>) {} fn enter(&self, id: &span::Id) { if self .current_spans .get_or_default() .borrow_mut() .push(id.clone()) { self.clone_span(id); } } fn exit(&self, id: &span::Id) { if let Some(spans) = self.current_spans.get() { if spans.borrow_mut().pop(id) { dispatcher::get_default(|dispatch| dispatch.try_close(id.clone())); } } } fn clone_span(&self, id: &span::Id) -> span::Id { let span = self .get(id) .unwrap_or_else(|| panic!( "tried to clone {:?}, but no span exists with that ID\n\ This may be caused by consuming a parent span (`parent: span`) rather than borrowing it (`parent: &span`).", id, )); // Like `std::sync::Arc`, adds to the ref count (on clone) don't require // a strong ordering; if we call` clone_span`, the reference count must // always at least 1. The only synchronization necessary is between // calls to `try_close`: we have to ensure that all threads have // dropped their refs to the span before the span is closed. let refs = span.ref_count.fetch_add(1, Ordering::Relaxed); assert_ne!( refs, 0, "tried to clone a span ({:?}) that already closed", id ); id.clone() } fn current_span(&self) -> Current { self.current_spans .get() .and_then(|spans| { let spans = spans.borrow(); let id = spans.current()?; let span = self.get(id)?; Some(Current::new(id.clone(), span.metadata)) }) .unwrap_or_else(Current::none) } /// Decrements the reference count of the span with the given `id`, and /// removes the span if it is zero. /// /// The allocated span slot will be reused when a new span is created. fn try_close(&self, id: span::Id) -> bool { let span = match self.get(&id) { Some(span) => span, None if std::thread::panicking() => return false, None => panic!("tried to drop a ref to {:?}, but no such span exists!", id), }; let refs = span.ref_count.fetch_sub(1, Ordering::Release); if !std::thread::panicking() { assert!(refs < std::usize::MAX, "reference count overflow!"); } if refs > 1 { return false; } // Synchronize if we are actually removing the span (stolen // from std::Arc); this ensures that all other `try_close` calls on // other threads happen-before we actually remove the span. fence(Ordering::Acquire); true } } impl<'a> LookupSpan<'a> for Registry { type Data = Data<'a>; fn span_data(&'a self, id: &Id) -> Option { let inner = self.get(id)?; Some(Data { inner }) } fn register_filter(&mut self) -> FilterId { let id = FilterId::new(self.next_filter_id); self.next_filter_id += 1; id } } // === impl CloseGuard === impl<'a> CloseGuard<'a> { pub(crate) fn set_closing(&mut self) { self.is_closing = true; } } impl<'a> Drop for CloseGuard<'a> { fn drop(&mut self) { // If this returns with an error, we are already panicking. At // this point, there's nothing we can really do to recover // except by avoiding a double-panic. let _ = CLOSE_COUNT.try_with(|count| { let c = count.get(); // Decrement the count to indicate that _this_ guard's // `on_close` callback has completed. // // Note that we *must* do this before we actually remove the span // from the registry, since dropping the `DataInner` may trigger a // new close, if this span is the last reference to a parent span. count.set(c - 1); // If the current close count is 1, this stack frame is the last // `on_close` call. If the span is closing, it's okay to remove the // span. if c == 1 && self.is_closing { self.registry.spans.clear(id_to_idx(&self.id)); } }); } } // === impl Data === impl<'a> SpanData<'a> for Data<'a> { fn id(&self) -> Id { idx_to_id(self.inner.key()) } fn metadata(&self) -> &'static Metadata<'static> { self.inner.metadata } fn parent(&self) -> Option<&Id> { self.inner.parent.as_ref() } fn extensions(&self) -> Extensions<'_> { Extensions::new(self.inner.extensions.read().expect("Mutex poisoned")) } fn extensions_mut(&self) -> ExtensionsMut<'_> { ExtensionsMut::new(self.inner.extensions.write().expect("Mutex poisoned")) } #[inline] fn is_enabled_for(&self, filter: FilterId) -> bool { self.inner.filter_map.is_enabled(filter) } } // === impl DataInner === impl Default for DataInner { fn default() -> Self { // Since `DataInner` owns a `&'static Callsite` pointer, we need // something to use as the initial default value for that callsite. // Since we can't access a `DataInner` until it has had actual span data // inserted into it, the null metadata will never actually be accessed. struct NullCallsite; impl tracing_core::callsite::Callsite for NullCallsite { fn set_interest(&self, _: Interest) { unreachable!( "/!\\ Tried to register the null callsite /!\\\n \ This should never have happened and is definitely a bug. \ A `tracing` bug report would be appreciated." ) } fn metadata(&self) -> &Metadata<'_> { unreachable!( "/!\\ Tried to access the null callsite's metadata /!\\\n \ This should never have happened and is definitely a bug. \ A `tracing` bug report would be appreciated." ) } } static NULL_CALLSITE: NullCallsite = NullCallsite; static NULL_METADATA: Metadata<'static> = tracing_core::metadata! { name: "", target: "", level: tracing_core::Level::TRACE, fields: &[], callsite: &NULL_CALLSITE, kind: tracing_core::metadata::Kind::SPAN, }; Self { filter_map: FilterMap::default(), metadata: &NULL_METADATA, parent: None, ref_count: AtomicUsize::new(0), extensions: RwLock::new(ExtensionsInner::new()), } } } impl Clear for DataInner { /// Clears the span's data in place, dropping the parent's reference count. fn clear(&mut self) { // A span is not considered closed until all of its children have closed. // Therefore, each span's `DataInner` holds a "reference" to the parent // span, keeping the parent span open until all its children have closed. // When we close a span, we must then decrement the parent's ref count // (potentially, allowing it to close, if this child is the last reference // to that span). // We have to actually unpack the option inside the `get_default` // closure, since it is a `FnMut`, but testing that there _is_ a value // here lets us avoid the thread-local access if we don't need the // dispatcher at all. if self.parent.is_some() { // Note that --- because `Layered::try_close` works by calling // `try_close` on the inner subscriber and using the return value to // determine whether to call the `Layer`'s `on_close` callback --- // we must call `try_close` on the entire subscriber stack, rather // than just on the registry. If the registry called `try_close` on // itself directly, the layers wouldn't see the close notification. let subscriber = dispatcher::get_default(Dispatch::clone); if let Some(parent) = self.parent.take() { let _ = subscriber.try_close(parent); } } // Clear (but do not deallocate!) the pooled `HashMap` for the span's extensions. self.extensions .get_mut() .unwrap_or_else(|l| { // This function can be called in a `Drop` impl, such as while // panicking, so ignore lock poisoning. l.into_inner() }) .clear(); self.filter_map = FilterMap::default(); } } #[cfg(test)] mod tests { use super::*; use crate::{layer::Context, registry::LookupSpan, Layer}; use std::{ collections::HashMap, sync::{Arc, Mutex, Weak}, }; use tracing::{self, subscriber::with_default}; use tracing_core::{ dispatcher, span::{Attributes, Id}, Subscriber, }; #[derive(Debug)] struct DoesNothing; impl Layer for DoesNothing {} struct AssertionLayer; impl Layer for AssertionLayer where S: Subscriber + for<'a> LookupSpan<'a>, { fn on_close(&self, id: Id, ctx: Context<'_, S>) { dbg!(format_args!("closing {:?}", id)); assert!(&ctx.span(&id).is_some()); } } #[test] fn single_layer_can_access_closed_span() { let subscriber = AssertionLayer.with_subscriber(Registry::default()); with_default(subscriber, || { let span = tracing::debug_span!("span"); drop(span); }); } #[test] fn multiple_layers_can_access_closed_span() { let subscriber = AssertionLayer .and_then(AssertionLayer) .with_subscriber(Registry::default()); with_default(subscriber, || { let span = tracing::debug_span!("span"); drop(span); }); } struct CloseLayer { inner: Arc>, } struct CloseHandle { state: Arc>, } #[derive(Default)] struct CloseState { open: HashMap<&'static str, Weak<()>>, closed: Vec<(&'static str, Weak<()>)>, } struct SetRemoved(Arc<()>); impl Layer for CloseLayer where S: Subscriber + for<'a> LookupSpan<'a>, { fn on_new_span(&self, _: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) { let span = ctx.span(id).expect("Missing span; this is a bug"); let mut lock = self.inner.lock().unwrap(); let is_removed = Arc::new(()); assert!( lock.open .insert(span.name(), Arc::downgrade(&is_removed)) .is_none(), "test layer saw multiple spans with the same name, the test is probably messed up" ); let mut extensions = span.extensions_mut(); extensions.insert(SetRemoved(is_removed)); } fn on_close(&self, id: Id, ctx: Context<'_, S>) { let span = if let Some(span) = ctx.span(&id) { span } else { println!( "span {:?} did not exist in `on_close`, are we panicking?", id ); return; }; let name = span.name(); println!("close {} ({:?})", name, id); if let Ok(mut lock) = self.inner.lock() { if let Some(is_removed) = lock.open.remove(name) { assert!(is_removed.upgrade().is_some()); lock.closed.push((name, is_removed)); } } } } impl CloseLayer { fn new() -> (Self, CloseHandle) { let state = Arc::new(Mutex::new(CloseState::default())); ( Self { inner: state.clone(), }, CloseHandle { state }, ) } } impl CloseState { fn is_open(&self, span: &str) -> bool { self.open.contains_key(span) } fn is_closed(&self, span: &str) -> bool { self.closed.iter().any(|(name, _)| name == &span) } } impl CloseHandle { fn assert_closed(&self, span: &str) { let lock = self.state.lock().unwrap(); assert!( lock.is_closed(span), "expected {} to be closed{}", span, if lock.is_open(span) { " (it was still open)" } else { ", but it never existed (is there a problem with the test?)" } ) } fn assert_open(&self, span: &str) { let lock = self.state.lock().unwrap(); assert!( lock.is_open(span), "expected {} to be open{}", span, if lock.is_closed(span) { " (it was still open)" } else { ", but it never existed (is there a problem with the test?)" } ) } fn assert_removed(&self, span: &str) { let lock = self.state.lock().unwrap(); let is_removed = match lock.closed.iter().find(|(name, _)| name == &span) { Some((_, is_removed)) => is_removed, None => panic!( "expected {} to be removed from the registry, but it was not closed {}", span, if lock.is_closed(span) { " (it was still open)" } else { ", but it never existed (is there a problem with the test?)" } ), }; assert!( is_removed.upgrade().is_none(), "expected {} to have been removed from the registry", span ) } fn assert_not_removed(&self, span: &str) { let lock = self.state.lock().unwrap(); let is_removed = match lock.closed.iter().find(|(name, _)| name == &span) { Some((_, is_removed)) => is_removed, None if lock.is_open(span) => return, None => unreachable!(), }; assert!( is_removed.upgrade().is_some(), "expected {} to have been removed from the registry", span ) } #[allow(unused)] // may want this for future tests fn assert_last_closed(&self, span: Option<&str>) { let lock = self.state.lock().unwrap(); let last = lock.closed.last().map(|(span, _)| span); assert_eq!( last, span.as_ref(), "expected {:?} to have closed last", span ); } fn assert_closed_in_order(&self, order: impl AsRef<[&'static str]>) { let lock = self.state.lock().unwrap(); let order = order.as_ref(); for (i, name) in order.iter().enumerate() { assert_eq!( lock.closed.get(i).map(|(span, _)| span), Some(name), "expected close order: {:?}, actual: {:?}", order, lock.closed.iter().map(|(name, _)| name).collect::>() ); } } } #[test] fn spans_are_removed_from_registry() { let (close_layer, state) = CloseLayer::new(); let subscriber = AssertionLayer .and_then(close_layer) .with_subscriber(Registry::default()); // Create a `Dispatch` (which is internally reference counted) so that // the subscriber lives to the end of the test. Otherwise, if we just // passed the subscriber itself to `with_default`, we could see the span // be dropped when the subscriber itself is dropped, destroying the // registry. let dispatch = dispatcher::Dispatch::new(subscriber); dispatcher::with_default(&dispatch, || { let span = tracing::debug_span!("span1"); drop(span); let span = tracing::info_span!("span2"); drop(span); }); state.assert_removed("span1"); state.assert_removed("span2"); // Ensure the registry itself outlives the span. drop(dispatch); } #[test] fn spans_are_only_closed_when_the_last_ref_drops() { let (close_layer, state) = CloseLayer::new(); let subscriber = AssertionLayer .and_then(close_layer) .with_subscriber(Registry::default()); // Create a `Dispatch` (which is internally reference counted) so that // the subscriber lives to the end of the test. Otherwise, if we just // passed the subscriber itself to `with_default`, we could see the span // be dropped when the subscriber itself is dropped, destroying the // registry. let dispatch = dispatcher::Dispatch::new(subscriber); let span2 = dispatcher::with_default(&dispatch, || { let span = tracing::debug_span!("span1"); drop(span); let span2 = tracing::info_span!("span2"); let span2_clone = span2.clone(); drop(span2); span2_clone }); state.assert_removed("span1"); state.assert_not_removed("span2"); drop(span2); state.assert_removed("span1"); // Ensure the registry itself outlives the span. drop(dispatch); } #[test] fn span_enter_guards_are_dropped_out_of_order() { let (close_layer, state) = CloseLayer::new(); let subscriber = AssertionLayer .and_then(close_layer) .with_subscriber(Registry::default()); // Create a `Dispatch` (which is internally reference counted) so that // the subscriber lives to the end of the test. Otherwise, if we just // passed the subscriber itself to `with_default`, we could see the span // be dropped when the subscriber itself is dropped, destroying the // registry. let dispatch = dispatcher::Dispatch::new(subscriber); dispatcher::with_default(&dispatch, || { let span1 = tracing::debug_span!("span1"); let span2 = tracing::info_span!("span2"); let enter1 = span1.enter(); let enter2 = span2.enter(); drop(enter1); drop(span1); state.assert_removed("span1"); state.assert_not_removed("span2"); drop(enter2); state.assert_not_removed("span2"); drop(span2); state.assert_removed("span1"); state.assert_removed("span2"); }); } #[test] fn child_closes_parent() { // This test asserts that if a parent span's handle is dropped before // a child span's handle, the parent will remain open until child // closes, and will then be closed. let (close_layer, state) = CloseLayer::new(); let subscriber = close_layer.with_subscriber(Registry::default()); let dispatch = dispatcher::Dispatch::new(subscriber); dispatcher::with_default(&dispatch, || { let span1 = tracing::info_span!("parent"); let span2 = tracing::info_span!(parent: &span1, "child"); state.assert_open("parent"); state.assert_open("child"); drop(span1); state.assert_open("parent"); state.assert_open("child"); drop(span2); state.assert_closed("parent"); state.assert_closed("child"); }); } #[test] fn child_closes_grandparent() { // This test asserts that, when a span is kept open by a child which // is *itself* kept open by a child, closing the grandchild will close // both the parent *and* the grandparent. let (close_layer, state) = CloseLayer::new(); let subscriber = close_layer.with_subscriber(Registry::default()); let dispatch = dispatcher::Dispatch::new(subscriber); dispatcher::with_default(&dispatch, || { let span1 = tracing::info_span!("grandparent"); let span2 = tracing::info_span!(parent: &span1, "parent"); let span3 = tracing::info_span!(parent: &span2, "child"); state.assert_open("grandparent"); state.assert_open("parent"); state.assert_open("child"); drop(span1); drop(span2); state.assert_open("grandparent"); state.assert_open("parent"); state.assert_open("child"); drop(span3); state.assert_closed_in_order(["child", "parent", "grandparent"]); }); } } tracing-subscriber-0.3.18/src/registry/stack.rs000064400000000000000000000034761046102023000176310ustar 00000000000000pub(crate) use tracing_core::span::Id; #[derive(Debug)] struct ContextId { id: Id, duplicate: bool, } /// `SpanStack` tracks what spans are currently executing on a thread-local basis. /// /// A "separate current span" for each thread is a semantic choice, as each span /// can be executing in a different thread. #[derive(Debug, Default)] pub(crate) struct SpanStack { stack: Vec, } impl SpanStack { #[inline] pub(super) fn push(&mut self, id: Id) -> bool { let duplicate = self.stack.iter().any(|i| i.id == id); self.stack.push(ContextId { id, duplicate }); !duplicate } #[inline] pub(super) fn pop(&mut self, expected_id: &Id) -> bool { if let Some((idx, _)) = self .stack .iter() .enumerate() .rev() .find(|(_, ctx_id)| ctx_id.id == *expected_id) { let ContextId { id: _, duplicate } = self.stack.remove(idx); return !duplicate; } false } #[inline] pub(crate) fn iter(&self) -> impl Iterator { self.stack .iter() .rev() .filter_map(|ContextId { id, duplicate }| if !*duplicate { Some(id) } else { None }) } #[inline] pub(crate) fn current(&self) -> Option<&Id> { self.iter().next() } } #[cfg(test)] mod tests { use super::{Id, SpanStack}; #[test] fn pop_last_span() { let mut stack = SpanStack::default(); let id = Id::from_u64(1); stack.push(id.clone()); assert!(stack.pop(&id)); } #[test] fn pop_first_span() { let mut stack = SpanStack::default(); stack.push(Id::from_u64(1)); stack.push(Id::from_u64(2)); let id = Id::from_u64(1); assert!(stack.pop(&id)); } } tracing-subscriber-0.3.18/src/reload.rs000064400000000000000000000307271046102023000161210ustar 00000000000000//! Wrapper for a `Layer` to allow it to be dynamically reloaded. //! //! This module provides a [`Layer` type] implementing the [`Layer` trait] or [`Filter` trait] //! which wraps another type implementing the corresponding trait. This //! allows the wrapped type to be replaced with another //! instance of that type at runtime. //! //! This can be used in cases where a subset of `Layer` or `Filter` functionality //! should be dynamically reconfigured, such as when filtering directives may //! change at runtime. Note that this layer introduces a (relatively small) //! amount of overhead, and should thus only be used as needed. //! //! # Examples //! //! Reloading a [global filtering](crate::layer#global-filtering) layer: //! //! ```rust //! # use tracing::info; //! use tracing_subscriber::{filter, fmt, reload, prelude::*}; //! let filter = filter::LevelFilter::WARN; //! let (filter, reload_handle) = reload::Layer::new(filter); //! tracing_subscriber::registry() //! .with(filter) //! .with(fmt::Layer::default()) //! .init(); //! # //! # // specifying the Registry type is required //! # let _: &reload::Handle = &reload_handle; //! # //! info!("This will be ignored"); //! reload_handle.modify(|filter| *filter = filter::LevelFilter::INFO); //! info!("This will be logged"); //! ``` //! //! Reloading a [`Filtered`](crate::filter::Filtered) layer: //! //! ```rust //! # use tracing::info; //! use tracing_subscriber::{filter, fmt, reload, prelude::*}; //! let filtered_layer = fmt::Layer::default().with_filter(filter::LevelFilter::WARN); //! let (filtered_layer, reload_handle) = reload::Layer::new(filtered_layer); //! # //! # // specifying the Registry type is required //! # let _: &reload::Handle, //! # filter::LevelFilter, tracing_subscriber::Registry>,tracing_subscriber::Registry> //! # = &reload_handle; //! # //! tracing_subscriber::registry() //! .with(filtered_layer) //! .init(); //! info!("This will be ignored"); //! reload_handle.modify(|layer| *layer.filter_mut() = filter::LevelFilter::INFO); //! info!("This will be logged"); //! ``` //! //! ## Note //! //! The [`Layer`] implementation is unable to implement downcasting functionality, //! so certain [`Layer`] will fail to downcast if wrapped in a `reload::Layer`. //! //! If you only want to be able to dynamically change the //! `Filter` on a layer, prefer wrapping that `Filter` in the `reload::Layer`. //! //! [`Filter` trait]: crate::layer::Filter //! [`Layer` type]: Layer //! [`Layer` trait]: super::layer::Layer use crate::layer; use crate::sync::RwLock; use core::any::TypeId; use std::{ error, fmt, marker::PhantomData, sync::{Arc, Weak}, }; use tracing_core::{ callsite, span, subscriber::{Interest, Subscriber}, Dispatch, Event, LevelFilter, Metadata, }; /// Wraps a `Layer` or `Filter`, allowing it to be reloaded dynamically at runtime. #[derive(Debug)] pub struct Layer { // TODO(eliza): this once used a `crossbeam_util::ShardedRwLock`. We may // eventually wish to replace it with a sharded lock implementation on top // of our internal `RwLock` wrapper type. If possible, we should profile // this first to determine if it's necessary. inner: Arc>, _s: PhantomData, } /// Allows reloading the state of an associated [`Layer`](crate::layer::Layer). #[derive(Debug)] pub struct Handle { inner: Weak>, _s: PhantomData, } /// Indicates that an error occurred when reloading a layer. #[derive(Debug)] pub struct Error { kind: ErrorKind, } #[derive(Debug)] enum ErrorKind { SubscriberGone, Poisoned, } // ===== impl Layer ===== impl crate::Layer for Layer where L: crate::Layer + 'static, S: Subscriber, { fn on_register_dispatch(&self, subscriber: &Dispatch) { try_lock!(self.inner.read()).on_register_dispatch(subscriber); } fn on_layer(&mut self, subscriber: &mut S) { try_lock!(self.inner.write(), else return).on_layer(subscriber); } #[inline] fn register_callsite(&self, metadata: &'static Metadata<'static>) -> Interest { try_lock!(self.inner.read(), else return Interest::sometimes()).register_callsite(metadata) } #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: layer::Context<'_, S>) -> bool { try_lock!(self.inner.read(), else return false).enabled(metadata, ctx) } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_record(span, values, ctx) } #[inline] fn on_follows_from(&self, span: &span::Id, follows: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_follows_from(span, follows, ctx) } #[inline] fn event_enabled(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) -> bool { try_lock!(self.inner.read(), else return false).event_enabled(event, ctx) } #[inline] fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_event(event, ctx) } #[inline] fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_enter(id, ctx) } #[inline] fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_exit(id, ctx) } #[inline] fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_close(id, ctx) } #[inline] fn on_id_change(&self, old: &span::Id, new: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_id_change(old, new, ctx) } #[inline] fn max_level_hint(&self) -> Option { try_lock!(self.inner.read(), else return None).max_level_hint() } #[doc(hidden)] unsafe fn downcast_raw(&self, id: TypeId) -> Option<*const ()> { // Safety: it is generally unsafe to downcast through a reload, because // the pointer can be invalidated after the lock is dropped. // `NoneLayerMarker` is a special case because it // is never dereferenced. // // Additionally, even if the marker type *is* dereferenced (which it // never will be), the pointer should be valid even if the subscriber // is reloaded, because all `NoneLayerMarker` pointers that we return // actually point to the global static singleton `NoneLayerMarker`, // rather than to a field inside the lock. if id == TypeId::of::() { return try_lock!(self.inner.read(), else return None).downcast_raw(id); } None } } // ===== impl Filter ===== #[cfg(all(feature = "registry", feature = "std"))] #[cfg_attr(docsrs, doc(cfg(all(feature = "registry", feature = "std"))))] impl crate::layer::Filter for Layer where L: crate::layer::Filter + 'static, S: Subscriber, { #[inline] fn callsite_enabled(&self, metadata: &'static Metadata<'static>) -> Interest { try_lock!(self.inner.read(), else return Interest::sometimes()).callsite_enabled(metadata) } #[inline] fn enabled(&self, metadata: &Metadata<'_>, ctx: &layer::Context<'_, S>) -> bool { try_lock!(self.inner.read(), else return false).enabled(metadata, ctx) } #[inline] fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_new_span(attrs, id, ctx) } #[inline] fn on_record(&self, span: &span::Id, values: &span::Record<'_>, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_record(span, values, ctx) } #[inline] fn on_enter(&self, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_enter(id, ctx) } #[inline] fn on_exit(&self, id: &span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_exit(id, ctx) } #[inline] fn on_close(&self, id: span::Id, ctx: layer::Context<'_, S>) { try_lock!(self.inner.read()).on_close(id, ctx) } #[inline] fn max_level_hint(&self) -> Option { try_lock!(self.inner.read(), else return None).max_level_hint() } } impl Layer { /// Wraps the given [`Layer`] or [`Filter`], returning a `reload::Layer` /// and a `Handle` that allows the inner value to be modified at runtime. /// /// [`Layer`]: crate::layer::Layer /// [`Filter`]: crate::layer::Filter pub fn new(inner: L) -> (Self, Handle) { let this = Self { inner: Arc::new(RwLock::new(inner)), _s: PhantomData, }; let handle = this.handle(); (this, handle) } /// Returns a `Handle` that can be used to reload the wrapped [`Layer`] or [`Filter`]. /// /// [`Layer`]: crate::layer::Layer /// [`Filter`]: crate::filter::Filter pub fn handle(&self) -> Handle { Handle { inner: Arc::downgrade(&self.inner), _s: PhantomData, } } } // ===== impl Handle ===== impl Handle { /// Replace the current [`Layer`] or [`Filter`] with the provided `new_value`. /// /// [`Handle::reload`] cannot be used with the [`Filtered`] layer; use /// [`Handle::modify`] instead (see [this issue] for additional details). /// /// However, if the _only_ the [`Filter`] needs to be modified, use /// `reload::Layer` to wrap the `Filter` directly. /// /// [`Layer`]: crate::layer::Layer /// [`Filter`]: crate::layer::Filter /// [`Filtered`]: crate::filter::Filtered /// /// [this issue]: https://github.com/tokio-rs/tracing/issues/1629 pub fn reload(&self, new_value: impl Into) -> Result<(), Error> { self.modify(|layer| { *layer = new_value.into(); }) } /// Invokes a closure with a mutable reference to the current layer or filter, /// allowing it to be modified in place. pub fn modify(&self, f: impl FnOnce(&mut L)) -> Result<(), Error> { let inner = self.inner.upgrade().ok_or(Error { kind: ErrorKind::SubscriberGone, })?; let mut lock = try_lock!(inner.write(), else return Err(Error::poisoned())); f(&mut *lock); // Release the lock before rebuilding the interest cache, as that // function will lock the new layer. drop(lock); callsite::rebuild_interest_cache(); Ok(()) } /// Returns a clone of the layer or filter's current value if it still exists. /// Otherwise, if the subscriber has been dropped, returns `None`. pub fn clone_current(&self) -> Option where L: Clone, { self.with_current(L::clone).ok() } /// Invokes a closure with a borrowed reference to the current layer or filter, /// returning the result (or an error if the subscriber no longer exists). pub fn with_current(&self, f: impl FnOnce(&L) -> T) -> Result { let inner = self.inner.upgrade().ok_or(Error { kind: ErrorKind::SubscriberGone, })?; let inner = try_lock!(inner.read(), else return Err(Error::poisoned())); Ok(f(&*inner)) } } impl Clone for Handle { fn clone(&self) -> Self { Handle { inner: self.inner.clone(), _s: PhantomData, } } } // ===== impl Error ===== impl Error { fn poisoned() -> Self { Self { kind: ErrorKind::Poisoned, } } /// Returns `true` if this error occurred because the layer was poisoned by /// a panic on another thread. pub fn is_poisoned(&self) -> bool { matches!(self.kind, ErrorKind::Poisoned) } /// Returns `true` if this error occurred because the `Subscriber` /// containing the reloadable layer was dropped. pub fn is_dropped(&self) -> bool { matches!(self.kind, ErrorKind::SubscriberGone) } } impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let msg = match self.kind { ErrorKind::SubscriberGone => "subscriber no longer exists", ErrorKind::Poisoned => "lock poisoned", }; f.pad(msg) } } impl error::Error for Error {} tracing-subscriber-0.3.18/src/sync.rs000064400000000000000000000037211046102023000156210ustar 00000000000000//! Abstracts over sync primitive implementations. //! //! Optionally, we allow the Rust standard library's `RwLock` to be replaced //! with the `parking_lot` crate's implementation. This may provide improved //! performance in some cases. However, the `parking_lot` dependency is an //! opt-in feature flag. Because `parking_lot::RwLock` has a slightly different //! API than `std::sync::RwLock` (it does not support poisoning on panics), we //! wrap it with a type that provides the same method signatures. This allows us //! to transparently swap `parking_lot` in without changing code at the callsite. #[allow(unused_imports)] // may be used later; pub(crate) use std::sync::{LockResult, PoisonError, TryLockResult}; #[cfg(not(feature = "parking_lot"))] pub(crate) use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; #[cfg(feature = "parking_lot")] pub(crate) use self::parking_lot_impl::*; #[cfg(feature = "parking_lot")] mod parking_lot_impl { pub(crate) use parking_lot::{RwLockReadGuard, RwLockWriteGuard}; use std::sync::{LockResult, TryLockError, TryLockResult}; #[derive(Debug)] pub(crate) struct RwLock { inner: parking_lot::RwLock, } impl RwLock { pub(crate) fn new(val: T) -> Self { Self { inner: parking_lot::RwLock::new(val), } } #[inline] pub(crate) fn get_mut(&mut self) -> LockResult<&mut T> { Ok(self.inner.get_mut()) } #[inline] pub(crate) fn read(&self) -> LockResult> { Ok(self.inner.read()) } #[inline] #[allow(dead_code)] // may be used later; pub(crate) fn try_read(&self) -> TryLockResult> { self.inner.try_read().ok_or(TryLockError::WouldBlock) } #[inline] pub(crate) fn write(&self) -> LockResult> { Ok(self.inner.write()) } } } tracing-subscriber-0.3.18/src/util.rs000064400000000000000000000132011046102023000156140ustar 00000000000000//! Extension traits and other utilities to make working with subscribers more //! ergonomic. use core::fmt; #[cfg(feature = "std")] use std::error::Error; use tracing_core::dispatcher::{self, Dispatch}; #[cfg(feature = "tracing-log")] use tracing_log::AsLog; /// Extension trait adding utility methods for subscriber initialization. /// /// This trait provides extension methods to make configuring and setting a /// [default subscriber] more ergonomic. It is automatically implemented for all /// types that can be converted into a [trace dispatcher]. Since `Dispatch` /// implements `From` for all `T: Subscriber`, all `Subscriber` /// implementations will implement this extension trait as well. Types which /// can be converted into `Subscriber`s, such as builders that construct a /// `Subscriber`, may implement `Into`, and will also receive an /// implementation of this trait. /// /// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [trace dispatcher]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html pub trait SubscriberInitExt where Self: Into, { /// Sets `self` as the [default subscriber] in the current scope, returning a /// guard that will unset it when dropped. /// /// If the "tracing-log" feature flag is enabled, this will also initialize /// a [`log`] compatibility layer. This allows the subscriber to consume /// `log::Record`s as though they were `tracing` `Event`s. /// /// [default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log #[cfg(feature = "std")] #[cfg_attr(docsrs, doc(cfg(feature = "std")))] fn set_default(self) -> dispatcher::DefaultGuard { #[cfg(feature = "tracing-log")] let _ = tracing_log::LogTracer::init(); dispatcher::set_default(&self.into()) } /// Attempts to set `self` as the [global default subscriber] in the current /// scope, returning an error if one is already set. /// /// If the "tracing-log" feature flag is enabled, this will also attempt to /// initialize a [`log`] compatibility layer. This allows the subscriber to /// consume `log::Record`s as though they were `tracing` `Event`s. /// /// This method returns an error if a global default subscriber has already /// been set, or if a `log` logger has already been set (when the /// "tracing-log" feature is enabled). /// /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log fn try_init(self) -> Result<(), TryInitError> { dispatcher::set_global_default(self.into()).map_err(TryInitError::new)?; // Since we are setting the global default subscriber, we can // opportunistically go ahead and set its global max level hint as // the max level for the `log` crate as well. This should make // skipping `log` diagnostics much faster. #[cfg(feature = "tracing-log")] tracing_log::LogTracer::builder() // Note that we must call this *after* setting the global default // subscriber, so that we get its max level hint. .with_max_level(tracing_core::LevelFilter::current().as_log()) .init() .map_err(TryInitError::new)?; Ok(()) } /// Attempts to set `self` as the [global default subscriber] in the current /// scope, panicking if this fails. /// /// If the "tracing-log" feature flag is enabled, this will also attempt to /// initialize a [`log`] compatibility layer. This allows the subscriber to /// consume `log::Record`s as though they were `tracing` `Event`s. /// /// This method panics if a global default subscriber has already been set, /// or if a `log` logger has already been set (when the "tracing-log" /// feature is enabled). /// /// [global default subscriber]: https://docs.rs/tracing/0.1.21/tracing/dispatcher/index.html#setting-the-default-subscriber /// [`log`]: https://crates.io/log fn init(self) { self.try_init() .expect("failed to set global default subscriber") } } impl SubscriberInitExt for T where T: Into {} /// Error returned by [`try_init`](SubscriberInitExt::try_init) if a global default subscriber could not be initialized. pub struct TryInitError { #[cfg(feature = "std")] inner: Box, #[cfg(not(feature = "std"))] _p: (), } // ==== impl TryInitError ==== impl TryInitError { #[cfg(feature = "std")] fn new(e: impl Into>) -> Self { Self { inner: e.into() } } #[cfg(not(feature = "std"))] fn new(_: T) -> Self { Self { _p: () } } } impl fmt::Debug for TryInitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { fmt::Debug::fmt(&self.inner, f) } #[cfg(not(feature = "std"))] { f.write_str("TryInitError(())") } } } impl fmt::Display for TryInitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { #[cfg(feature = "std")] { fmt::Display::fmt(&self.inner, f) } #[cfg(not(feature = "std"))] { f.write_str("failed to set global default subscriber") } } } #[cfg(feature = "std")] impl Error for TryInitError { fn source(&self) -> Option<&(dyn Error + 'static)> { self.inner.source() } } tracing-subscriber-0.3.18/tests/cached_layer_filters_dont_break_other_layers.rs000064400000000000000000000073361046102023000263310ustar 00000000000000#![cfg(feature = "registry")] use tracing::Level; use tracing_mock::{ expect, layer::{self, MockLayer}, subscriber, }; use tracing_subscriber::{filter::LevelFilter, prelude::*}; #[test] fn layer_filters() { let (unfiltered, unfiltered_handle) = unfiltered("unfiltered"); let (filtered, filtered_handle) = filtered("filtered"); let _subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered.with_filter(filter())) .set_default(); events(); unfiltered_handle.assert_finished(); filtered_handle.assert_finished(); } #[test] fn layered_layer_filters() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let unfiltered = unfiltered1.and_then(unfiltered2); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let filtered = filtered1 .with_filter(filter()) .and_then(filtered2.with_filter(filter())); let _subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn out_of_order() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let _subscriber = tracing_subscriber::registry() .with(unfiltered1) .with(filtered1.with_filter(filter())) .with(unfiltered2) .with(filtered2.with_filter(filter())) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn mixed_layered() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let layered1 = filtered1.with_filter(filter()).and_then(unfiltered1); let layered2 = unfiltered2.and_then(filtered2.with_filter(filter())); let _subscriber = tracing_subscriber::registry() .with(layered1) .with(layered2) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } fn filter() -> LevelFilter { LevelFilter::INFO } fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } tracing-subscriber-0.3.18/tests/duplicate_spans.rs000064400000000000000000000030641046102023000203760ustar 00000000000000#![cfg(all(feature = "env-filter", feature = "fmt"))] use tracing::{self, subscriber::with_default, Span}; use tracing_subscriber::{filter::EnvFilter, FmtSubscriber}; #[test] fn duplicate_spans() { let subscriber = FmtSubscriber::builder() .with_env_filter(EnvFilter::new("[root]=debug")) .finish(); with_default(subscriber, || { let root = tracing::debug_span!("root"); root.in_scope(|| { // root: assert_eq!(root, Span::current(), "Current span must be 'root'"); let leaf = tracing::debug_span!("leaf"); leaf.in_scope(|| { // root:leaf: assert_eq!(leaf, Span::current(), "Current span must be 'leaf'"); root.in_scope(|| { // root:leaf: assert_eq!( leaf, Span::current(), "Current span must be 'leaf' after entering twice the 'root' span" ); }) }); // root: assert_eq!( root, Span::current(), "Current span must be root ('leaf' exited, nested 'root' exited)" ); root.in_scope(|| { assert_eq!(root, Span::current(), "Current span must be root"); }); // root: assert_eq!( root, Span::current(), "Current span must still be root after exiting nested 'root'" ); }); }); } tracing-subscriber-0.3.18/tests/env_filter/main.rs000064400000000000000000000447231046102023000203100ustar 00000000000000#![cfg(feature = "env-filter")] mod per_layer; use tracing::{self, subscriber::with_default, Level}; use tracing_mock::{expect, layer, span, subscriber}; use tracing_subscriber::{ filter::{EnvFilter, LevelFilter}, prelude::*, Registry, }; #[test] fn level_filter_event() { let filter: EnvFilter = "info".parse().expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "foo", "this should also be disabled"); tracing::warn!(target: "foo", "this should be enabled"); tracing::error!("this should be enabled too"); }); finished.assert_finished(); } #[test] fn same_name_spans() { let filter: EnvFilter = "[foo{bar}]=trace,[foo{baz}]=trace" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("bar")), ) .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("baz")), ) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace_span!("foo", bar = 1); tracing::trace_span!("foo", baz = 1); }); finished.assert_finished(); } #[test] fn level_filter_event_with_target() { let filter: EnvFilter = "info,stuff=debug".parse().expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); }); finished.assert_finished(); } #[test] fn level_filter_event_with_target_and_span_global() { let filter: EnvFilter = "info,stuff[cool_span]=debug" .parse() .expect("filter should parse"); let cool_span = span::named("cool_span"); let uncool_span = span::named("uncool_span"); let (layer, handle) = layer::mock() .enter(cool_span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![cool_span.clone()]), ) .exit(cool_span) .enter(uncool_span.clone()) .exit(uncool_span) .only() .run_with_handle(); let subscriber = Registry::default().with(filter).with(layer); with_default(subscriber, || { { let _span = tracing::info_span!(target: "stuff", "cool_span").entered(); tracing::debug!("this should be enabled"); } tracing::debug!("should also be disabled"); { let _span = tracing::info_span!("uncool_span").entered(); tracing::debug!("this should be disabled"); } }); handle.assert_finished(); } #[test] fn not_order_dependent() { // this test reproduces tokio-rs/tracing#623 let filter: EnvFilter = "stuff=debug,info".parse().expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); }); finished.assert_finished(); } #[test] fn add_directive_enables_event() { // this test reproduces tokio-rs/tracing#591 // by default, use info level let mut filter = EnvFilter::new(LevelFilter::INFO.to_string()); // overwrite with a more specific directive filter = filter.add_directive("hello=trace".parse().expect("directive should parse")); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO).with_target("hello")) .event(expect::event().at_level(Level::TRACE).with_target("hello")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::info!(target: "hello", "hello info"); tracing::trace!(target: "hello", "hello trace"); }); finished.assert_finished(); } #[test] fn span_name_filter_is_dynamic() { let filter: EnvFilter = "info,[cool_span]=debug" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO)) .enter(expect::span().named("cool_span")) .event(expect::event().at_level(Level::DEBUG)) .enter(expect::span().named("uncool_span")) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::DEBUG)) .exit(expect::span().named("uncool_span")) .exit(expect::span().named("cool_span")) .enter(expect::span().named("uncool_span")) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .exit(expect::span().named("uncool_span")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); let cool_span = tracing::info_span!("cool_span"); let uncool_span = tracing::info_span!("uncool_span"); { let _enter = cool_span.enter(); tracing::debug!("i'm a cool event"); tracing::trace!("i'm cool, but not cool enough"); let _enter2 = uncool_span.enter(); tracing::warn!("warning: extremely cool!"); tracing::debug!("i'm still cool"); } let _enter = uncool_span.enter(); tracing::warn!("warning: not that cool"); tracing::trace!("im not cool enough"); tracing::error!("uncool error"); }); finished.assert_finished(); } #[test] fn method_name_resolution() { #[allow(unused_imports)] use tracing_subscriber::layer::{Filter, Layer}; let filter = EnvFilter::new("hello_world=info"); filter.max_level_hint(); } // contains the same tests as the first half of this file // but using EnvFilter as a `Filter`, not as a `Layer` mod per_layer_filter { use super::*; #[test] fn level_filter_event() { let filter: EnvFilter = "info".parse().expect("filter should parse"); let (layer, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "foo", "this should also be disabled"); tracing::warn!(target: "foo", "this should be enabled"); tracing::error!("this should be enabled too"); handle.assert_finished(); } #[test] fn same_name_spans() { let filter: EnvFilter = "[foo{bar}]=trace,[foo{baz}]=trace" .parse() .expect("filter should parse"); let (layer, handle) = layer::mock() .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("bar")), ) .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("baz")), ) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace_span!("foo", bar = 1); tracing::trace_span!("foo", baz = 1); handle.assert_finished(); } #[test] fn level_filter_event_with_target() { let filter: EnvFilter = "info,stuff=debug".parse().expect("filter should parse"); let (layer, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); handle.assert_finished(); } #[test] fn level_filter_event_with_target_and_span() { let filter: EnvFilter = "stuff[cool_span]=debug" .parse() .expect("filter should parse"); let cool_span = span::named("cool_span"); let (layer, handle) = layer::mock() .enter(cool_span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![cool_span.clone()]), ) .exit(cool_span) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); { let _span = tracing::info_span!(target: "stuff", "cool_span").entered(); tracing::debug!("this should be enabled"); } tracing::debug!("should also be disabled"); { let _span = tracing::info_span!("uncool_span").entered(); tracing::debug!("this should be disabled"); } handle.assert_finished(); } #[test] fn not_order_dependent() { // this test reproduces tokio-rs/tracing#623 let filter: EnvFilter = "stuff=debug,info".parse().expect("filter should parse"); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); finished.assert_finished(); } #[test] fn add_directive_enables_event() { // this test reproduces tokio-rs/tracing#591 // by default, use info level let mut filter = EnvFilter::new(LevelFilter::INFO.to_string()); // overwrite with a more specific directive filter = filter.add_directive("hello=trace".parse().expect("directive should parse")); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO).with_target("hello")) .event(expect::event().at_level(Level::TRACE).with_target("hello")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::info!(target: "hello", "hello info"); tracing::trace!(target: "hello", "hello trace"); finished.assert_finished(); } #[test] fn span_name_filter_is_dynamic() { let filter: EnvFilter = "info,[cool_span]=debug" .parse() .expect("filter should parse"); let cool_span = span::named("cool_span"); let uncool_span = span::named("uncool_span"); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO)) .enter(cool_span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![cool_span.clone()]), ) .enter(uncool_span.clone()) .event( expect::event() .at_level(Level::WARN) .in_scope(vec![uncool_span.clone()]), ) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![uncool_span.clone()]), ) .exit(uncool_span.clone()) .exit(cool_span) .enter(uncool_span.clone()) .event( expect::event() .at_level(Level::WARN) .in_scope(vec![uncool_span.clone()]), ) .event( expect::event() .at_level(Level::ERROR) .in_scope(vec![uncool_span.clone()]), ) .exit(uncool_span) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); let cool_span = tracing::info_span!("cool_span"); let uncool_span = tracing::info_span!("uncool_span"); { let _enter = cool_span.enter(); tracing::debug!("i'm a cool event"); tracing::trace!("i'm cool, but not cool enough"); let _enter2 = uncool_span.enter(); tracing::warn!("warning: extremely cool!"); tracing::debug!("i'm still cool"); } { let _enter = uncool_span.enter(); tracing::warn!("warning: not that cool"); tracing::trace!("im not cool enough"); tracing::error!("uncool error"); } finished.assert_finished(); } #[test] fn multiple_dynamic_filters() { // Test that multiple dynamic (span) filters only apply to the layers // they're attached to. let (layer1, handle1) = { let span = span::named("span1"); let filter: EnvFilter = "[span1]=debug".parse().expect("filter 1 should parse"); let (layer, handle) = layer::named("layer1") .enter(span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![span.clone()]), ) .exit(span) .only() .run_with_handle(); (layer.with_filter(filter), handle) }; let (layer2, handle2) = { let span = span::named("span2"); let filter: EnvFilter = "[span2]=info".parse().expect("filter 2 should parse"); let (layer, handle) = layer::named("layer2") .enter(span.clone()) .event( expect::event() .at_level(Level::INFO) .in_scope(vec![span.clone()]), ) .exit(span) .only() .run_with_handle(); (layer.with_filter(filter), handle) }; let _subscriber = tracing_subscriber::registry() .with(layer1) .with(layer2) .set_default(); tracing::info_span!("span1").in_scope(|| { tracing::debug!("hello from span 1"); tracing::trace!("not enabled"); }); tracing::info_span!("span2").in_scope(|| { tracing::info!("hello from span 2"); tracing::debug!("not enabled"); }); handle1.assert_finished(); handle2.assert_finished(); } } tracing-subscriber-0.3.18/tests/env_filter/per_layer.rs000064400000000000000000000230611046102023000213360ustar 00000000000000//! Tests for using `EnvFilter` as a per-layer filter (rather than a global //! `Layer` filter). #![cfg(feature = "registry")] use super::*; use tracing_mock::{layer, span}; #[test] fn level_filter_event() { let filter: EnvFilter = "info".parse().expect("filter should parse"); let (layer, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "foo", "this should also be disabled"); tracing::warn!(target: "foo", "this should be enabled"); tracing::error!("this should be enabled too"); handle.assert_finished(); } #[test] fn same_name_spans() { let filter: EnvFilter = "[foo{bar}]=trace,[foo{baz}]=trace" .parse() .expect("filter should parse"); let (layer, handle) = layer::mock() .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("bar")), ) .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("baz")), ) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace_span!("foo", bar = 1); tracing::trace_span!("foo", baz = 1); handle.assert_finished(); } #[test] fn level_filter_event_with_target() { let filter: EnvFilter = "info,stuff=debug".parse().expect("filter should parse"); let (layer, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); handle.assert_finished(); } #[test] fn level_filter_event_with_target_and_span() { let filter: EnvFilter = "stuff[cool_span]=debug" .parse() .expect("filter should parse"); let cool_span = span::named("cool_span"); let (layer, handle) = layer::mock() .enter(cool_span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![cool_span.clone()]), ) .exit(cool_span) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); { let _span = tracing::info_span!(target: "stuff", "cool_span").entered(); tracing::debug!("this should be enabled"); } tracing::debug!("should also be disabled"); { let _span = tracing::info_span!("uncool_span").entered(); tracing::debug!("this should be disabled"); } handle.assert_finished(); } #[test] fn not_order_dependent() { // this test reproduces tokio-rs/tracing#623 let filter: EnvFilter = "stuff=debug,info".parse().expect("filter should parse"); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::DEBUG).with_target("stuff")) .event(expect::event().at_level(Level::WARN).with_target("stuff")) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::ERROR).with_target("stuff")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); tracing::debug!(target: "stuff", "this should be enabled"); tracing::debug!("but this shouldn't"); tracing::trace!(target: "stuff", "and neither should this"); tracing::warn!(target: "stuff", "this should be enabled"); tracing::error!("this should be enabled too"); tracing::error!(target: "stuff", "this should be enabled also"); finished.assert_finished(); } #[test] fn add_directive_enables_event() { // this test reproduces tokio-rs/tracing#591 // by default, use info level let mut filter = EnvFilter::new(LevelFilter::INFO.to_string()); // overwrite with a more specific directive filter = filter.add_directive("hello=trace".parse().expect("directive should parse")); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO).with_target("hello")) .event(expect::event().at_level(Level::TRACE).with_target("hello")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::info!(target: "hello", "hello info"); tracing::trace!(target: "hello", "hello trace"); finished.assert_finished(); } #[test] fn span_name_filter_is_dynamic() { let filter: EnvFilter = "info,[cool_span]=debug" .parse() .expect("filter should parse"); let cool_span = span::named("cool_span"); let uncool_span = span::named("uncool_span"); let (layer, finished) = layer::mock() .event(expect::event().at_level(Level::INFO)) .enter(cool_span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![cool_span.clone()]), ) .enter(uncool_span.clone()) .event( expect::event() .at_level(Level::WARN) .in_scope(vec![uncool_span.clone()]), ) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![uncool_span.clone()]), ) .exit(uncool_span.clone()) .exit(cool_span) .enter(uncool_span.clone()) .event( expect::event() .at_level(Level::WARN) .in_scope(vec![uncool_span.clone()]), ) .event( expect::event() .at_level(Level::ERROR) .in_scope(vec![uncool_span.clone()]), ) .exit(uncool_span) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); tracing::trace!("this should be disabled"); tracing::info!("this shouldn't be"); let cool_span = tracing::info_span!("cool_span"); let uncool_span = tracing::info_span!("uncool_span"); { let _enter = cool_span.enter(); tracing::debug!("i'm a cool event"); tracing::trace!("i'm cool, but not cool enough"); let _enter2 = uncool_span.enter(); tracing::warn!("warning: extremely cool!"); tracing::debug!("i'm still cool"); } { let _enter = uncool_span.enter(); tracing::warn!("warning: not that cool"); tracing::trace!("im not cool enough"); tracing::error!("uncool error"); } finished.assert_finished(); } #[test] fn multiple_dynamic_filters() { // Test that multiple dynamic (span) filters only apply to the layers // they're attached to. let (layer1, handle1) = { let span = span::named("span1"); let filter: EnvFilter = "[span1]=debug".parse().expect("filter 1 should parse"); let (layer, handle) = layer::named("layer1") .enter(span.clone()) .event( expect::event() .at_level(Level::DEBUG) .in_scope(vec![span.clone()]), ) .exit(span) .only() .run_with_handle(); (layer.with_filter(filter), handle) }; let (layer2, handle2) = { let span = span::named("span2"); let filter: EnvFilter = "[span2]=info".parse().expect("filter 2 should parse"); let (layer, handle) = layer::named("layer2") .enter(span.clone()) .event( expect::event() .at_level(Level::INFO) .in_scope(vec![span.clone()]), ) .exit(span) .only() .run_with_handle(); (layer.with_filter(filter), handle) }; let _subscriber = tracing_subscriber::registry() .with(layer1) .with(layer2) .set_default(); tracing::info_span!("span1").in_scope(|| { tracing::debug!("hello from span 1"); tracing::trace!("not enabled"); }); tracing::info_span!("span2").in_scope(|| { tracing::info!("hello from span 2"); tracing::debug!("not enabled"); }); handle1.assert_finished(); handle2.assert_finished(); } tracing-subscriber-0.3.18/tests/event_enabling.rs000064400000000000000000000043141046102023000201770ustar 00000000000000#![cfg(feature = "registry")] use std::sync::{Arc, Mutex}; use tracing::{subscriber::with_default, Event, Metadata, Subscriber}; use tracing_subscriber::{layer::Context, prelude::*, registry, Layer}; struct TrackingLayer { enabled: bool, event_enabled_count: Arc>, event_enabled: bool, on_event_count: Arc>, } impl Layer for TrackingLayer where C: Subscriber + Send + Sync + 'static, { fn enabled(&self, _metadata: &Metadata<'_>, _ctx: Context<'_, C>) -> bool { self.enabled } fn event_enabled(&self, _event: &Event<'_>, _ctx: Context<'_, C>) -> bool { *self.event_enabled_count.lock().unwrap() += 1; self.event_enabled } fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, C>) { *self.on_event_count.lock().unwrap() += 1; } } #[test] fn event_enabled_is_only_called_once() { let event_enabled_count = Arc::new(Mutex::default()); let count = event_enabled_count.clone(); let subscriber = registry().with(TrackingLayer { enabled: true, event_enabled_count, event_enabled: true, on_event_count: Arc::new(Mutex::default()), }); with_default(subscriber, || { tracing::error!("hiya!"); }); assert_eq!(1, *count.lock().unwrap()); } #[test] fn event_enabled_not_called_when_not_enabled() { let event_enabled_count = Arc::new(Mutex::default()); let count = event_enabled_count.clone(); let subscriber = registry().with(TrackingLayer { enabled: false, event_enabled_count, event_enabled: true, on_event_count: Arc::new(Mutex::default()), }); with_default(subscriber, || { tracing::error!("hiya!"); }); assert_eq!(0, *count.lock().unwrap()); } #[test] fn event_disabled_does_disable_event() { let on_event_count = Arc::new(Mutex::default()); let count = on_event_count.clone(); let subscriber = registry().with(TrackingLayer { enabled: true, event_enabled_count: Arc::new(Mutex::default()), event_enabled: false, on_event_count, }); with_default(subscriber, || { tracing::error!("hiya!"); }); assert_eq!(0, *count.lock().unwrap()); } tracing-subscriber-0.3.18/tests/field_filter.rs000064400000000000000000000067061046102023000176560ustar 00000000000000#![cfg(feature = "env-filter")] use tracing::{self, subscriber::with_default, Level}; use tracing_mock::*; use tracing_subscriber::{filter::EnvFilter, prelude::*}; #[test] #[cfg_attr(not(flaky_tests), ignore)] fn field_filter_events() { let filter: EnvFilter = "[{thing}]=debug".parse().expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event( expect::event() .at_level(Level::INFO) .with_fields(expect::field("thing")), ) .event( expect::event() .at_level(Level::DEBUG) .with_fields(expect::field("thing")), ) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!(disabled = true); tracing::info!("also disabled"); tracing::info!(thing = 1); tracing::debug!(thing = 2); tracing::trace!(thing = 3); }); finished.assert_finished(); } #[test] #[cfg_attr(not(flaky_tests), ignore)] fn field_filter_spans() { let filter: EnvFilter = "[{enabled=true}]=debug" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .enter(expect::span().named("span1")) .event( expect::event() .at_level(Level::INFO) .with_fields(expect::field("something")), ) .exit(expect::span().named("span1")) .enter(expect::span().named("span2")) .exit(expect::span().named("span2")) .enter(expect::span().named("span3")) .event( expect::event() .at_level(Level::DEBUG) .with_fields(expect::field("something")), ) .exit(expect::span().named("span3")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!("disabled"); tracing::info!("also disabled"); tracing::info_span!("span1", enabled = true).in_scope(|| { tracing::info!(something = 1); }); tracing::debug_span!("span2", enabled = false, foo = "hi").in_scope(|| { tracing::warn!(something = 2); }); tracing::trace_span!("span3", enabled = true, answer = 42).in_scope(|| { tracing::debug!(something = 2); }); }); finished.assert_finished(); } #[test] fn record_after_created() { let filter: EnvFilter = "[{enabled=true}]=debug" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .enter(expect::span().named("span")) .exit(expect::span().named("span")) .record( expect::span().named("span"), expect::field("enabled").with_value(&true), ) .enter(expect::span().named("span")) .event(expect::event().at_level(Level::DEBUG)) .exit(expect::span().named("span")) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { let span = tracing::info_span!("span", enabled = false); span.in_scope(|| { tracing::debug!("i'm disabled!"); }); span.record("enabled", true); span.in_scope(|| { tracing::debug!("i'm enabled!"); }); tracing::debug!("i'm also disabled"); }); finished.assert_finished(); } tracing-subscriber-0.3.18/tests/filter_log.rs000064400000000000000000000036171046102023000173520ustar 00000000000000#![cfg(all(feature = "env-filter", feature = "tracing-log"))] use tracing::{self, Level}; use tracing_mock::*; use tracing_subscriber::{filter::EnvFilter, prelude::*}; mod my_module { pub(crate) fn test_records() { log::trace!("this should be disabled"); log::info!("this shouldn't be"); log::debug!("this should be disabled"); log::warn!("this should be enabled"); log::warn!(target: "something else", "this shouldn't be enabled"); log::error!("this should be enabled too"); } pub(crate) fn test_log_enabled() { assert!( log::log_enabled!(log::Level::Info), "info should be enabled inside `my_module`" ); assert!( !log::log_enabled!(log::Level::Debug), "debug should not be enabled inside `my_module`" ); assert!( log::log_enabled!(log::Level::Warn), "warn should be enabled inside `my_module`" ); } } #[test] fn log_is_enabled() { let filter: EnvFilter = "filter_log::my_module=info" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); // Note: we have to set the global default in order to set the `log` max // level, which can only be set once. subscriber.with(filter).init(); my_module::test_records(); log::info!("this is disabled"); my_module::test_log_enabled(); assert!( !log::log_enabled!(log::Level::Info), "info should not be enabled outside `my_module`" ); assert!( !log::log_enabled!(log::Level::Warn), "warn should not be enabled outside `my_module`" ); finished.assert_finished(); } tracing-subscriber-0.3.18/tests/fmt_max_level_hint.rs000064400000000000000000000004071046102023000210620ustar 00000000000000#![cfg(feature = "fmt")] use tracing_subscriber::filter::LevelFilter; #[test] fn fmt_sets_max_level_hint() { tracing_subscriber::fmt() .with_max_level(LevelFilter::DEBUG) .init(); assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); } tracing-subscriber-0.3.18/tests/hinted_layer_filters_dont_break_other_layers.rs000064400000000000000000000104251046102023000263660ustar 00000000000000#![cfg(feature = "registry")] use tracing::{Level, Metadata, Subscriber}; use tracing_mock::{ expect, layer, layer::MockLayer, subscriber::{self}, }; use tracing_subscriber::{filter::DynFilterFn, layer::Context, prelude::*}; #[test] fn layer_filters() { let (unfiltered, unfiltered_handle) = unfiltered("unfiltered"); let (filtered, filtered_handle) = filtered("filtered"); let subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered.with_filter(filter())); assert_eq!(subscriber.max_level_hint(), None); let _subscriber = subscriber.set_default(); events(); unfiltered_handle.assert_finished(); filtered_handle.assert_finished(); } #[test] fn layered_layer_filters() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let unfiltered = unfiltered1.and_then(unfiltered2); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let filtered = filtered1 .with_filter(filter()) .and_then(filtered2.with_filter(filter())); let subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered); assert_eq!(subscriber.max_level_hint(), None); let _subscriber = subscriber.set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn out_of_order() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let subscriber = tracing_subscriber::registry() .with(unfiltered1) .with(filtered1.with_filter(filter())) .with(unfiltered2) .with(filtered2.with_filter(filter())); assert_eq!(subscriber.max_level_hint(), None); let _subscriber = subscriber.set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn mixed_layered() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let layered1 = filtered1.with_filter(filter()).and_then(unfiltered1); let layered2 = unfiltered2.and_then(filtered2.with_filter(filter())); let subscriber = tracing_subscriber::registry().with(layered1).with(layered2); assert_eq!(subscriber.max_level_hint(), None); let _subscriber = subscriber.set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } fn filter() -> DynFilterFn { DynFilterFn::new( (|metadata: &Metadata<'_>, _: &tracing_subscriber::layer::Context<'_, S>| { metadata.level() <= &Level::INFO }) as fn(&Metadata<'_>, &Context<'_, S>) -> bool, ) .with_max_level_hint(Level::INFO) } fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } tracing-subscriber-0.3.18/tests/layer_filter_interests_are_cached.rs000064400000000000000000000034301046102023000241140ustar 00000000000000#![cfg(feature = "registry")] use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; use tracing_mock::{expect, layer}; use tracing_subscriber::{filter, prelude::*}; #[test] fn layer_filter_interests_are_cached() { let seen = Arc::new(Mutex::new(HashMap::new())); let seen2 = seen.clone(); let filter = filter::filter_fn(move |meta| { *seen .lock() .unwrap() .entry(meta.callsite()) .or_insert(0usize) += 1; meta.level() == &Level::INFO }); let (expect, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let subscriber = tracing_subscriber::registry().with(expect.with_filter(filter)); assert!(subscriber.max_level_hint().is_none()); let _subscriber = subscriber.set_default(); fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } events(); { let lock = seen2.lock().unwrap(); for (callsite, &count) in lock.iter() { assert_eq!( count, 1, "callsite {:?} should have been seen 1 time (after first set of events)", callsite ); } } events(); { let lock = seen2.lock().unwrap(); for (callsite, &count) in lock.iter() { assert_eq!( count, 1, "callsite {:?} should have been seen 1 time (after second set of events)", callsite ); } } handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/boxed.rs000064400000000000000000000022351046102023000211640ustar 00000000000000use super::*; use tracing_mock::layer::MockLayer; use tracing_subscriber::{filter, prelude::*, Layer}; fn layer() -> (MockLayer, subscriber::MockHandle) { layer::mock().only().run_with_handle() } fn filter() -> 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) } /// reproduces https://github.com/tokio-rs/tracing/issues/1563#issuecomment-921363629 #[test] fn box_works() { let (layer, handle) = layer(); let layer = Box::new(layer.with_filter(filter())); let _guard = tracing_subscriber::registry().with(layer).set_default(); for i in 0..2 { tracing::info!(i); } handle.assert_finished(); } /// the same as `box_works` but with a type-erased `Box`. #[test] fn dyn_box_works() { let (layer, handle) = layer(); let layer: Box + Send + Sync + 'static> = Box::new(layer.with_filter(filter())); let _guard = tracing_subscriber::registry().with(layer).set_default(); for i in 0..2 { tracing::info!(i); } handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/combinators.rs000064400000000000000000000025131046102023000224020ustar 00000000000000use super::*; use tracing_subscriber::{ filter::{filter_fn, FilterExt, LevelFilter}, prelude::*, }; #[test] fn and() { let (layer, handle) = layer::mock() .event( event::msg("a very interesting event") .at_level(tracing::Level::INFO) .with_target("interesting_target"), ) .only() .run_with_handle(); // Enables spans and events with targets starting with `interesting_target`: let target_filter = filter::filter_fn(|meta| meta.target().starts_with("interesting_target")); // Enables spans and events with levels `INFO` and below: let level_filter = LevelFilter::INFO; // Combine the two filters together, returning a filter that only enables // spans and events that *both* filters will enable: let filter = target_filter.and(level_filter); let _subscriber = tracing_subscriber::registry() .with(layer.with_filter(filter)) .set_default(); // This event will *not* be enabled: tracing::info!("an event with an uninteresting target"); // This event *will* be enabled: tracing::info!(target: "interesting_target", "a very interesting event"); // This event will *not* be enabled: tracing::debug!(target: "interesting_target", "interesting debug event..."); handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/downcast_raw.rs000064400000000000000000000042771046102023000225660ustar 00000000000000use tracing::Subscriber; use tracing_subscriber::filter::Targets; use tracing_subscriber::prelude::*; use tracing_subscriber::Layer; #[test] fn downcast_ref_to_inner_layer_and_filter() { // Test that a filtered layer gives downcast_ref access to // both the layer and the filter. struct WrappedLayer; impl Layer for WrappedLayer where S: Subscriber, S: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>, { } let layer = WrappedLayer; let filter = Targets::new().with_default(tracing::Level::INFO); let registry = tracing_subscriber::registry().with(layer.with_filter(filter)); let dispatch = tracing::dispatcher::Dispatch::new(registry); // The filter is available assert!(dispatch.downcast_ref::().is_some()); // The wrapped layer is available assert!(dispatch.downcast_ref::().is_some()); } #[test] fn forward_downcast_raw_to_layer() { // Test that a filtered layer still gives its wrapped layer a chance to // return a custom struct from downcast_raw. // https://github.com/tokio-rs/tracing/issues/1618 struct WrappedLayer { with_context: WithContext, } struct WithContext; impl Layer for WrappedLayer where S: Subscriber, S: for<'lookup> tracing_subscriber::registry::LookupSpan<'lookup>, { unsafe fn downcast_raw(&self, id: std::any::TypeId) -> Option<*const ()> { match id { id if id == std::any::TypeId::of::() => Some(self as *const _ as *const ()), id if id == std::any::TypeId::of::() => { Some(&self.with_context as *const _ as *const ()) } _ => None, } } } let layer = WrappedLayer { with_context: WithContext, }; let filter = Targets::new().with_default(tracing::Level::INFO); let registry = tracing_subscriber::registry().with(layer.with_filter(filter)); let dispatch = tracing::dispatcher::Dispatch::new(registry); // Types from a custom implementation of `downcast_raw` are available assert!(dispatch.downcast_ref::().is_some()); } tracing-subscriber-0.3.18/tests/layer_filters/filter_scopes.rs000064400000000000000000000122261046102023000227250ustar 00000000000000use super::*; use tracing_mock::{event, expect, layer::MockLayer}; #[test] fn filters_span_scopes() { let (debug_layer, debug_handle) = layer::named("debug") .enter(expect::span().at_level(Level::DEBUG)) .enter(expect::span().at_level(Level::INFO)) .enter(expect::span().at_level(Level::WARN)) .enter(expect::span().at_level(Level::ERROR)) .event(event::msg("hello world").in_scope(vec![ expect::span().at_level(Level::ERROR), expect::span().at_level(Level::WARN), expect::span().at_level(Level::INFO), expect::span().at_level(Level::DEBUG), ])) .exit(expect::span().at_level(Level::ERROR)) .exit(expect::span().at_level(Level::WARN)) .exit(expect::span().at_level(Level::INFO)) .exit(expect::span().at_level(Level::DEBUG)) .only() .run_with_handle(); let (info_layer, info_handle) = layer::named("info") .enter(expect::span().at_level(Level::INFO)) .enter(expect::span().at_level(Level::WARN)) .enter(expect::span().at_level(Level::ERROR)) .event(event::msg("hello world").in_scope(vec![ expect::span().at_level(Level::ERROR), expect::span().at_level(Level::WARN), expect::span().at_level(Level::INFO), ])) .exit(expect::span().at_level(Level::ERROR)) .exit(expect::span().at_level(Level::WARN)) .exit(expect::span().at_level(Level::INFO)) .only() .run_with_handle(); let (warn_layer, warn_handle) = layer::named("warn") .enter(expect::span().at_level(Level::WARN)) .enter(expect::span().at_level(Level::ERROR)) .event(event::msg("hello world").in_scope(vec![ expect::span().at_level(Level::ERROR), expect::span().at_level(Level::WARN), ])) .exit(expect::span().at_level(Level::ERROR)) .exit(expect::span().at_level(Level::WARN)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(debug_layer.with_filter(LevelFilter::DEBUG)) .with(info_layer.with_filter(LevelFilter::INFO)) .with(warn_layer.with_filter(LevelFilter::WARN)) .set_default(); { let _trace = tracing::trace_span!("my_span").entered(); let _debug = tracing::debug_span!("my_span").entered(); let _info = tracing::info_span!("my_span").entered(); let _warn = tracing::warn_span!("my_span").entered(); let _error = tracing::error_span!("my_span").entered(); tracing::error!("hello world"); } debug_handle.assert_finished(); info_handle.assert_finished(); warn_handle.assert_finished(); } #[test] fn filters_interleaved_span_scopes() { fn target_layer(target: &'static str) -> (MockLayer, subscriber::MockHandle) { layer::named(format!("target_{}", target)) .enter(expect::span().with_target(target)) .enter(expect::span().with_target(target)) .event(event::msg("hello world").in_scope(vec![ expect::span().with_target(target), expect::span().with_target(target), ])) .event( event::msg("hello to my target") .in_scope(vec![ expect::span().with_target(target), expect::span().with_target(target), ]) .with_target(target), ) .exit(expect::span().with_target(target)) .exit(expect::span().with_target(target)) .only() .run_with_handle() } let (a_layer, a_handle) = target_layer("a"); let (b_layer, b_handle) = target_layer("b"); let (all_layer, all_handle) = layer::named("all") .enter(expect::span().with_target("b")) .enter(expect::span().with_target("a")) .event(event::msg("hello world").in_scope(vec![ expect::span().with_target("a"), expect::span().with_target("b"), ])) .exit(expect::span().with_target("a")) .exit(expect::span().with_target("b")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(all_layer.with_filter(LevelFilter::INFO)) .with(a_layer.with_filter(filter::filter_fn(|meta| { let target = meta.target(); target == "a" || target == module_path!() }))) .with(b_layer.with_filter(filter::filter_fn(|meta| { let target = meta.target(); target == "b" || target == module_path!() }))) .set_default(); { let _a1 = tracing::trace_span!(target: "a", "a/trace").entered(); let _b1 = tracing::info_span!(target: "b", "b/info").entered(); let _a2 = tracing::info_span!(target: "a", "a/info").entered(); let _b2 = tracing::trace_span!(target: "b", "b/trace").entered(); tracing::info!("hello world"); tracing::debug!(target: "a", "hello to my target"); tracing::debug!(target: "b", "hello to my target"); } a_handle.assert_finished(); b_handle.assert_finished(); all_handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/main.rs000064400000000000000000000132131046102023000210050ustar 00000000000000#![cfg(feature = "registry")] mod boxed; mod downcast_raw; mod filter_scopes; mod option; mod per_event; mod targets; mod trees; mod vec; use tracing::{level_filters::LevelFilter, Level}; use tracing_mock::{event, expect, layer, subscriber}; use tracing_subscriber::{filter, prelude::*, Layer}; #[test] fn basic_layer_filters() { let (trace_layer, trace_handle) = layer::named("trace") .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let (debug_layer, debug_handle) = layer::named("debug") .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let (info_layer, info_handle) = layer::named("info") .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(trace_layer.with_filter(LevelFilter::TRACE)) .with(debug_layer.with_filter(LevelFilter::DEBUG)) .with(info_layer.with_filter(LevelFilter::INFO)) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); trace_handle.assert_finished(); debug_handle.assert_finished(); info_handle.assert_finished(); } #[test] fn basic_layer_filter_spans() { let (trace_layer, trace_handle) = layer::named("trace") .new_span(expect::span().at_level(Level::TRACE)) .new_span(expect::span().at_level(Level::DEBUG)) .new_span(expect::span().at_level(Level::INFO)) .only() .run_with_handle(); let (debug_layer, debug_handle) = layer::named("debug") .new_span(expect::span().at_level(Level::DEBUG)) .new_span(expect::span().at_level(Level::INFO)) .only() .run_with_handle(); let (info_layer, info_handle) = layer::named("info") .new_span(expect::span().at_level(Level::INFO)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(trace_layer.with_filter(LevelFilter::TRACE)) .with(debug_layer.with_filter(LevelFilter::DEBUG)) .with(info_layer.with_filter(LevelFilter::INFO)) .set_default(); tracing::trace_span!("hello trace"); tracing::debug_span!("hello debug"); tracing::info_span!("hello info"); trace_handle.assert_finished(); debug_handle.assert_finished(); info_handle.assert_finished(); } #[test] fn global_filters_subscribers_still_work() { let (expect, handle) = layer::mock() .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(expect) .with(LevelFilter::INFO) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); handle.assert_finished(); } #[test] fn global_filter_interests_are_cached() { let (expect, handle) = layer::mock() .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(expect.with_filter(filter::filter_fn(|meta| { assert!( meta.level() <= &Level::INFO, "enabled should not be called for callsites disabled by the global filter" ); meta.level() <= &Level::WARN }))) .with(LevelFilter::INFO) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); handle.assert_finished(); } #[test] fn global_filters_affect_subscriber_filters() { let (expect, handle) = layer::named("debug") .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(expect.with_filter(LevelFilter::DEBUG)) .with(LevelFilter::INFO) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); handle.assert_finished(); } #[test] fn filter_fn() { let (all, all_handle) = layer::named("all_targets") .event(event::msg("hello foo")) .event(event::msg("hello bar")) .only() .run_with_handle(); let (foo, foo_handle) = layer::named("foo_target") .event(event::msg("hello foo")) .only() .run_with_handle(); let (bar, bar_handle) = layer::named("bar_target") .event(event::msg("hello bar")) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(all) .with(foo.with_filter(filter::filter_fn(|meta| meta.target().starts_with("foo")))) .with(bar.with_filter(filter::filter_fn(|meta| meta.target().starts_with("bar")))) .set_default(); tracing::trace!(target: "foo", "hello foo"); tracing::trace!(target: "bar", "hello bar"); foo_handle.assert_finished(); bar_handle.assert_finished(); all_handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/option.rs000064400000000000000000000107411046102023000213740ustar 00000000000000use super::*; use tracing::Subscriber; use tracing_subscriber::{ filter::{self, LevelFilter}, prelude::*, Layer, }; 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 (layer, handle) = layer::mock().only().run_with_handle(); let layer = layer.with_filter(Some(filter_out_everything())); let _guard = tracing_subscriber::registry().with(layer).set_default(); for i in 0..2 { tracing::info!(i); } handle.assert_finished(); } #[test] fn option_none() { let (layer, handle) = layer::mock() .event(expect::event()) .event(expect::event()) .only() .run_with_handle(); let layer = layer.with_filter(None::>); let _guard = tracing_subscriber::registry().with(layer).set_default(); for i in 0..2 { tracing::info!(i); } handle.assert_finished(); } #[test] fn option_mixed() { let (layer, handle) = layer::mock() .event(expect::event()) .only() .run_with_handle(); let layer = layer .with_filter(filter::dynamic_filter_fn(|meta, _ctx| { meta.target() == "interesting" })) .with_filter(None::>); let _guard = tracing_subscriber::registry().with(layer).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/layer. #[test] fn none_max_level_hint() { let (layer_some, handle_none) = layer::mock() .event(expect::event()) .event(expect::event()) .only() .run_with_handle(); let subscribe_none = layer_some.with_filter(None::>); assert!(subscribe_none.max_level_hint().is_none()); let (layer_filter_fn, handle_filter_fn) = layer::mock() .event(expect::event()) .only() .run_with_handle(); let max_level = Level::INFO; let layer_filter_fn = layer_filter_fn.with_filter( filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &max_level) .with_max_level_hint(max_level), ); assert_eq!(layer_filter_fn.max_level_hint(), Some(LevelFilter::INFO)); let subscriber = tracing_subscriber::registry() .with(subscribe_none) .with(layer_filter_fn); // The absence of a hint from the `None` filter upgrades the `INFO` hint // from the filter fn layer. 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/layers. #[test] fn some_max_level_hint() { let (layer_some, handle_some) = layer::mock() .event(expect::event()) .event(expect::event()) .only() .run_with_handle(); let layer_some = layer_some.with_filter(Some( filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &Level::DEBUG) .with_max_level_hint(Level::DEBUG), )); assert_eq!(layer_some.max_level_hint(), Some(LevelFilter::DEBUG)); let (layer_filter_fn, handle_filter_fn) = layer::mock() .event(expect::event()) .only() .run_with_handle(); let layer_filter_fn = layer_filter_fn.with_filter( filter::dynamic_filter_fn(move |meta, _| return meta.level() <= &Level::INFO) .with_max_level_hint(Level::INFO), ); assert_eq!(layer_filter_fn.max_level_hint(), Some(LevelFilter::INFO)); let subscriber = tracing_subscriber::registry() .with(layer_some) .with(layer_filter_fn); // The `DEBUG` hint from the `Some` filter upgrades the `INFO` hint from the // filter fn layer. 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(); } tracing-subscriber-0.3.18/tests/layer_filters/per_event.rs000064400000000000000000000032421046102023000220510ustar 00000000000000use tracing::Level; use tracing_mock::{expect, layer}; use tracing_subscriber::{field::Visit, layer::Filter, prelude::*}; struct FilterEvent; impl Filter for FilterEvent { fn enabled( &self, _meta: &tracing::Metadata<'_>, _cx: &tracing_subscriber::layer::Context<'_, S>, ) -> bool { true } fn event_enabled( &self, event: &tracing::Event<'_>, _cx: &tracing_subscriber::layer::Context<'_, S>, ) -> bool { struct ShouldEnable(bool); impl Visit for ShouldEnable { fn record_bool(&mut self, field: &tracing_core::Field, value: bool) { if field.name() == "enable" { self.0 = value; } } fn record_debug( &mut self, _field: &tracing_core::Field, _value: &dyn core::fmt::Debug, ) { } } let mut should_enable = ShouldEnable(false); event.record(&mut should_enable); should_enable.0 } } #[test] fn per_layer_event_field_filtering() { let (expect, handle) = layer::mock() .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let _subscriber = tracing_subscriber::registry() .with(expect.with_filter(FilterEvent)) .set_default(); tracing::trace!(enable = true, "hello trace"); tracing::debug!("hello debug"); tracing::info!(enable = true, "hello info"); tracing::warn!(enable = false, "hello warn"); tracing::error!("hello error"); handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/targets.rs000064400000000000000000000033771046102023000215440ustar 00000000000000use super::*; use tracing_mock::event; use tracing_subscriber::{ filter::{filter_fn, Targets}, prelude::*, }; #[test] #[cfg_attr(not(feature = "tracing-log"), ignore)] fn log_events() { // Reproduces https://github.com/tokio-rs/tracing/issues/1563 mod inner { pub(super) const MODULE_PATH: &str = module_path!(); #[tracing::instrument] pub(super) fn logs() { log::debug!("inner"); } } let filter = Targets::new() .with_default(LevelFilter::DEBUG) .with_target(inner::MODULE_PATH, LevelFilter::WARN); let layer = tracing_subscriber::layer::Identity::new().with_filter(filter_fn(move |_meta| true)); let _guard = tracing_subscriber::registry() .with(filter) .with(layer) .set_default(); inner::logs(); } #[test] fn inner_layer_short_circuits() { // This test ensures that when a global filter short-circuits `Interest` // evaluation, we aren't left with a "dirty" per-layer filter state. let (layer, handle) = layer::mock() .event(event::msg("hello world")) .only() .run_with_handle(); let filter = Targets::new().with_target("magic_target", LevelFilter::DEBUG); let _guard = tracing_subscriber::registry() // Note: we don't just use a `LevelFilter` for the global filter here, // because it will just return a max level filter, and the chain of // `register_callsite` calls that would trigger the bug never happens... .with(filter::filter_fn(|meta| meta.level() <= &Level::INFO)) .with(layer.with_filter(filter)) .set_default(); tracing::debug!("skip me please!"); tracing::info!(target: "magic_target", "hello world"); handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/trees.rs000064400000000000000000000130201046102023000211770ustar 00000000000000use super::*; use tracing_mock::{event, expect, layer::MockLayer}; #[test] fn basic_trees() { let (with_target, with_target_handle) = layer::named("info_with_target") .event( expect::event() .at_level(Level::INFO) .with_target("my_target"), ) .only() .run_with_handle(); let (info, info_handle) = layer::named("info") .event( expect::event() .at_level(Level::INFO) .with_target(module_path!()), ) .event( expect::event() .at_level(Level::INFO) .with_target("my_target"), ) .only() .run_with_handle(); let (all, all_handle) = layer::named("all") .event( expect::event() .at_level(Level::INFO) .with_target(module_path!()), ) .event(expect::event().at_level(Level::TRACE)) .event( expect::event() .at_level(Level::INFO) .with_target("my_target"), ) .event( expect::event() .at_level(Level::TRACE) .with_target("my_target"), ) .only() .run_with_handle(); let info_tree = info .and_then( with_target.with_filter(filter::filter_fn(|meta| dbg!(meta.target()) == "my_target")), ) .with_filter(LevelFilter::INFO); let subscriber = tracing_subscriber::registry().with(info_tree).with(all); let _guard = dbg!(subscriber).set_default(); tracing::info!("hello world"); tracing::trace!("hello trace"); tracing::info!(target: "my_target", "hi to my target"); tracing::trace!(target: "my_target", "hi to my target at trace"); all_handle.assert_finished(); info_handle.assert_finished(); with_target_handle.assert_finished(); } #[test] fn filter_span_scopes() { fn target_layer(target: &'static str) -> (MockLayer, subscriber::MockHandle) { layer::named(format!("target_{}", target)) .enter(expect::span().with_target(target).at_level(Level::INFO)) .event(event::msg("hello world").in_scope(vec![ expect::span().with_target(target).at_level(Level::INFO), ])) .exit(expect::span().with_target(target).at_level(Level::INFO)) .only() .run_with_handle() } let (a_layer, a_handle) = target_layer("a"); let (b_layer, b_handle) = target_layer("b"); let (info_layer, info_handle) = layer::named("info") .enter(expect::span().with_target("b").at_level(Level::INFO)) .enter(expect::span().with_target("a").at_level(Level::INFO)) .event(event::msg("hello world").in_scope(vec![ expect::span().with_target("a").at_level(Level::INFO), expect::span().with_target("b").at_level(Level::INFO), ])) .exit(expect::span().with_target("a").at_level(Level::INFO)) .exit(expect::span().with_target("b").at_level(Level::INFO)) .only() .run_with_handle(); let full_scope = vec![ expect::span().with_target("b").at_level(Level::TRACE), expect::span().with_target("a").at_level(Level::INFO), expect::span().with_target("b").at_level(Level::INFO), expect::span().with_target("a").at_level(Level::TRACE), ]; let (all_layer, all_handle) = layer::named("all") .enter(expect::span().with_target("a").at_level(Level::TRACE)) .enter(expect::span().with_target("b").at_level(Level::INFO)) .enter(expect::span().with_target("a").at_level(Level::INFO)) .enter(expect::span().with_target("b").at_level(Level::TRACE)) .event(event::msg("hello world").in_scope(full_scope.clone())) .event( event::msg("hello to my target") .with_target("a") .in_scope(full_scope.clone()), ) .event( event::msg("hello to my target") .with_target("b") .in_scope(full_scope), ) .exit(expect::span().with_target("b").at_level(Level::TRACE)) .exit(expect::span().with_target("a").at_level(Level::INFO)) .exit(expect::span().with_target("b").at_level(Level::INFO)) .exit(expect::span().with_target("a").at_level(Level::TRACE)) .only() .run_with_handle(); let a_layer = a_layer.with_filter(filter::filter_fn(|meta| { let target = meta.target(); target == "a" || target == module_path!() })); let b_layer = b_layer.with_filter(filter::filter_fn(|meta| { let target = meta.target(); target == "b" || target == module_path!() })); let info_tree = info_layer .and_then(a_layer) .and_then(b_layer) .with_filter(LevelFilter::INFO); let subscriber = tracing_subscriber::registry() .with(info_tree) .with(all_layer); let _guard = dbg!(subscriber).set_default(); { let _a1 = tracing::trace_span!(target: "a", "a/trace").entered(); let _b1 = tracing::info_span!(target: "b", "b/info").entered(); let _a2 = tracing::info_span!(target: "a", "a/info").entered(); let _b2 = tracing::trace_span!(target: "b", "b/trace").entered(); tracing::info!("hello world"); tracing::debug!(target: "a", "hello to my target"); tracing::debug!(target: "b", "hello to my target"); } all_handle.assert_finished(); info_handle.assert_finished(); a_handle.assert_finished(); b_handle.assert_finished(); } tracing-subscriber-0.3.18/tests/layer_filters/vec.rs000064400000000000000000000074451046102023000206500ustar 00000000000000use super::*; use tracing::Subscriber; use tracing_mock::{expect, layer::MockLayer}; #[test] fn with_filters_unboxed() { let (trace_layer, trace_handle) = layer::named("trace") .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let trace_layer = trace_layer.with_filter(LevelFilter::TRACE); let (debug_layer, debug_handle) = layer::named("debug") .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let debug_layer = debug_layer.with_filter(LevelFilter::DEBUG); let (info_layer, info_handle) = layer::named("info") .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let info_layer = info_layer.with_filter(LevelFilter::INFO); let _subscriber = tracing_subscriber::registry() .with(vec![trace_layer, debug_layer, info_layer]) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); trace_handle.assert_finished(); debug_handle.assert_finished(); info_handle.assert_finished(); } #[test] fn with_filters_boxed() { let (unfiltered_layer, unfiltered_handle) = layer::named("unfiltered") .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let unfiltered_layer = unfiltered_layer.boxed(); let (debug_layer, debug_handle) = layer::named("debug") .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let debug_layer = debug_layer.with_filter(LevelFilter::DEBUG).boxed(); let (target_layer, target_handle) = layer::named("target") .event(expect::event().at_level(Level::INFO)) .only() .run_with_handle(); let target_layer = target_layer .with_filter(filter::filter_fn(|meta| meta.target() == "my_target")) .boxed(); let _subscriber = tracing_subscriber::registry() .with(vec![unfiltered_layer, debug_layer, target_layer]) .set_default(); tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!(target: "my_target", "hello my target"); unfiltered_handle.assert_finished(); debug_handle.assert_finished(); target_handle.assert_finished(); } #[test] fn mixed_max_level_hint() { let unfiltered = layer::named("unfiltered").run().boxed(); let info = layer::named("info") .run() .with_filter(LevelFilter::INFO) .boxed(); let debug = layer::named("debug") .run() .with_filter(LevelFilter::DEBUG) .boxed(); let subscriber = tracing_subscriber::registry().with(vec![unfiltered, info, debug]); assert_eq!(subscriber.max_level_hint(), None); } #[test] fn all_filtered_max_level_hint() { let warn = layer::named("warn") .run() .with_filter(LevelFilter::WARN) .boxed(); let info = layer::named("info") .run() .with_filter(LevelFilter::INFO) .boxed(); let debug = layer::named("debug") .run() .with_filter(LevelFilter::DEBUG) .boxed(); let subscriber = tracing_subscriber::registry().with(vec![warn, info, debug]); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG)); } #[test] fn empty_vec() { // Just a None means everything is off let subscriber = tracing_subscriber::registry().with(Vec::::new()); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); } tracing-subscriber-0.3.18/tests/multiple_layer_filter_interests_cached.rs000064400000000000000000000076601046102023000252110ustar 00000000000000#![cfg(feature = "registry")] use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; use tracing_mock::{expect, layer}; use tracing_subscriber::{filter, prelude::*}; #[test] fn multiple_layer_filter_interests_are_cached() { // This layer will return Interest::always for INFO and lower. let seen_info = Arc::new(Mutex::new(HashMap::new())); let seen_info2 = seen_info.clone(); let filter = filter::filter_fn(move |meta| { *seen_info .lock() .unwrap() .entry(*meta.level()) .or_insert(0usize) += 1; meta.level() <= &Level::INFO }); let seen_info = seen_info2; let (info_layer, info_handle) = layer::named("info") .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let info_layer = info_layer.with_filter(filter); // This layer will return Interest::always for WARN and lower. let seen_warn = Arc::new(Mutex::new(HashMap::new())); let seen_warn2 = seen_warn.clone(); let filter = filter::filter_fn(move |meta| { *seen_warn .lock() .unwrap() .entry(*meta.level()) .or_insert(0usize) += 1; meta.level() <= &Level::WARN }); let seen_warn = seen_warn2; let (warn_layer, warn_handle) = layer::named("warn") .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let warn_layer = warn_layer.with_filter(filter); let subscriber = tracing_subscriber::registry() .with(warn_layer) .with(info_layer); assert!(subscriber.max_level_hint().is_none()); let _subscriber = subscriber.set_default(); fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } events(); { let lock = seen_info.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the INFO layer (after first set of events)", level ); } let lock = seen_warn.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the WARN layer (after first set of events)", level ); } } events(); { let lock = seen_info.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the INFO layer (after second set of events)", level ); } let lock = seen_warn.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the WARN layer (after second set of events)", level ); } } info_handle.assert_finished(); warn_handle.assert_finished(); } tracing-subscriber-0.3.18/tests/option.rs000064400000000000000000000200271046102023000165260ustar 00000000000000#![cfg(feature = "registry")] use tracing_core::{subscriber::Interest, LevelFilter, Metadata, Subscriber}; use tracing_subscriber::{layer, prelude::*}; // A basic layer that returns its inner for `max_level_hint` #[derive(Debug)] struct BasicLayer(Option); impl tracing_subscriber::Layer for BasicLayer { fn register_callsite(&self, _m: &Metadata<'_>) -> Interest { Interest::sometimes() } fn enabled(&self, _m: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { true } fn max_level_hint(&self) -> Option { self.0 } } // This test is just used to compare to the tests below #[test] fn just_layer() { let subscriber = tracing_subscriber::registry().with(LevelFilter::INFO); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::INFO)); } #[test] fn subscriber_and_option_some_layer() { let subscriber = tracing_subscriber::registry() .with(LevelFilter::INFO) .with(Some(LevelFilter::DEBUG)); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG)); } #[test] fn subscriber_and_option_none_layer() { // None means the other layer takes control let subscriber = tracing_subscriber::registry() .with(LevelFilter::ERROR) .with(None::); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::ERROR)); } #[test] fn just_option_some_layer() { // Just a None means everything is off let subscriber = tracing_subscriber::registry().with(None::); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); } /// Tests that the logic tested in `doesnt_override_none` works through the reload subscriber #[test] fn just_option_none_layer() { let subscriber = tracing_subscriber::registry().with(Some(LevelFilter::ERROR)); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::ERROR)); } // Test that the `None` max level hint only applies if its the only layer #[test] fn none_outside_doesnt_override_max_level() { // None means the other layer takes control let subscriber = tracing_subscriber::registry() .with(BasicLayer(None)) .with(None::); assert_eq!( subscriber.max_level_hint(), None, "\n stack: {:#?}", subscriber ); // The `None`-returning layer still wins let subscriber = tracing_subscriber::registry() .with(BasicLayer(None)) .with(Some(LevelFilter::ERROR)); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::ERROR), "\n stack: {:#?}", subscriber ); // Check that we aren't doing anything truly wrong let subscriber = tracing_subscriber::registry() .with(BasicLayer(Some(LevelFilter::DEBUG))) .with(None::); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // Test that per-subscriber filters aren't affected // One layer is None so it "wins" let subscriber = tracing_subscriber::registry() .with(BasicLayer(None)) .with(None::.with_filter(LevelFilter::DEBUG)); assert_eq!( subscriber.max_level_hint(), None, "\n stack: {:#?}", subscriber ); // The max-levels wins let subscriber = tracing_subscriber::registry() .with(BasicLayer(Some(LevelFilter::INFO))) .with(None::.with_filter(LevelFilter::DEBUG)); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // Test filter on the other layer let subscriber = tracing_subscriber::registry() .with(BasicLayer(Some(LevelFilter::INFO)).with_filter(LevelFilter::DEBUG)) .with(None::); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); let subscriber = tracing_subscriber::registry() .with(BasicLayer(None).with_filter(LevelFilter::DEBUG)) .with(None::); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // The `OFF` from `None` over overridden. let subscriber = tracing_subscriber::registry() .with(BasicLayer(Some(LevelFilter::INFO))) .with(None::); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::INFO), "\n stack: {:#?}", subscriber ); } // Test that the `None` max level hint only applies if its the only layer #[test] fn none_inside_doesnt_override_max_level() { // None means the other layer takes control let subscriber = tracing_subscriber::registry() .with(None::) .with(BasicLayer(None)); assert_eq!( subscriber.max_level_hint(), None, "\n stack: {:#?}", subscriber ); // The `None`-returning layer still wins let subscriber = tracing_subscriber::registry() .with(Some(LevelFilter::ERROR)) .with(BasicLayer(None)); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::ERROR), "\n stack: {:#?}", subscriber ); // Check that we aren't doing anything truly wrong let subscriber = tracing_subscriber::registry() .with(None::) .with(BasicLayer(Some(LevelFilter::DEBUG))); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // Test that per-subscriber filters aren't affected // One layer is None so it "wins" let subscriber = tracing_subscriber::registry() .with(None::.with_filter(LevelFilter::DEBUG)) .with(BasicLayer(None)); assert_eq!( subscriber.max_level_hint(), None, "\n stack: {:#?}", subscriber ); // The max-levels wins let subscriber = tracing_subscriber::registry() .with(None::.with_filter(LevelFilter::DEBUG)) .with(BasicLayer(Some(LevelFilter::INFO))); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // Test filter on the other layer let subscriber = tracing_subscriber::registry() .with(None::) .with(BasicLayer(Some(LevelFilter::INFO)).with_filter(LevelFilter::DEBUG)); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); let subscriber = tracing_subscriber::registry() .with(None::) .with(BasicLayer(None).with_filter(LevelFilter::DEBUG)); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::DEBUG), "\n stack: {:#?}", subscriber ); // The `OFF` from `None` over overridden. let subscriber = tracing_subscriber::registry() .with(None::) .with(BasicLayer(Some(LevelFilter::INFO))); assert_eq!( subscriber.max_level_hint(), Some(LevelFilter::INFO), "\n stack: {:#?}", subscriber ); } /// Tests that the logic tested in `doesnt_override_none` works through the reload layer #[test] fn reload_works_with_none() { let (layer1, handle1) = tracing_subscriber::reload::Layer::new(None::); let (layer2, _handle2) = tracing_subscriber::reload::Layer::new(None::); let subscriber = tracing_subscriber::registry().with(layer1).with(layer2); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); // reloading one should pass through correctly. handle1.reload(Some(BasicLayer(None))).unwrap(); assert_eq!(subscriber.max_level_hint(), None); // Check pass-through of an actual level as well handle1 .reload(Some(BasicLayer(Some(LevelFilter::DEBUG)))) .unwrap(); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::DEBUG)); } tracing-subscriber-0.3.18/tests/option_filter_interest_caching.rs000064400000000000000000000035601046102023000234670ustar 00000000000000// 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, layer}; use tracing_subscriber::{filter, prelude::*, Layer}; /// 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 (layer_none, handle_none) = layer::mock() .event(expect::event()) .event(expect::event()) .only() .run_with_handle(); let layer_none = layer_none.with_filter(None::>); let times_filtered = Arc::new(AtomicUsize::new(0)); let (layer_filter_fn, handle_filter_fn) = layer::mock() .event(expect::event()) .event(expect::event()) .only() .run_with_handle(); let layer_filter_fn = layer_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(layer_none) .with(layer_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(); } tracing-subscriber-0.3.18/tests/registry_max_level_hint.rs000064400000000000000000000005401046102023000221420ustar 00000000000000#![cfg(all(feature = "registry", feature = "fmt"))] use tracing_subscriber::{filter::LevelFilter, prelude::*}; #[test] fn registry_sets_max_level_hint() { tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) .with(LevelFilter::DEBUG) .init(); assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); } tracing-subscriber-0.3.18/tests/registry_with_subscriber.rs000064400000000000000000000012551046102023000223460ustar 00000000000000#![cfg(feature = "registry")] use tracing_futures::{Instrument, WithSubscriber}; use tracing_subscriber::prelude::*; #[tokio::test] async fn future_with_subscriber() { tracing_subscriber::registry().init(); let span = tracing::info_span!("foo"); let _e = span.enter(); let span = tracing::info_span!("bar"); let _e = span.enter(); tokio::spawn( async { async { let span = tracing::Span::current(); println!("{:?}", span); } .instrument(tracing::info_span!("hi")) .await } .with_subscriber(tracing_subscriber::registry()), ) .await .unwrap(); } tracing-subscriber-0.3.18/tests/reload.rs000064400000000000000000000110751046102023000164670ustar 00000000000000#![cfg(feature = "registry")] use std::sync::atomic::{AtomicUsize, Ordering}; use tracing_core::{ span::{Attributes, Id, Record}, subscriber::Interest, Event, LevelFilter, Metadata, Subscriber, }; use tracing_subscriber::{layer, prelude::*, reload::*}; pub struct NopSubscriber; fn event() { tracing::info!("my event"); } impl Subscriber for NopSubscriber { fn register_callsite(&self, _: &'static Metadata<'static>) -> Interest { Interest::never() } fn enabled(&self, _: &Metadata<'_>) -> bool { false } fn new_span(&self, _: &Attributes<'_>) -> Id { Id::from_u64(1) } fn record(&self, _: &Id, _: &Record<'_>) {} fn record_follows_from(&self, _: &Id, _: &Id) {} fn event(&self, _: &Event<'_>) {} fn enter(&self, _: &Id) {} fn exit(&self, _: &Id) {} } #[test] fn reload_handle() { static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0); static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0); enum Filter { One, Two, } impl tracing_subscriber::Layer for Filter { fn register_callsite(&self, m: &Metadata<'_>) -> Interest { println!("REGISTER: {:?}", m); Interest::sometimes() } fn enabled(&self, m: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { println!("ENABLED: {:?}", m); match self { Filter::One => FILTER1_CALLS.fetch_add(1, Ordering::SeqCst), Filter::Two => FILTER2_CALLS.fetch_add(1, Ordering::SeqCst), }; true } fn max_level_hint(&self) -> Option { match self { Filter::One => Some(LevelFilter::INFO), Filter::Two => Some(LevelFilter::DEBUG), } } } let (layer, handle) = Layer::new(Filter::One); let subscriber = tracing_core::dispatcher::Dispatch::new(layer.with_subscriber(NopSubscriber)); tracing_core::dispatcher::with_default(&subscriber, || { assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 0); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); event(); assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); assert_eq!(LevelFilter::current(), LevelFilter::INFO); handle.reload(Filter::Two).expect("should reload"); assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); event(); assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1); }) } #[test] fn reload_filter() { struct NopLayer; impl tracing_subscriber::Layer for NopLayer { fn register_callsite(&self, _m: &Metadata<'_>) -> Interest { Interest::sometimes() } fn enabled(&self, _m: &Metadata<'_>, _: layer::Context<'_, S>) -> bool { true } } static FILTER1_CALLS: AtomicUsize = AtomicUsize::new(0); static FILTER2_CALLS: AtomicUsize = AtomicUsize::new(0); enum Filter { One, Two, } impl tracing_subscriber::layer::Filter for Filter { fn enabled(&self, m: &Metadata<'_>, _: &layer::Context<'_, S>) -> bool { println!("ENABLED: {:?}", m); match self { Filter::One => FILTER1_CALLS.fetch_add(1, Ordering::SeqCst), Filter::Two => FILTER2_CALLS.fetch_add(1, Ordering::SeqCst), }; true } fn max_level_hint(&self) -> Option { match self { Filter::One => Some(LevelFilter::INFO), Filter::Two => Some(LevelFilter::DEBUG), } } } let (filter, handle) = Layer::new(Filter::One); let dispatcher = tracing_core::Dispatch::new( tracing_subscriber::registry().with(NopLayer.with_filter(filter)), ); tracing_core::dispatcher::with_default(&dispatcher, || { assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 0); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); event(); assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 0); assert_eq!(LevelFilter::current(), LevelFilter::INFO); handle.reload(Filter::Two).expect("should reload"); assert_eq!(LevelFilter::current(), LevelFilter::DEBUG); event(); assert_eq!(FILTER1_CALLS.load(Ordering::SeqCst), 1); assert_eq!(FILTER2_CALLS.load(Ordering::SeqCst), 1); }) } tracing-subscriber-0.3.18/tests/same_len_filters.rs000064400000000000000000000045111046102023000205310ustar 00000000000000// These tests include field filters with no targets, so they have to go in a // separate file. #![cfg(feature = "env-filter")] use tracing::{self, subscriber::with_default, Level}; use tracing_mock::*; use tracing_subscriber::{filter::EnvFilter, prelude::*}; #[test] fn same_length_targets() { let filter: EnvFilter = "foo=trace,bar=trace".parse().expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::TRACE)) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!(target: "foo", "foo"); tracing::trace!(target: "bar", "bar"); }); finished.assert_finished(); } #[test] fn same_num_fields_event() { let filter: EnvFilter = "[{foo}]=trace,[{bar}]=trace" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .event( expect::event() .at_level(Level::TRACE) .with_fields(expect::field("foo")), ) .event( expect::event() .at_level(Level::TRACE) .with_fields(expect::field("bar")), ) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace!(foo = 1); tracing::trace!(bar = 3); }); finished.assert_finished(); } #[test] fn same_num_fields_and_name_len() { let filter: EnvFilter = "[foo{bar=1}]=trace,[baz{boz=1}]=trace" .parse() .expect("filter should parse"); let (subscriber, finished) = subscriber::mock() .new_span( expect::span() .named("foo") .at_level(Level::TRACE) .with_field(expect::field("bar")), ) .new_span( expect::span() .named("baz") .at_level(Level::TRACE) .with_field(expect::field("boz")), ) .only() .run_with_handle(); let subscriber = subscriber.with(filter); with_default(subscriber, || { tracing::trace_span!("foo", bar = 1); tracing::trace_span!("baz", boz = 1); }); finished.assert_finished(); } tracing-subscriber-0.3.18/tests/unhinted_layer_filters_dont_break_other_layers.rs000064400000000000000000000074231046102023000267350ustar 00000000000000#![cfg(feature = "registry")] use tracing::Level; use tracing_mock::{ expect, layer::{self, MockLayer}, subscriber, }; use tracing_subscriber::{filter::DynFilterFn, prelude::*}; #[test] fn layer_filters() { let (unfiltered, unfiltered_handle) = unfiltered("unfiltered"); let (filtered, filtered_handle) = filtered("filtered"); let _subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered.with_filter(filter())) .set_default(); events(); unfiltered_handle.assert_finished(); filtered_handle.assert_finished(); } #[test] fn layered_layer_filters() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let unfiltered = unfiltered1.and_then(unfiltered2); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let filtered = filtered1 .with_filter(filter()) .and_then(filtered2.with_filter(filter())); let _subscriber = tracing_subscriber::registry() .with(unfiltered) .with(filtered) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn out_of_order() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let _subscriber = tracing_subscriber::registry() .with(unfiltered1) .with(filtered1.with_filter(filter())) .with(unfiltered2) .with(filtered2.with_filter(filter())) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } #[test] fn mixed_layered() { let (unfiltered1, unfiltered1_handle) = unfiltered("unfiltered_1"); let (unfiltered2, unfiltered2_handle) = unfiltered("unfiltered_2"); let (filtered1, filtered1_handle) = filtered("filtered_1"); let (filtered2, filtered2_handle) = filtered("filtered_2"); let layered1 = filtered1.with_filter(filter()).and_then(unfiltered1); let layered2 = unfiltered2.and_then(filtered2.with_filter(filter())); let _subscriber = tracing_subscriber::registry() .with(layered1) .with(layered2) .set_default(); events(); unfiltered1_handle.assert_finished(); unfiltered2_handle.assert_finished(); filtered1_handle.assert_finished(); filtered2_handle.assert_finished(); } fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } fn filter() -> DynFilterFn { DynFilterFn::new(|metadata, _| metadata.level() <= &Level::INFO) } fn unfiltered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::TRACE)) .event(expect::event().at_level(Level::DEBUG)) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } fn filtered(name: &str) -> (MockLayer, subscriber::MockHandle) { layer::named(name) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle() } tracing-subscriber-0.3.18/tests/utils.rs000064400000000000000000000017051046102023000163600ustar 00000000000000#![cfg(feature = "std")] use tracing_mock::*; use tracing_subscriber::prelude::*; #[test] fn init_ext_works() { let (subscriber, finished) = subscriber::mock() .event( expect::event() .at_level(tracing::Level::INFO) .with_target("init_works"), ) .only() .run_with_handle(); let _guard = subscriber.set_default(); tracing::info!(target: "init_works", "it worked!"); finished.assert_finished(); } #[test] #[cfg(feature = "fmt")] fn builders_are_init_ext() { tracing_subscriber::fmt().set_default(); let _ = tracing_subscriber::fmt() .with_target(false) .compact() .try_init(); } #[test] #[cfg(all(feature = "fmt", feature = "env-filter"))] fn layered_is_init_ext() { tracing_subscriber::registry() .with(tracing_subscriber::fmt::layer()) .with(tracing_subscriber::EnvFilter::new("foo=info")) .set_default(); } tracing-subscriber-0.3.18/tests/vec.rs000064400000000000000000000011251046102023000157710ustar 00000000000000#![cfg(feature = "registry")] use tracing::level_filters::LevelFilter; use tracing::Subscriber; use tracing_subscriber::prelude::*; #[test] fn just_empty_vec() { // Just a None means everything is off let subscriber = tracing_subscriber::registry().with(Vec::::new()); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::OFF)); } #[test] fn layer_and_empty_vec() { let subscriber = tracing_subscriber::registry() .with(LevelFilter::INFO) .with(Vec::::new()); assert_eq!(subscriber.max_level_hint(), Some(LevelFilter::INFO)); } tracing-subscriber-0.3.18/tests/vec_subscriber_filter_interests_cached.rs000064400000000000000000000073021046102023000251530ustar 00000000000000#![cfg(feature = "registry")] use std::{ collections::HashMap, sync::{Arc, Mutex}, }; use tracing::{Level, Subscriber}; use tracing_mock::{layer::MockLayer, *}; use tracing_subscriber::{filter, prelude::*}; #[test] fn vec_layer_filter_interests_are_cached() { let mk_filtered = |level: Level, subscriber: MockLayer| { let seen = Arc::new(Mutex::new(HashMap::new())); let filter = filter::filter_fn({ let seen = seen.clone(); move |meta| { *seen.lock().unwrap().entry(*meta.level()).or_insert(0usize) += 1; meta.level() <= &level } }); (subscriber.with_filter(filter).boxed(), seen) }; // This layer will return Interest::always for INFO and lower. let (info_layer, info_handle) = layer::named("info") .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::INFO)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let (info_layer, seen_info) = mk_filtered(Level::INFO, info_layer); // This layer will return Interest::always for WARN and lower. let (warn_layer, warn_handle) = layer::named("warn") .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .event(expect::event().at_level(Level::WARN)) .event(expect::event().at_level(Level::ERROR)) .only() .run_with_handle(); let (warn_layer, seen_warn) = mk_filtered(Level::WARN, warn_layer); let subscriber = tracing_subscriber::registry().with(vec![warn_layer, info_layer]); assert!(subscriber.max_level_hint().is_none()); let _subscriber = subscriber.set_default(); fn events() { tracing::trace!("hello trace"); tracing::debug!("hello debug"); tracing::info!("hello info"); tracing::warn!("hello warn"); tracing::error!("hello error"); } events(); { let lock = seen_info.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the INFO subscriber (after first set of events)", level ); } let lock = seen_warn.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the WARN subscriber (after first set of events)", level ); } } events(); { let lock = seen_info.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the INFO subscriber (after second set of events)", level ); } let lock = seen_warn.lock().unwrap(); for (&level, &count) in lock.iter() { if level == Level::INFO { continue; } assert_eq!( count, 1, "level {:?} should have been seen 1 time by the WARN subscriber (after second set of events)", level ); } } info_handle.assert_finished(); warn_handle.assert_finished(); }