use std::fs::File;
use std::path::PathBuf;
use clap::ValueEnum;
use miette::Result;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::EnvFilter;
use tracing_subscriber::fmt::{self};
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
#[derive(Clone, Copy, Debug, Default, ValueEnum)]
pub enum LogLevel {
#[default]
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
impl LogLevel {
#[must_use]
pub const fn to_tracing_level(self) -> LevelFilter {
match self {
Self::Off => LevelFilter::OFF,
Self::Error => LevelFilter::ERROR,
Self::Warn => LevelFilter::WARN,
Self::Info => LevelFilter::INFO,
Self::Debug => LevelFilter::DEBUG,
Self::Trace => LevelFilter::TRACE,
}
}
}
pub struct Logger;
impl Logger {
pub fn init_logging(log_level: LogLevel, log_file: Option<&PathBuf>) -> Result<()> {
let level: LevelFilter = log_level.to_tracing_level();
if level == LevelFilter::OFF {
return Ok(());
}
let subscriber = tracing_subscriber::registry().with(
EnvFilter::builder()
.with_default_directive(level.into())
.from_env_lossy(),
);
if let Some(log_path) = log_file {
let file: File = File::create(log_path)
.map_err(|e| miette::miette!("Failed to create file: {e:?}"))?;
let layer = fmt::layer()
.with_writer(file)
.with_ansi(false)
.with_target(true)
.with_thread_ids(true);
subscriber.with(layer).init();
} else {
let layer = fmt::layer()
.with_writer(std::io::stderr)
.with_ansi(true)
.with_target(true)
.compact();
subscriber.with(layer).init();
}
tracing::info!("Logging initialized at level: {log_level:?}");
Ok(())
}
}