use std::sync::OnceLock;
use clap::ValueEnum;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{EnvFilter, Layer, prelude::*};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, ValueEnum, Default)]
#[clap(rename_all = "kebab-case")]
pub enum LogFormat {
#[default]
Auto,
Pretty,
Simplified,
Json,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, ValueEnum, Default)]
#[clap(rename_all = "kebab-case")]
pub enum Level {
Error,
Warn,
#[default]
Info,
Debug,
Trace,
Off,
}
impl Level {
pub const fn level_filter(self) -> LevelFilter {
match self {
Self::Error => LevelFilter::ERROR,
Self::Warn => LevelFilter::WARN,
Self::Info => LevelFilter::INFO,
Self::Debug => LevelFilter::DEBUG,
Self::Trace => LevelFilter::TRACE,
Self::Off => LevelFilter::OFF,
}
}
}
pub fn init(log_level: Level, log_format: LogFormat, log_filter: Option<&str>) {
static INIT: OnceLock<()> = OnceLock::new();
let _ = INIT.get_or_init(|| {
let subscriber = tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_target(true);
let format = match (log_format, console::user_attended()) {
(LogFormat::Auto, true) | (LogFormat::Pretty, _) => {
subscriber.compact().without_time().boxed()
}
(LogFormat::Auto, false) | (LogFormat::Simplified, _) => {
subscriber.with_ansi(false).boxed()
}
(LogFormat::Json, _) => subscriber
.json()
.flatten_event(true)
.with_current_span(true)
.with_span_list(true)
.with_file(true)
.with_line_number(true)
.boxed(),
};
let filter = match log_filter {
Some(directive) => EnvFilter::builder()
.with_default_directive(log_level.level_filter().into())
.parse_lossy(directive),
None => EnvFilter::builder()
.with_default_directive(log_level.level_filter().into())
.from_env_lossy(),
};
tracing_subscriber::registry()
.with(format.with_filter(filter))
.init();
});
}