From fca911e5f35646c36541d3fd3b16c41fdef3c4df Mon Sep 17 00:00:00 2001 From: Rob Bradford Date: Wed, 7 Aug 2019 12:19:21 +0100 Subject: [PATCH] main: Add logging support controlled by command line This makes the log macros (error!, warn!, info!, etc) in the code work. It currently defaults to showing only error! messages, but by passing an increasing number of "-v"s on the command line the verbosity can be increased. By default log output goes onto stderr but it can also be sent to a file. Fixes: #121 Signed-off-by: Rob Bradford --- Cargo.lock | 1 + Cargo.toml | 2 ++ src/main.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 9c2cd0004..1194a406b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -160,6 +160,7 @@ dependencies = [ "credibility 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "ssh2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "vmm 0.1.0", diff --git a/Cargo.toml b/Cargo.toml index 98a0bccc8..38ffb3951 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition = "2018" [dependencies] clap = "2.33.0" +lazy_static = "1.3.0" +log = { version = "0.4.8", features = ["std"] } vmm = { path = "vmm" } [dev-dependencies] diff --git a/src/main.rs b/src/main.rs index 18a94089e..302817202 100755 --- a/src/main.rs +++ b/src/main.rs @@ -9,9 +9,55 @@ extern crate vmm; extern crate clap; use clap::{App, Arg}; +use log::LevelFilter; use std::process; +use std::sync::Mutex; use vmm::config; +struct Logger { + output: Mutex>, + start: std::time::Instant, +} + +impl log::Log for Logger { + fn enabled(&self, _metadata: &log::Metadata) -> bool { + true + } + + fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + + let now = std::time::Instant::now(); + let duration = now.duration_since(self.start); + + if record.file().is_some() && record.line().is_some() { + writeln!( + *(*(self.output.lock().unwrap())), + "cloud-hypervisor: {:?}: {}:{}:{} -- {}", + duration, + record.level(), + record.file().unwrap(), + record.line().unwrap(), + record.args() + ) + .expect("Failed to write to log file"); + } else { + writeln!( + *(*(self.output.lock().unwrap())), + "cloud-hypervisor: {:?}: {}:{} -- {}", + duration, + record.level(), + record.target(), + record.args() + ) + .expect("Failed to write to log file"); + } + } + fn flush(&self) {} +} + fn main() { let cmd_arguments = App::new("cloud-hypervisor") .version(crate_version!()) @@ -107,6 +153,19 @@ fn main() { .takes_value(true) .min_values(1), ) + .arg( + Arg::with_name("v") + .short("v") + .multiple(true) + .help("Sets the level of debugging output"), + ) + .arg( + Arg::with_name("log-file") + .long("log-file") + .help("Log file. Standard error is used if not specified") + .takes_value(true) + .min_values(1), + ) .get_matches(); // These .unwrap()s cannot fail as there is a default value defined @@ -127,6 +186,30 @@ fn main() { let pmem: Option> = cmd_arguments.values_of("pmem").map(|x| x.collect()); let devices: Option> = cmd_arguments.values_of("device").map(|x| x.collect()); + let log_level = match cmd_arguments.occurrences_of("v") { + 0 => LevelFilter::Error, + 1 => LevelFilter::Warn, + 2 => LevelFilter::Info, + 3 => LevelFilter::Debug, + _ => LevelFilter::Trace, + }; + + let log_file: Box = + if let Some(file) = cmd_arguments.value_of("log-file") { + Box::new( + std::fs::File::create(std::path::Path::new(file)).expect("Error creating log file"), + ) + } else { + Box::new(std::io::stderr()) + }; + + log::set_boxed_logger(Box::new(Logger { + output: Mutex::new(log_file), + start: std::time::Instant::now(), + })) + .map(|()| log::set_max_level(log_level)) + .expect("Expected to be able to setup logger"); + let vm_config = match config::VmConfig::parse(config::VmParams { cpus, memory,