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 <rbradford@meta.com>
This commit is contained in:
Rob Bradford
2026-05-08 14:31:27 +01:00
parent 3bbf97c5e5
commit d9b8f28f21
2 changed files with 16 additions and 9 deletions
+8 -8
View File
@@ -90,7 +90,7 @@ fn parse_format(fmt: &str) -> Result<Vec<Token>, Error> {
Ok(tokens) Ok(tokens)
} }
const DEFAULT_FORMAT: &str = pub const DEFAULT_FORMAT: &str =
"cloud-hypervisor: {boottime}s: <{thread}> {level}:{location} -- {msg}"; "cloud-hypervisor: {boottime}s: <{thread}> {level}:{location} -- {msg}";
pub struct Logger { pub struct Logger {
@@ -100,11 +100,11 @@ pub struct Logger {
} }
impl Logger { impl Logger {
pub fn new(output: Box<dyn Write + Send>) -> Result<Self, Error> { pub fn new(output: Box<dyn Write + Send>, format: &str) -> Result<Self, Error> {
Ok(Self { Ok(Self {
output: Mutex::new(output), output: Mutex::new(output),
start: Instant::now(), start: Instant::now(),
tokens: parse_format(DEFAULT_FORMAT)?, tokens: parse_format(format)?,
}) })
} }
} }
@@ -262,7 +262,7 @@ mod tests {
#[test] #[test]
fn logger_new_uses_default_format() { fn logger_new_uses_default_format() {
let buf = SharedBuffer::default(); 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. // The default format has all 5 dynamic tokens.
assert_eq!( assert_eq!(
logger logger
@@ -277,7 +277,7 @@ mod tests {
#[test] #[test]
fn logger_enabled_always_true() { fn logger_enabled_always_true() {
let buf = SharedBuffer::default(); 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() let metadata = log::Metadata::builder()
.level(log::Level::Trace) .level(log::Level::Trace)
.target("anything") .target("anything")
@@ -288,7 +288,7 @@ mod tests {
#[test] #[test]
fn logger_writes_expected_fields() { fn logger_writes_expected_fields() {
let buf = SharedBuffer::default(); 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( logger.log(
&log::Record::builder() &log::Record::builder()
@@ -311,7 +311,7 @@ mod tests {
#[test] #[test]
fn logger_uses_target_when_no_file() { fn logger_uses_target_when_no_file() {
let buf = SharedBuffer::default(); 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( logger.log(
&log::Record::builder() &log::Record::builder()
@@ -331,7 +331,7 @@ mod tests {
#[test] #[test]
fn logger_appends_each_record() { fn logger_appends_each_record() {
let buf = SharedBuffer::default(); 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 { for i in 0..3 {
logger.log( logger.log(
+8 -1
View File
@@ -300,6 +300,12 @@ fn get_cli_options_sorted(
.help("Log file. Standard error is used if not specified") .help("Log file. Standard error is used if not specified")
.num_args(1) .num_args(1)
.group("logging"), .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") Arg::new("memory")
.long("memory") .long("memory")
.help( .help(
@@ -515,7 +521,8 @@ fn start_vmm(
Box::new(std::io::stderr()) Box::new(std::io::stderr())
}; };
let logger = Logger::new(log_file).map_err(Error::LoggerFormat)?; let format = cmd_arguments.get_one::<String>("log-format").unwrap();
let logger = Logger::new(log_file, format).map_err(Error::LoggerFormat)?;
log::set_boxed_logger(Box::new(logger)) log::set_boxed_logger(Box::new(logger))
.map(|()| log::set_max_level(log_level)) .map(|()| log::set_max_level(log_level))
.map_err(Error::LoggerSetup)?; .map_err(Error::LoggerSetup)?;