use tracing_subscriber::EnvFilter;
pub fn cli_env_filter() -> EnvFilter {
use tracing_subscriber::filter::LevelFilter;
EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy()
}
#[cfg(test)]
mod tests {
use super::cli_env_filter;
use std::io;
use std::sync::{Arc, Mutex};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::format::FmtSpan;
use tracing_subscriber::prelude::*;
#[derive(Clone, Default)]
struct Buffer(Arc<Mutex<Vec<u8>>>);
impl io::Write for Buffer {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.0.lock().unwrap().extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> io::Result<()> {
Ok(())
}
}
impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for Buffer {
type Writer = Self;
fn make_writer(&'a self) -> Self::Writer {
self.clone()
}
}
fn rendered(filter: EnvFilter) -> String {
let out = Buffer::default();
let subscriber = tracing_subscriber::registry().with(filter).with(
tracing_subscriber::fmt::layer()
.with_target(false)
.with_span_events(FmtSpan::CLOSE)
.pretty()
.with_writer(out.clone()),
);
tracing::subscriber::with_default(subscriber, || {
tracing::error!("MARKER_ERROR");
tracing::warn!("MARKER_WARN");
tracing::info!("MARKER_INFO");
tracing::debug!("MARKER_DEBUG");
tracing::trace!("MARKER_TRACE");
});
String::from_utf8(out.0.lock().unwrap().clone()).unwrap()
}
#[test]
fn default_filter_surfaces_warn_and_info() {
with_env("RUST_LOG", None, || {
let text = rendered(cli_env_filter());
assert!(
text.contains("MARKER_WARN"),
"WARN must reach the fmt layer with RUST_LOG unset, otherwise the \
CLI silently drops warnings that explain a degraded result"
);
assert!(
text.contains("MARKER_INFO"),
"INFO must reach the fmt layer with RUST_LOG unset"
);
});
}
#[test]
fn default_filter_still_surfaces_error() {
with_env("RUST_LOG", None, || {
assert!(rendered(cli_env_filter()).contains("MARKER_ERROR"));
});
}
#[test]
fn default_filter_excludes_debug_and_trace() {
with_env("RUST_LOG", None, || {
let text = rendered(cli_env_filter());
assert!(
!text.contains("MARKER_DEBUG"),
"DEBUG must stay off by default; RUST_LOG is the opt-in"
);
assert!(!text.contains("MARKER_TRACE"));
});
}
#[test]
fn rust_log_overrides_the_default_directive() {
with_env("RUST_LOG", Some("cargo_tangle=trace"), || {
assert!(rendered(cli_env_filter()).contains("MARKER_TRACE"));
});
with_env("RUST_LOG", Some("error"), || {
let text = rendered(cli_env_filter());
assert!(text.contains("MARKER_ERROR"));
assert!(
!text.contains("MARKER_WARN"),
"RUST_LOG=error must still narrow the filter"
);
});
}
#[test]
fn malformed_rust_log_falls_back_to_default() {
with_env("RUST_LOG", Some("not a valid directive ==="), || {
let text = rendered(cli_env_filter());
assert!(text.contains("MARKER_INFO"));
assert!(!text.contains("MARKER_TRACE"));
});
}
fn with_env(key: &str, value: Option<&str>, f: impl FnOnce()) {
let prior = std::env::var_os(key);
unsafe {
match value {
Some(v) => std::env::set_var(key, v),
None => std::env::remove_var(key),
}
}
f();
unsafe {
match prior {
Some(v) => std::env::set_var(key, v),
None => std::env::remove_var(key),
}
}
}
}