1use is_terminal::IsTerminal;
4use tracing::level_filters::LevelFilter;
5use tracing_subscriber::{EnvFilter, fmt, layer::SubscriberExt, util::SubscriberInitExt};
6
7const WHITELISTED_LOG_TARGETS: &[&str] = &["wasmer", "wasmer_wasix", "virtual_fs"];
8
9#[derive(Debug, Default, Clone, PartialEq, clap::Parser)]
11pub struct Output {
12 #[clap(short, long, action = clap::ArgAction::Count, global = true, conflicts_with = "quiet")]
14 pub verbose: u8,
15 #[clap(short, long, global = true, conflicts_with = "verbose")]
17 pub quiet: bool,
18 #[clap(long, global = true, env, default_value = "text")]
20 pub log_format: LogFormat,
21 #[clap(long, global = true, env, default_value = "close")]
23 pub log_events: LogEvents,
24 #[clap(long, default_value_t = clap::ColorChoice::Auto, global = true)]
26 pub color: clap::ColorChoice,
27}
28
29impl Output {
30 pub fn is_verbose(&self) -> bool {
32 self.verbose > 0
33 }
34
35 pub fn initialize_logging(&self) {
38 let fmt_layer = fmt::layer()
39 .with_target(true)
40 .with_ansi(self.should_emit_colors())
41 .with_thread_ids(true)
42 .with_writer(std::io::stderr);
43
44 let fmt_layer = match self.log_events {
45 LogEvents::New => fmt_layer.with_span_events(fmt::format::FmtSpan::NEW),
46 LogEvents::Close => fmt_layer.with_span_events(fmt::format::FmtSpan::CLOSE),
47 LogEvents::All => {
48 fmt_layer.with_span_events(fmt::format::FmtSpan::NEW | fmt::format::FmtSpan::CLOSE)
49 }
50 };
51
52 let filter_layer = self.log_filter();
53
54 match self.log_format {
55 LogFormat::Text => tracing_subscriber::registry()
56 .with(filter_layer)
57 .with(fmt_layer.compact().with_target(true))
58 .init(),
59 LogFormat::Json => tracing_subscriber::registry()
60 .with(filter_layer)
61 .with(fmt_layer.json().with_target(true))
62 .init(),
63 }
64 }
65
66 fn log_filter(&self) -> EnvFilter {
67 let default_filters = [
68 LevelFilter::OFF,
69 LevelFilter::WARN,
70 LevelFilter::INFO,
71 LevelFilter::DEBUG,
72 ];
73
74 let default_level = default_filters
76 .get(self.verbose as usize)
77 .copied()
78 .unwrap_or(LevelFilter::TRACE);
79 let mut filter = EnvFilter::builder()
80 .with_default_directive(default_level.into())
81 .from_env_lossy();
82
83 let specific_filters = [LevelFilter::WARN, LevelFilter::INFO, LevelFilter::DEBUG];
87 if self.verbose > 0 {
88 let level = specific_filters
89 .get(self.verbose as usize)
90 .copied()
91 .unwrap_or(LevelFilter::TRACE);
92
93 for target in WHITELISTED_LOG_TARGETS {
94 let directive = format!("{target}={level}").parse().unwrap();
95 filter = filter.add_directive(directive);
96 }
97 }
98
99 filter
100 }
101
102 fn should_emit_colors(&self) -> bool {
109 match self.color {
110 clap::ColorChoice::Auto => std::io::stderr().is_terminal(),
111 clap::ColorChoice::Always => true,
112 clap::ColorChoice::Never => false,
113 }
114 }
115
116 pub fn draw_target(&self) -> indicatif::ProgressDrawTarget {
121 if self.quiet {
122 return indicatif::ProgressDrawTarget::hidden();
123 }
124
125 indicatif::ProgressDrawTarget::stderr()
126 }
127}
128
129#[derive(Debug, Default, Copy, Clone, PartialEq, clap::ValueEnum)]
131pub enum LogFormat {
132 #[default]
134 Text,
135 Json,
137}
138
139#[derive(Debug, Default, Copy, Clone, PartialEq, clap::ValueEnum)]
141pub enum LogEvents {
142 New,
144 #[default]
146 Close,
147 All,
149}