#![forbid(unsafe_code)]
use std::sync::OnceLock;
use tracing_error::ErrorLayer;
use tracing_subscriber::reload;
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
static LOG_FILTER_RELOAD: OnceLock<reload::Handle<EnvFilter, Registry>> = OnceLock::new();
#[must_use]
pub fn build_env_filter(verbose: bool) -> EnvFilter {
if verbose {
return EnvFilter::new("debug");
}
EnvFilter::new("error")
}
pub fn bootstrap_logs() {
if LOG_FILTER_RELOAD.get().is_some() {
return;
}
let (filter_layer, handle) = reload::Layer::new(EnvFilter::new("error"));
let subscriber = Registry::default()
.with(filter_layer)
.with(ErrorLayer::default())
.with(
fmt::layer()
.with_writer(std::io::stderr)
.with_target(true)
.with_thread_names(true)
.with_ansi(false),
);
if tracing::subscriber::set_global_default(subscriber).is_ok() {
let _ = LOG_FILTER_RELOAD.set(handle);
let _ = tracing_log::LogTracer::builder()
.with_max_level(log::LevelFilter::Trace)
.init();
tracing::debug!("tracing subscriber installed (stderr, filter=error)");
}
}
pub fn initialize_logs(verbose: bool) {
let filter = build_env_filter(verbose);
if let Some(handle) = LOG_FILTER_RELOAD.get() {
match handle.reload(filter) {
Ok(()) => {
tracing::debug!(
verbose,
rust_log_set = std::env::var_os("RUST_LOG").is_some(),
"tracing filter reloaded"
);
}
Err(e) => {
tracing::warn!(err = %e, "failed to reload tracing filter");
}
}
return;
}
let _ = fmt()
.with_env_filter(filter)
.with_writer(std::io::stderr)
.with_target(true)
.with_thread_names(true)
.with_ansi(false)
.try_init();
let _ = tracing_log::LogTracer::builder()
.with_max_level(log::LevelFilter::Trace)
.init();
}
#[must_use]
pub fn has_reload_handle() -> bool {
LOG_FILTER_RELOAD.get().is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_env_filter_default_is_error() {
let prev = std::env::var_os("RUST_LOG");
crate::test_util::env::remove_var("RUST_LOG");
let f = build_env_filter(false);
assert_eq!(f.to_string(), "error");
match prev {
Some(v) => crate::test_util::env::set_var("RUST_LOG", v),
None => crate::test_util::env::remove_var("RUST_LOG"),
}
}
#[test]
fn build_env_filter_verbose_is_debug() {
let prev = std::env::var_os("RUST_LOG");
crate::test_util::env::remove_var("RUST_LOG");
let f = build_env_filter(true);
assert_eq!(f.to_string(), "debug");
match prev {
Some(v) => crate::test_util::env::set_var("RUST_LOG", v),
None => crate::test_util::env::remove_var("RUST_LOG"),
}
}
#[test]
fn bootstrap_logs_is_idempotent() {
bootstrap_logs();
bootstrap_logs();
let _ = has_reload_handle();
}
}