use is_terminal::IsTerminal;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
const WHITELISTED_LOG_TARGETS: &[&str] = &["wasmer", "wasmer_wasix", "virtual_fs"];
#[derive(Debug, Default, Clone, PartialEq, clap::Parser)]
pub struct Output {
#[clap(short, long, action = clap::ArgAction::Count, global = true, conflicts_with = "quiet")]
pub verbose: u8,
#[clap(short, long, global = true, conflicts_with = "verbose")]
pub quiet: bool,
#[clap(long, global = true, env, default_value = "text")]
pub log_format: LogFormat,
#[clap(long, default_value_t = clap::ColorChoice::Auto, global = true)]
pub color: clap::ColorChoice,
}
impl Output {
pub fn is_verbose(&self) -> bool {
self.verbose > 0
}
pub fn initialize_logging(&self) {
let fmt_layer = fmt::layer()
.with_target(true)
.with_span_events(fmt::format::FmtSpan::CLOSE)
.with_ansi(self.should_emit_colors())
.with_thread_ids(true)
.with_writer(std::io::stderr);
let filter_layer = self.log_filter();
match self.log_format {
LogFormat::Text => tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer.compact().with_target(true))
.init(),
LogFormat::Json => tracing_subscriber::registry()
.with(filter_layer)
.with(fmt_layer.json().with_target(true))
.init(),
}
}
fn log_filter(&self) -> EnvFilter {
let default_filters = [
LevelFilter::OFF,
LevelFilter::WARN,
LevelFilter::INFO,
LevelFilter::DEBUG,
];
let default_level = default_filters
.get(self.verbose as usize)
.copied()
.unwrap_or(LevelFilter::TRACE);
let mut filter = EnvFilter::builder()
.with_default_directive(default_level.into())
.from_env_lossy();
let specific_filters = [LevelFilter::WARN, LevelFilter::INFO, LevelFilter::DEBUG];
if self.verbose > 0 {
let level = specific_filters
.get(self.verbose as usize)
.copied()
.unwrap_or(LevelFilter::TRACE);
for target in WHITELISTED_LOG_TARGETS {
let directive = format!("{target}={level}").parse().unwrap();
filter = filter.add_directive(directive);
}
}
filter
}
fn should_emit_colors(&self) -> bool {
match self.color {
clap::ColorChoice::Auto => std::io::stderr().is_terminal(),
clap::ColorChoice::Always => true,
clap::ColorChoice::Never => false,
}
}
pub fn draw_target(&self) -> indicatif::ProgressDrawTarget {
if self.quiet {
return indicatif::ProgressDrawTarget::hidden();
}
indicatif::ProgressDrawTarget::stderr()
}
}
#[derive(Debug, Default, Copy, Clone, PartialEq, clap::ValueEnum)]
pub enum LogFormat {
#[default]
Text,
Json,
}