use std::sync::OnceLock;
use clap::ValueEnum;
use tracing::{level_filters::LevelFilter, log::error};
use tracing_subscriber::{
fmt::{self, format::FmtSpan, time},
prelude::*,
reload::{self, Handle},
Registry,
};
use crate::settings::Settings;
pub(crate) const LOG_LEVEL_DEFAULT: &str = "info";
#[derive(ValueEnum, Clone, Copy, Debug)]
pub(crate) enum LogLevel {
Debug,
Info,
Warn,
Error,
}
impl From<LogLevel> for tracing::Level {
fn from(val: LogLevel) -> Self {
match val {
LogLevel::Debug => Self::DEBUG,
LogLevel::Info => Self::INFO,
LogLevel::Warn => Self::WARN,
LogLevel::Error => Self::ERROR,
}
}
}
impl From<LogLevel> for tracing::log::LevelFilter {
fn from(val: LogLevel) -> Self {
match val {
LogLevel::Debug => Self::Debug,
LogLevel::Info => Self::Info,
LogLevel::Warn => Self::Warn,
LogLevel::Error => Self::Error,
}
}
}
static LOGGER_HANDLE: OnceLock<LoggerHandle> = OnceLock::new();
static GLOBAL_SPAN: OnceLock<tracing::span::Span> = OnceLock::new();
static SPAN_GUARD: OnceLock<tracing::span::Entered> = OnceLock::new();
pub(crate) type LoggerHandle = Handle<LevelFilter, Registry>;
fn set_default_span() {
let server_id = Settings::service_id();
let global_span = tracing::info_span!("Global", ServerID = server_id);
if GLOBAL_SPAN.set(global_span).is_err() {
error!("Failed to set default span. Is it already set?");
}
if let Some(global_span) = GLOBAL_SPAN.get() {
let span_guard = global_span.enter();
if SPAN_GUARD.set(span_guard).is_err() {
error!("Failed to set default span. Is it already set?");
}
}
}
pub(crate) fn init(log_level: LogLevel) {
let layer = fmt::layer()
.json()
.with_timer(time::UtcTime::rfc_3339())
.with_span_events(FmtSpan::CLOSE)
.with_current_span(true)
.with_span_list(true)
.with_target(true)
.with_file(true)
.with_line_number(true)
.with_level(true)
.with_thread_names(true)
.with_thread_ids(true)
.flatten_event(true);
let filter = LevelFilter::from_level(log_level.into());
let (filter, logger_handle) = reload::Layer::new(filter);
tracing_subscriber::registry()
.with(filter)
.with(layer)
.with(
tracing_subscriber::EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy(),
)
.init();
tracing::log::set_max_level(log_level.into());
if LOGGER_HANDLE.set(logger_handle).is_err() {
error!("Failed to initialize logger handle. Called multiple times?");
}
set_default_span();
}
pub(crate) fn modify_logger_level(level: LogLevel) {
if let Some(logger_handle) = LOGGER_HANDLE.get() {
if let Err(error) = logger_handle.modify(|f| *f = LevelFilter::from_level(level.into())) {
error!("Failed to modify log level to {:?} : {}", level, error);
}
} else {
error!(
"Failed to modify log level to {:?} : Logger handle not available.",
level
);
}
}