use std::io;
use std::sync::OnceLock;
use super::{paths::CachePaths, Result};
type ReloadFn = Box<dyn Fn(&str) -> std::result::Result<(), String> + Send + Sync + 'static>;
static FILTER_RELOAD: OnceLock<ReloadFn> = OnceLock::new();
pub fn init(paths: &CachePaths) -> Result<tracing_appender::non_blocking::WorkerGuard> {
let appender = tracing_appender::rolling::never(&paths.log_dir, &paths.log_file_name);
let _ = super::paths::ensure_file_600(&paths.log);
let (non_blocking, guard) = tracing_appender::non_blocking(appender);
let env_filter = tracing_subscriber::EnvFilter::try_from_env("ZSHRS_LOG")
.or_else(|_| {
let directive = super::paths::load_log_directive(paths);
tracing_subscriber::EnvFilter::try_new(&directive)
})
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info"));
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::reload;
use tracing_subscriber::util::SubscriberInitExt;
let (filter_layer, filter_handle) = reload::Layer::new(env_filter);
let file_layer = tracing_subscriber::fmt::layer()
.with_writer(non_blocking)
.with_ansi(false)
.with_target(true)
.with_level(true);
let stderr_enabled = matches!(
std::env::var("ZSHRS_LOG_STDERR").as_deref(),
Ok("1") | Ok("true") | Ok("yes")
);
let init_result = if stderr_enabled {
let stderr_layer = tracing_subscriber::fmt::layer()
.with_writer(io::stderr)
.with_ansi(true)
.with_target(true)
.with_level(true);
tracing_subscriber::registry()
.with(filter_layer)
.with(file_layer)
.with(stderr_layer)
.try_init()
} else {
tracing_subscriber::registry()
.with(filter_layer)
.with(file_layer)
.try_init()
};
if init_result.is_ok() {
let reload_fn: ReloadFn = Box::new(move |directive: &str| {
let new_filter = tracing_subscriber::EnvFilter::try_new(directive)
.map_err(|e| format!("bad directive `{}`: {}", directive, e))?;
filter_handle
.reload(new_filter)
.map_err(|e| format!("reload failed: {}", e))
});
let _ = FILTER_RELOAD.set(reload_fn);
}
Ok(guard)
}
pub fn set_runtime_level(directive: &str) -> std::result::Result<(), String> {
let f = FILTER_RELOAD
.get()
.ok_or_else(|| "log subsystem not reload-capable (init returned Err — likely a global subscriber was already set)".to_string())?;
f(directive)
}