You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Linux taladardesktop 6.13.4 #216 SMP PREEMPT_DYNAMIC Fri Feb 21 15:56:13 CET 2025 x86_64 AMD Ryzen 9 3900X 12-Core Processor AuthenticAMD GNU/Linux
Crates
I assume the problem is in tracing-subscriber
Description
I have an integration test binary in my project (among other tests).
This integration test uses a Mutex to serialize all tests since setting test threads can not easily be done per test binary and it uses some resources that do not allow concurrent access.
I want the test to log a high level overview (info level and above for my crate and some specific dependencies) to the test output and a more detailed log (trace level for my crate and other settings for other dependencies) into one log file per test.
For this I am using a reload handle to change the writer in the file layer at the start of each test.
This works fine as far as the file output is concerned but with the default writer the console output is printed even if a test does not fail.
So I wanted to replace the default writer with the TestWriter but the code does not compile with a complex trait bound error that I can't quite figure out but it suggests to me that some implementation for some trait is likely missing for TestWriter that exists for the default writer.
I made a minimal example crate to reproduce the issue with the problematic change to test writer commented out. Sadly, the minimal code is still quite long thanks to the huge reload handle type.
Pay particular attention to the first error in the output where the compiler notes that an implementation for fn() -> std::io::Stdout exists but not for TestWriter in the same position.
Minimal repro
Cargo.toml
[package]
name = "minimize_tracing_reload_problem"
version = "0.1.0"
edition = "2024"
[dependencies]
function_name = "0.3.0"
tokio = { version = "1.43.0", features = ["full"] }
tracing = "0.1.41"
tracing-appender = "0.2.3"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
build.rs (necessary or OUT_DIR is not set at compile time)
fn main() { }
src/main.rs (default cargo new --bin version)
fn main() {
println!("Hello, world!");
}
tests/test.rs
use tracing_subscriber::Layer as _;
use tracing_subscriber::layer::SubscriberExt as _;
use tracing_subscriber::util::SubscriberInitExt as _;
/// ensures tracing is only initialized once on the first test who gets the lock
pub static TRACING_INITIALIZED: std::sync::Once = std::sync::Once::new();
/// stores the reload handle to change the file layer
#[allow(clippy::type_complexity)]
pub static TRACING_RELOAD_HANDLE: std::sync::Mutex<
Option<
tracing_subscriber::reload::Handle<
tracing_subscriber::fmt::Layer<
tracing_subscriber::layer::Layered<
tracing_subscriber::filter::Filtered<
tracing_subscriber::fmt::Layer<tracing_subscriber::Registry>,
tracing_subscriber::EnvFilter,
tracing_subscriber::Registry,
>,
tracing_subscriber::Registry,
>,
tracing_subscriber::fmt::format::DefaultFields,
tracing_subscriber::fmt::format::Format,
tracing_appender::rolling::RollingFileAppender,
>,
tracing_subscriber::layer::Layered<
tracing_subscriber::filter::Filtered<
tracing_subscriber::fmt::Layer<tracing_subscriber::Registry>,
tracing_subscriber::EnvFilter,
tracing_subscriber::Registry,
>,
tracing_subscriber::Registry,
>,
>,
>,
> = std::sync::Mutex::new(None);
fn setup_tracing(test_name: &str) -> Result<(), Box<dyn std::error::Error>> {
let log_dir = std::path::Path::new(env!("OUT_DIR")).join("test_logs");
let log_file = format!("{}.log", test_name);
TRACING_INITIALIZED.call_once(|| {
#[allow(clippy::expect_used)]
let minimal_env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::INFO.into())
.parse(std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string())).expect("level filter from RUST_LOG parse");
#[allow(clippy::expect_used)]
let env_filter = tracing_subscriber::EnvFilter::builder()
.with_default_directive(tracing::level_filters::LevelFilter::TRACE.into())
.parse(std::env::var("RUST_FILE_LOG").unwrap_or_else(|_| "trace".to_string())).expect("level filter from RUST_FILE_LOG parse");
let registry = tracing_subscriber::Registry::default();
// works
let registry =
registry.with(tracing_subscriber::fmt::Layer::default()
.with_filter(minimal_env_filter));
// but this does not
//let registry =
// registry.with(tracing_subscriber::fmt::Layer::default()
// .with_test_writer()
// .with_filter(minimal_env_filter));
let file_appender = tracing_appender::rolling::never(&log_dir, &log_file);
let file_layer =
tracing_subscriber::fmt::Layer::default()
.with_writer(file_appender);
let (reloadable_file_layer, reload_handle) = tracing_subscriber::reload::Layer::new(file_layer);
let reloadable_filtered_file_layer = reloadable_file_layer.with_filter(env_filter);
{
#[allow(clippy::expect_used)]
let mut l = TRACING_RELOAD_HANDLE.lock().expect("reload handler mutex");
*l = Some(reload_handle);
}
let registry =
registry.with(reloadable_filtered_file_layer);
registry.init();
});
let file_appender = tracing_appender::rolling::never(&log_dir, &log_file);
let file_layer = tracing_subscriber::fmt::Layer::default().with_writer(file_appender);
{
#[allow(clippy::expect_used)]
let l = TRACING_RELOAD_HANDLE.lock().expect("reload handler mutex");
if let Some(ref reload_handle) = *l {
reload_handle.reload(file_layer)?;
tracing::info!(
"Now logging details to\n{}/{}",
log_dir.display(),
log_file
);
}
}
Ok(())
}
static TEST_LOCK: std::sync::LazyLock<tokio::sync::Mutex<()>> =
std::sync::LazyLock::new(|| tokio::sync::Mutex::new(()));
#[tokio::test]
#[function_name::named]
async fn test1() -> Result<(), Box<dyn std::error::Error>> {
let _guard = TEST_LOCK.lock().await;
setup_tracing(function_name!())?;
tracing::info!("Logging in test {}", function_name!());
Ok(())
}
#[tokio::test]
#[function_name::named]
async fn test2() -> Result<(), Box<dyn std::error::Error>> {
let _guard = TEST_LOCK.lock().await;
setup_tracing(function_name!())?;
tracing::info!("Logging in test {}", function_name!());
Ok(())
}
Compiler error when with_test_writer() version is commented in
Compiling minimize_tracing_reload_problem v0.1.0 (/home/taladar/temp/20250228/minimize_tracing_reload_problem)
error[E0277]: the trait bound `Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>: __tracing_subscriber_Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>` is not satisfied
--> tests/test.rs:72:31
|
72 | registry.with(reloadable_filtered_file_layer);
| ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unsatisfied trait bound
| |
| required by a bound introduced by this call
|
= help: the trait `Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<_, _, _, TestWriter>, _, _>, _>>` is not implemented for `Filtered<Layer<Layer<..., ..., ..., ...>, ...>, ..., ...>`
but trait `Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<_, _, _, fn() -> std::io::Stdout>, _, _>, _>>` is implemented for it
= help: for that trait implementation, expected `fn() -> std::io::Stdout`, found `TestWriter`
note: required by a bound in `with`
--> /home/taladar/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-subscriber-0.3.19/src/layer/mod.rs:1504:12
|
1502 | fn with<L>(self, layer: L) -> Layered<L, Self>
| ---- required by a bound in this associated function
1503 | where
1504 | L: Layer<Self>,
| ^^^^^^^^^^^ required by this bound in `__tracing_subscriber_SubscriberExt::with`
error[E0599]: the method `init` exists for struct `Layered<Filtered<Layer<..., ...>, ..., ...>, ...>`, but its trait bounds were not satisfied
--> tests/test.rs:73:22
|
73 | registry.init();
| ^^^^ method cannot be called on `Layered<Filtered<Layer<..., ...>, ..., ...>, ...>` due to unsatisfied trait bounds
|
::: /home/taladar/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tracing-subscriber-0.3.19/src/layer/layered.rs:22:1
|
22 | pub struct Layered<L, I, S = I> {
| ------------------------------- doesn't satisfy `_: Into<Dispatch>` or `_: SubscriberInitExt`
|
note: there's an earlier shadowed binding `registry` of type `Registry` that has method `init` available
--> tests/test.rs:50:17
|
50 | let registry = tracing_subscriber::Registry::default();
| ^^^^^^^^ `registry` of type `Registry` that has method `init` defined earlier here
...
71 | let registry =
| -------- earlier `registry` shadowed here with type `Layered<Filtered<Layer<..., ...>, ..., ...>, ...>`
= note: the full type name has been written to '/home/taladar/temp/20250228/minimize_tracing_reload_problem/target/debug/deps/test-581b513331dfbb49.long-type-9381801419203904221.txt'
= note: consider using `--verbose` to print the full type name to the console
= note: the following trait bounds were not satisfied:
`Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: Into<Dispatch>`
which is required by `Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: SubscriberInitExt`
`&Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: Into<Dispatch>`
which is required by `&Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: SubscriberInitExt`
`&mut Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: Into<Dispatch>`
which is required by `&mut Layered<Filtered<tracing_subscriber::reload::Layer<tracing_subscriber::fmt::Layer<Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>, DefaultFields, Format, RollingFileAppender>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, EnvFilter, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry>, EnvFilter, Registry>, Registry>>, Layered<Filtered<tracing_subscriber::fmt::Layer<Registry, DefaultFields, Format, TestWriter>, EnvFilter, Registry>, Registry>>: SubscriberInitExt`
Some errors have detailed explanations: E0277, E0599.
For more information about an error, try `rustc --explain E0277`.
error: could not compile `minimize_tracing_reload_problem` (test "test") due to 2 previous errors
The text was updated successfully, but these errors were encountered:
Bug Report
Working code breaks with a complex unsatisfied trait bound by adding a call to .with_test_writer()
Version
Platform
Crates
I assume the problem is in tracing-subscriber
Description
I have an integration test binary in my project (among other tests).
This integration test uses a Mutex to serialize all tests since setting test threads can not easily be done per test binary and it uses some resources that do not allow concurrent access.
I want the test to log a high level overview (info level and above for my crate and some specific dependencies) to the test output and a more detailed log (trace level for my crate and other settings for other dependencies) into one log file per test.
For this I am using a reload handle to change the writer in the file layer at the start of each test.
This works fine as far as the file output is concerned but with the default writer the console output is printed even if a test does not fail.
So I wanted to replace the default writer with the TestWriter but the code does not compile with a complex trait bound error that I can't quite figure out but it suggests to me that some implementation for some trait is likely missing for TestWriter that exists for the default writer.
I made a minimal example crate to reproduce the issue with the problematic change to test writer commented out. Sadly, the minimal code is still quite long thanks to the huge reload handle type.
Pay particular attention to the first error in the output where the compiler notes that an implementation for fn() -> std::io::Stdout exists but not for TestWriter in the same position.
Minimal repro
Cargo.toml
build.rs (necessary or OUT_DIR is not set at compile time)
src/main.rs (default cargo new --bin version)
tests/test.rs
Compiler error when with_test_writer() version is commented in
The text was updated successfully, but these errors were encountered: