use std::io;
use std::sync::LazyLock;
use std::sync::Mutex;
use tracing::debug;
use tracing_subscriber::fmt;
use tracing_subscriber::layer::{Layered, SubscriberExt};
use tracing_subscriber::reload::{self, Handle};
use tracing_subscriber::{EnvFilter, Layer, Registry};
use crate::UtilesError;
use crate::errors::UtilesResult;
use crate::lager::{LagerConfig, LagerFormat, LagerLevel};
type LagerLayer = Handle<Box<dyn Layer<Registry> + Send + Sync>, Registry>;
type LagerFormatLayer = Handle<
Box<
dyn Layer<Layered<Box<dyn Layer<Registry> + Send + Sync>, Registry, Registry>>
+ Send
+ Sync,
>,
Layered<Box<dyn Layer<Registry> + Send + Sync>, Registry, Registry>,
>;
static GLOBAL_FILTER_RELOAD_HANDLE: LazyLock<Mutex<Option<LagerLayer>>> =
LazyLock::new(|| Mutex::new(None));
static GLOBAL_FORMAT_RELOAD_HANDLE: LazyLock<Mutex<Option<LagerFormatLayer>>> =
LazyLock::new(|| Mutex::new(None));
static GLOBAL_LAGER_CONFIG: LazyLock<Mutex<LagerConfig>> =
LazyLock::new(|| Mutex::new(LagerConfig::default()));
pub fn init_tracing(log_config: LagerConfig) -> UtilesResult<()> {
let filter = log_config.env_filter();
let (filter_layer, filter_reload_handle) = reload::Layer::new(filter.boxed());
let format_layer_raw = if log_config.json {
fmt::Layer::new().json().with_writer(io::stderr).boxed()
} else {
fmt::Layer::new().with_writer(io::stderr).boxed()
};
let (format_layer, format_reload_handle) = reload::Layer::new(format_layer_raw);
let subscriber = Registry::default()
.with(filter_layer.boxed())
.with(format_layer);
if let Err(e) = tracing::subscriber::set_global_default(subscriber) {
debug!("tracing::subscriber::set_global_default failed: {}", e);
} else {
{
let mut handle = GLOBAL_FILTER_RELOAD_HANDLE.lock().map_err(|e| {
UtilesError::AdHoc(format!("Failed to lock filter reload handle: {e}"))
})?;
*handle = Some(filter_reload_handle);
}
{
let mut handle = GLOBAL_FORMAT_RELOAD_HANDLE.lock().map_err(|e| {
UtilesError::AdHoc(format!("Failed to lock format reload handle: {e}"))
})?;
*handle = Some(format_reload_handle);
}
{
let mut config = GLOBAL_LAGER_CONFIG.lock().map_err(|e| {
UtilesError::AdHoc(format!("Failed to lock logging configuration: {e}"))
})?;
*config = log_config;
}
debug!("Logging configuration initialized: {:?}", log_config);
}
Ok(())
}
pub fn set_log_level(level: &str) -> UtilesResult<()> {
let filter = EnvFilter::try_new(level).map_err(|e| {
println!("failed to set log level: {e}");
UtilesError::AdHoc(format!("failed to set log level: {e}"))
})?;
let global_handle = GLOBAL_FILTER_RELOAD_HANDLE.lock().map_err(|e| {
UtilesError::AdHoc(format!("failed to lock global handle: {e}"))
})?;
if let Some(handle) = global_handle.as_ref() {
handle.reload(filter.boxed()).map_err(|e| {
UtilesError::AdHoc(format!("failed to reload filter layer: {e}"))
})?;
Ok(())
} else {
Err(UtilesError::AdHoc(
"global reload handle not set".to_string(),
))
}
}
pub fn get_lager_level() -> UtilesResult<LagerLevel> {
let lager_config = GLOBAL_LAGER_CONFIG
.lock()
.map_err(|_| UtilesError::LockError("lager-config-lock".to_string()))?;
Ok(lager_config.level)
}
pub fn get_lager_format() -> UtilesResult<LagerFormat> {
let lager_config = GLOBAL_LAGER_CONFIG
.lock()
.map_err(|_| UtilesError::LockError("lager-config-lock".to_string()))?;
let format = if lager_config.json {
LagerFormat::Json
} else {
LagerFormat::Full
};
Ok(format)
}
pub fn set_log_format(json: bool) -> UtilesResult<()> {
let format_layer_raw = if json {
fmt::Layer::new().json().with_writer(io::stderr).boxed()
} else {
fmt::Layer::new().with_writer(io::stderr).boxed()
};
let global_handle = GLOBAL_FORMAT_RELOAD_HANDLE.lock().map_err(|e| {
UtilesError::AdHoc(format!("failed to lock global handle: {e}"))
})?;
if let Some(handle) = global_handle.as_ref() {
handle.reload(format_layer_raw).map_err(|e| {
UtilesError::AdHoc(format!("failed to reload format layer: {e}"))
})?;
Ok(())
} else {
Err(UtilesError::AdHoc(
"global reload handle not set".to_string(),
))
}
}