use std::str::FromStr as _;
use tracing_subscriber::filter::Targets;
use tracing_subscriber::layer::SubscriberExt as _;
use tracing_subscriber::util::SubscriberInitExt as _;
use super::json_log::{JsonLog, StderrSink};
pub const FILTER_ENV: &str = "RUST_LOG";
pub fn install_logging(default_filter: &str) -> Result<(), super::ObserveError> {
let filter = filter(default_filter);
let installed = if cfg!(debug_assertions) {
tracing_subscriber::registry()
.with(filter)
.with(
tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_ansi(false)
.with_target(true),
)
.try_init()
} else {
tracing_subscriber::registry()
.with(filter)
.with(JsonLog::new(StderrSink))
.try_init()
};
installed.map_err(|_| super::ObserveError::Logging {
reason: "a global tracing subscriber is already installed",
})
}
fn filter(default_filter: &str) -> Targets {
let from_env = std::env::var(FILTER_ENV).ok();
resolve(from_env.as_deref(), default_filter)
}
fn resolve(from_env: Option<&str>, default_filter: &str) -> Targets {
let fallback = || {
Targets::from_str(default_filter).unwrap_or_else(|_| {
eprintln!(
"warning: default log filter `{default_filter}` is not valid; logging at `info`"
);
Targets::new().with_default(tracing::Level::INFO)
})
};
match from_env {
Some(value) if !value.trim().is_empty() => Targets::from_str(value).unwrap_or_else(|_| {
eprintln!(
"warning: {FILTER_ENV} is not a valid log filter; using the application default"
);
fallback()
}),
_ => fallback(),
}
}
#[cfg(test)]
mod tests {
use tracing::Level;
use super::{Targets, resolve};
#[test]
fn an_absent_variable_uses_the_default() {
let targets = resolve(None, "warn,arcature=debug");
assert!(targets.would_enable("arcature", &Level::DEBUG));
assert!(!targets.would_enable("other", &Level::INFO));
}
#[test]
fn an_empty_variable_counts_as_absent() {
let targets = resolve(Some(" "), "arcature=debug");
assert!(targets.would_enable("arcature", &Level::DEBUG));
}
#[test]
fn the_variable_wins_when_it_parses() {
let targets = resolve(Some("error"), "debug");
assert!(!targets.would_enable("arcature", &Level::INFO));
assert!(targets.would_enable("arcature", &Level::ERROR));
}
#[test]
fn an_unparseable_variable_falls_back_rather_than_failing() {
let targets = resolve(Some("=="), "arcature=debug");
assert!(targets.would_enable("arcature", &Level::DEBUG));
}
#[test]
fn an_unparseable_default_lands_on_info_rather_than_panicking() {
let targets: Targets = resolve(None, "== not a filter ==");
assert!(targets.would_enable("anything", &Level::INFO));
assert!(!targets.would_enable("anything", &Level::DEBUG));
}
}