From d9b8f28f21f5d5c39d2f25d9d638677476cd8018 Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Fri, 8 May 2026 14:31:27 +0100 Subject: [PATCH] main: Add --log-format Currently only the existing format options are supported and the default format string is unchanged. This allows more fine grained logging control. Assisted-by: Claude:Opus-4.7 Signed-off-by: Rob Bradford --- cloud-hypervisor/src/logger.rs | 16 ++++++++-------- cloud-hypervisor/src/main.rs | 9 ++++++++- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cloud-hypervisor/src/logger.rs b/cloud-hypervisor/src/logger.rs index 594a07423..80ca8c35d 100644 --- a/cloud-hypervisor/src/logger.rs +++ b/cloud-hypervisor/src/logger.rs @@ -90,7 +90,7 @@ fn parse_format(fmt: &str) -> Result, Error> { Ok(tokens) } -const DEFAULT_FORMAT: &str = +pub const DEFAULT_FORMAT: &str = "cloud-hypervisor: {boottime}s: <{thread}> {level}:{location} -- {msg}"; pub struct Logger { @@ -100,11 +100,11 @@ pub struct Logger { } impl Logger { - pub fn new(output: Box) -> Result { + pub fn new(output: Box, format: &str) -> Result { Ok(Self { output: Mutex::new(output), start: Instant::now(), - tokens: parse_format(DEFAULT_FORMAT)?, + tokens: parse_format(format)?, }) } } @@ -262,7 +262,7 @@ mod tests { #[test] fn logger_new_uses_default_format() { let buf = SharedBuffer::default(); - let logger = Logger::new(Box::new(buf.clone())).unwrap(); + let logger = Logger::new(Box::new(buf.clone()), DEFAULT_FORMAT).unwrap(); // The default format has all 5 dynamic tokens. assert_eq!( logger @@ -277,7 +277,7 @@ mod tests { #[test] fn logger_enabled_always_true() { let buf = SharedBuffer::default(); - let logger = Logger::new(Box::new(buf)).unwrap(); + let logger = Logger::new(Box::new(buf), DEFAULT_FORMAT).unwrap(); let metadata = log::Metadata::builder() .level(log::Level::Trace) .target("anything") @@ -288,7 +288,7 @@ mod tests { #[test] fn logger_writes_expected_fields() { let buf = SharedBuffer::default(); - let logger = Logger::new(Box::new(buf.clone())).unwrap(); + let logger = Logger::new(Box::new(buf.clone()), DEFAULT_FORMAT).unwrap(); logger.log( &log::Record::builder() @@ -311,7 +311,7 @@ mod tests { #[test] fn logger_uses_target_when_no_file() { let buf = SharedBuffer::default(); - let logger = Logger::new(Box::new(buf.clone())).unwrap(); + let logger = Logger::new(Box::new(buf.clone()), DEFAULT_FORMAT).unwrap(); logger.log( &log::Record::builder() @@ -331,7 +331,7 @@ mod tests { #[test] fn logger_appends_each_record() { let buf = SharedBuffer::default(); - let logger = Logger::new(Box::new(buf.clone())).unwrap(); + let logger = Logger::new(Box::new(buf.clone()), DEFAULT_FORMAT).unwrap(); for i in 0..3 { logger.log( diff --git a/cloud-hypervisor/src/main.rs b/cloud-hypervisor/src/main.rs index 5085f5683..0790ff91c 100644 --- a/cloud-hypervisor/src/main.rs +++ b/cloud-hypervisor/src/main.rs @@ -300,6 +300,12 @@ fn get_cli_options_sorted( .help("Log file. Standard error is used if not specified") .num_args(1) .group("logging"), + Arg::new("log-format") + .long("log-format") + .help("Log format. Available tokens: {boottime}, {thread}, {level}, {location}, {msg}") + .num_args(1) + .default_value(logger::DEFAULT_FORMAT) + .group("logging"), Arg::new("memory") .long("memory") .help( @@ -515,7 +521,8 @@ fn start_vmm( Box::new(std::io::stderr()) }; - let logger = Logger::new(log_file).map_err(Error::LoggerFormat)?; + let format = cmd_arguments.get_one::("log-format").unwrap(); + let logger = Logger::new(log_file, format).map_err(Error::LoggerFormat)?; log::set_boxed_logger(Box::new(logger)) .map(|()| log::set_max_level(log_level)) .map_err(Error::LoggerSetup)?;