use std::sync::{Once, OnceLock};
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt;
use tracing_subscriber::prelude::*;
use tracing_subscriber::reload;
type SetLogFn = Box<dyn Fn(&str) -> Result<(), String> + Send + Sync>;
static SET_LOG: OnceLock<SetLogFn> = OnceLock::new();
static TRACING_INIT: Once = Once::new();
pub fn init_tracing() {
TRACING_INIT.call_once(|| {
let filter =
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info,nautalid=debug"));
let (filter, handle) = reload::Layer::new(filter);
let _ = SET_LOG.set(Box::new(move |directive: &str| {
let next = EnvFilter::try_new(directive).map_err(|e| e.to_string())?;
handle
.modify(|current| *current = next)
.map_err(|e| e.to_string())
}));
tracing_subscriber::registry()
.with(filter)
.with(fmt::layer().with_target(true))
.init();
});
}
#[must_use]
pub fn current_filter_directive() -> String {
std::env::var("RUST_LOG").unwrap_or_else(|_| "info,nautalid=debug".into())
}
const MAX_LOG_DIRECTIVE_LEN: usize = 256;
pub fn set_filter_directive(directive: &str) -> Result<(), String> {
if directive.len() > MAX_LOG_DIRECTIVE_LEN {
return Err(format!(
"directive exceeds {MAX_LOG_DIRECTIVE_LEN} characters"
));
}
validate_log_directive(directive)?;
SET_LOG
.get()
.ok_or_else(|| String::from("tracing reload handle not installed"))?(directive)
}
fn validate_log_directive(directive: &str) -> Result<(), String> {
const LEVELS: &[&str] = &["trace", "debug", "info", "warn", "error", "off"];
if LEVELS.contains(&directive) {
return Ok(());
}
if let Some(level) = directive.strip_prefix("nautalid=") {
if LEVELS.contains(&level) {
return Ok(());
}
}
Err("directive must be trace|debug|info|warn|error|off or nautalid=<level>".into())
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Once;
static INIT: Once = Once::new();
#[test]
fn rejects_broad_crate_directive() {
assert!(validate_log_directive("trace,hickory=debug").is_err());
}
#[test]
fn accepts_nautalid_level() {
assert!(validate_log_directive("nautalid=debug").is_ok());
}
#[test]
fn set_filter_directive_runtime() {
INIT.call_once(init_tracing);
set_filter_directive("warn").expect("reload");
}
}
#[cfg(feature = "filter-control")]
pub mod control;