use std::sync::OnceLock;
use tracing_subscriber::{
Registry, filter::EnvFilter, fmt, layer::SubscriberExt, reload, util::SubscriberInitExt,
};
pub type LogFilterHandle = reload::Handle<EnvFilter, Registry>;
static LOG_FILTER_HANDLE: OnceLock<LogFilterHandle> = OnceLock::new();
pub fn init_logging(default_level: &str) -> anyhow::Result<()> {
let filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default_level));
let (filter_layer, reload_handle) = reload::Layer::new(filter);
if LOG_FILTER_HANDLE.set(reload_handle).is_err() {
return Err(anyhow::anyhow!("Logging already initialized"));
}
let fmt_layer = fmt::Layer::default()
.with_writer(std::io::stderr)
.with_target(true)
.with_thread_ids(true)
.with_file(false)
.with_line_number(false);
tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer)
.try_init()?;
Ok(())
}
pub fn set_global_log_level(level: &str) -> anyhow::Result<()> {
if let Some(handle) = LOG_FILTER_HANDLE.get() {
let new_filter = EnvFilter::new(level);
handle.reload(new_filter)?;
Ok(())
} else {
Err(anyhow::anyhow!("Logging not initialized"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_logging_init_and_level_change() {
let init_result = init_logging("info");
if let Err(e) = &init_result {
assert_eq!(e.to_string(), "Logging already initialized");
}
let change_result = set_global_log_level("debug");
assert!(change_result.is_ok(), "Should be able to change log level");
let change_result_2 = set_global_log_level("warn");
assert!(
change_result_2.is_ok(),
"Should be able to change log level again"
);
}
}