refactor(verbose): replace the global flag and veprintln! with log

Diagnostics now go through the log facade: the library no longer
decides where output goes, and consumers plug in their own sink (the
CLI installs a stderr logger printing warnings always and ecr debug
diagnostics under -v; dependency records stay filtered out).  The
kernel 'quiet' flag moves from the global verbosity flag to an
explicit QemuConfig field.
This commit is contained in:
2026-10-03 00:13:34 +02:00
parent 031c6ba113
commit fe0b99ee6b
16 changed files with 129 additions and 106 deletions
+47
View File
@@ -0,0 +1,47 @@
//! stderr logging sink: `Warn`/`Error` records always, `Debug`
//! diagnostics only with -v.
use log::{Level, LevelFilter, Log, Metadata, Record};
struct CliLogger {
max: LevelFilter,
}
impl Log for CliLogger {
fn enabled(&self, metadata: &Metadata) -> bool {
if metadata.level() > self.max {
return false;
}
match metadata.level() {
Level::Warn | Level::Error => true,
// Dependencies (hyper, rustls, …) share the log facade and are
// far noisier than we are; -v follows only our own diagnostics.
_ => metadata.target().starts_with("ecr"),
}
}
fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) {
return;
}
match record.level() {
Level::Error => eprintln!("Error: {}", record.args()),
Level::Warn => eprintln!("Warning: {}", record.args()),
_ => eprintln!("{}", record.args()),
}
}
fn flush(&self) {}
}
/// Install the CLI logger: warnings and errors always, `ecr` debug
/// diagnostics only when `verbose` (-v).
pub fn init(verbose: bool) {
let max = if verbose {
LevelFilter::Debug
} else {
LevelFilter::Warn
};
log::set_boxed_logger(Box::new(CliLogger { max })).ok();
log::set_max_level(max);
}
+9 -7
View File
@@ -1,4 +1,5 @@
mod cli;
mod logger;
mod progress;
use std::path::{Path, PathBuf};
@@ -11,14 +12,14 @@ use ecr::chroot;
use ecr::config::Config;
use ecr::exec::ExecOptions;
use ecr::mount::BindTarget;
use ecr::{kernel, qemu, qemu_vm, utils, veprintln, verbose, PrepareRequest, RootfsCache};
use ecr::{kernel, qemu, qemu_vm, utils, PrepareRequest, RootfsCache};
use progress::TermProgress;
fn main() -> Result<()> {
let args = Args::parse();
// Initialise verbosity before anything else so all downstream code can use veprintln!.
verbose::set(args.verbose);
// Install the logger before anything else so all downstream code can log.
logger::init(args.verbose);
// Render library progress (bars/spinners) on the terminal
let progress = TermProgress::new();
@@ -63,11 +64,11 @@ fn main() -> Result<()> {
// QEMU system mode
let kernel_path = match kernel_opt {
Some(path) => {
veprintln!("QEMU mode: using provided kernel {}", path.display());
log::debug!("QEMU mode: using provided kernel {}", path.display());
path.clone()
}
None => {
veprintln!("QEMU mode: downloading default kernel...");
log::debug!("QEMU mode: downloading default kernel...");
kernel::get_default_kernel(cache.dir(), &arch, &progress)?
}
};
@@ -83,6 +84,7 @@ fn main() -> Result<()> {
kernel_path,
rootfs_path: rootfs.dir().to_path_buf(),
memory: args.memory.clone(),
quiet: !args.verbose,
arch: arch.clone(),
command,
},
@@ -91,7 +93,7 @@ fn main() -> Result<()> {
// Cleanup happens automatically via tempfile
if result.is_ok() {
veprintln!("Cleanup complete.");
log::debug!("Cleanup complete.");
}
result
@@ -123,7 +125,7 @@ fn namespace_mode(args: &Args, rootfs: &Path, config: &Config, arch: &str) -> Re
// Cleanup happens automatically via tempfile
if exit_code.is_ok() {
veprintln!("Cleanup complete.");
log::debug!("Cleanup complete.");
}
exit_code