use std::sync::atomic::{AtomicU8, Ordering};
use num_enum::{FromPrimitive, IntoPrimitive};
use serde::{Deserialize, Serialize};
use strum_macros::{Display, EnumString};
use tokio::sync::OnceCell;
use tracing_subscriber::{reload::Handle, EnvFilter};
#[allow(dead_code)]
static RIGH_LOG_RELOAD_HANDLE: OnceCell<Handle<EnvFilter, tracing_subscriber::Registry>> =
OnceCell::const_new();
#[allow(dead_code)]
static RIGH_CURRENT_LOG_LEVEL: AtomicU8 = AtomicU8::new(2);
#[derive(
Debug,
Clone,
Default,
Copy,
Serialize,
Deserialize,
Display,
EnumString,
FromPrimitive,
IntoPrimitive,
PartialEq,
)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
#[repr(u8)]
pub enum RighLogLevel {
Error = 0,
Warn = 1,
#[default]
Info = 2,
Debug = 3,
Trace = 4,
Unknown = 255,
}
impl RighLogLevel {
pub fn to_tracing_level(self) -> tracing::Level {
match self {
RighLogLevel::Error => tracing::Level::ERROR,
RighLogLevel::Warn => tracing::Level::WARN,
RighLogLevel::Info => tracing::Level::INFO,
RighLogLevel::Debug => tracing::Level::DEBUG,
RighLogLevel::Trace => tracing::Level::TRACE,
RighLogLevel::Unknown => tracing::Level::INFO, }
}
}
#[allow(dead_code)]
pub async fn set_reload_handle(handle: Handle<EnvFilter, tracing_subscriber::Registry>) {
RIGH_LOG_RELOAD_HANDLE
.set(handle)
.expect("Reload handle already set");
}
#[allow(dead_code)]
pub fn get_current_log_level() -> RighLogLevel {
let level_u8 = RIGH_CURRENT_LOG_LEVEL.load(Ordering::Relaxed);
RighLogLevel::from(level_u8)
}
#[allow(dead_code)]
pub fn set_log_level(level: RighLogLevel) -> Result<(), String> {
if level == RighLogLevel::Unknown {
return Err("Cannot set log level to Unknown".to_string());
}
let level_u8: u8 = level.into();
RIGH_CURRENT_LOG_LEVEL.store(level_u8, Ordering::Relaxed);
if let Some(handle) = RIGH_LOG_RELOAD_HANDLE.get() {
let new_filter = EnvFilter::new(level.to_string().to_uppercase());
if let Err(e) = handle.reload(new_filter) {
return Err(format!("Failed to reload tracing filter: {e}"));
}
tracing::info!(
"Log level dynamically changed to: {}",
level.to_string().to_uppercase()
);
Ok(())
} else {
tracing::warn!("Log reload handle not initialized, level stored but not applied");
Ok(())
}
}
pub fn init_logging_with_reload() -> Handle<EnvFilter, tracing_subscriber::Registry> {
use std::io::{stderr, IsTerminal};
use tracing::level_filters::LevelFilter;
use tracing_appender::{non_blocking, rolling::daily};
use tracing_subscriber::{filter::EnvFilter, layer::SubscriberExt, Registry};
let log_level = std::env::var("LOG")
.ok()
.and_then(|level| match level.to_uppercase().as_str() {
"TRACE" => Some(tracing::Level::TRACE),
"DEBUG" => Some(tracing::Level::DEBUG),
"INFO" => Some(tracing::Level::INFO),
"WARN" => Some(tracing::Level::WARN),
"ERROR" => Some(tracing::Level::ERROR),
_ => None,
})
.unwrap_or(tracing::Level::INFO);
let dir = tracing_subscriber::filter::Directive::from(LevelFilter::from(log_level));
let filter = vec![dir]
.into_iter()
.fold(EnvFilter::from_default_env(), |filter, directive| {
filter.add_directive(directive)
});
let (filter, reload_handle) = tracing_subscriber::reload::Layer::new(filter);
let identity = c"RighValor";
let (options, facility) = Default::default();
let syslog_option = syslog_tracing::Syslog::new(identity, options, facility);
if let Ok(log_file) = std::env::var("RG_LOGDIR") {
let file_appender = daily(log_file, "righvalor.log");
let (file_writer, guard) = non_blocking(file_appender);
Box::leak(Box::new(guard)); let fmt = tracing_subscriber::fmt::Layer::default()
.with_ansi(false)
.with_writer(file_writer)
.with_target(false);
let subscriber = Registry::default().with(filter).with(fmt);
tracing::subscriber::set_global_default(subscriber)
.expect("to set global subscriber to file");
} else if stderr().is_terminal() || syslog_option.is_none() {
match std::env::var("GLOG").ok() {
Some(_) => {
use tracing_glog::{Glog, GlogFields};
let fmt = tracing_subscriber::fmt::Layer::default()
.with_ansi(true)
.with_writer(std::io::stderr)
.event_format(Glog::default().with_timer(tracing_glog::LocalTime::default()))
.fmt_fields(GlogFields::default().compact());
let subscriber = Registry::default().with(filter).with(fmt);
tracing::subscriber::set_global_default(subscriber)
.expect("to set GLOG global subscriber");
}
None => {
let fmt = tracing_subscriber::fmt::Layer::default()
.with_ansi(true)
.with_writer(std::io::stderr)
.with_target(false);
let subscriber = Registry::default().with(filter).with(fmt);
tracing::subscriber::set_global_default(subscriber)
.expect("to set global subscriber");
}
}
} else if let Some(syslog_writer) = syslog_option {
let syslog_layer = tracing_subscriber::fmt::Layer::default()
.with_writer(syslog_writer)
.with_ansi(false) .with_target(false)
.without_time();
let subscriber = Registry::default().with(filter).with(syslog_layer);
tracing::subscriber::set_global_default(subscriber)
.expect("to set syslog global subscriber");
} else {
println!("init_logging failed!")
}
reload_handle
}