mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
main: Add more log format options
Add {wallclock}, {pid}, and {tid} tokens to the log format system.
Assisted-by: Claude:Opus-4.7
Signed-off-by: Rob Bradford <rbradford@meta.com>
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -449,6 +449,7 @@ dependencies = [
|
|||||||
"epoll",
|
"epoll",
|
||||||
"event_monitor",
|
"event_monitor",
|
||||||
"hypervisor",
|
"hypervisor",
|
||||||
|
"jiff",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"net_util",
|
"net_util",
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ env_logger = "0.11.10"
|
|||||||
epoll = "4.4.0"
|
epoll = "4.4.0"
|
||||||
flume = "0.12.0"
|
flume = "0.12.0"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
|
jiff = { version = "0.2", default-features = false, features = ["std"] }
|
||||||
libc = "0.2.186"
|
libc = "0.2.186"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
sha2 = "0.11.0"
|
sha2 = "0.11.0"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ env_logger = { workspace = true }
|
|||||||
epoll = { workspace = true }
|
epoll = { workspace = true }
|
||||||
event_monitor = { path = "../event_monitor" }
|
event_monitor = { path = "../event_monitor" }
|
||||||
hypervisor = { path = "../hypervisor" }
|
hypervisor = { path = "../hypervisor" }
|
||||||
|
jiff = { workspace = true }
|
||||||
libc = { workspace = true }
|
libc = { workspace = true }
|
||||||
log = { workspace = true, features = ["std"] }
|
log = { workspace = true, features = ["std"] }
|
||||||
option_parser = { path = "../option_parser" }
|
option_parser = { path = "../option_parser" }
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ pub enum Error {
|
|||||||
enum Token {
|
enum Token {
|
||||||
Literal(String),
|
Literal(String),
|
||||||
BootTime,
|
BootTime,
|
||||||
|
WallClock,
|
||||||
|
Pid,
|
||||||
|
Tid,
|
||||||
Thread,
|
Thread,
|
||||||
Level,
|
Level,
|
||||||
Location,
|
Location,
|
||||||
@@ -35,6 +38,9 @@ impl FromStr for Token {
|
|||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
"boottime" => Ok(Self::BootTime),
|
"boottime" => Ok(Self::BootTime),
|
||||||
|
"wallclock" => Ok(Self::WallClock),
|
||||||
|
"pid" => Ok(Self::Pid),
|
||||||
|
"tid" => Ok(Self::Tid),
|
||||||
"thread" => Ok(Self::Thread),
|
"thread" => Ok(Self::Thread),
|
||||||
"level" => Ok(Self::Level),
|
"level" => Ok(Self::Level),
|
||||||
"location" => Ok(Self::Location),
|
"location" => Ok(Self::Location),
|
||||||
@@ -96,6 +102,7 @@ pub const DEFAULT_FORMAT: &str =
|
|||||||
pub struct Logger {
|
pub struct Logger {
|
||||||
output: Mutex<Box<dyn Write + Send>>,
|
output: Mutex<Box<dyn Write + Send>>,
|
||||||
start: Instant,
|
start: Instant,
|
||||||
|
pid: u32,
|
||||||
tokens: Vec<Token>,
|
tokens: Vec<Token>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +111,7 @@ impl Logger {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
output: Mutex::new(output),
|
output: Mutex::new(output),
|
||||||
start: Instant::now(),
|
start: Instant::now(),
|
||||||
|
pid: std::process::id(),
|
||||||
tokens: parse_format(format)?,
|
tokens: parse_format(format)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -126,6 +134,14 @@ impl log::Log for Logger {
|
|||||||
Token::Literal(s) => out.write_all(s.as_bytes()),
|
Token::Literal(s) => out.write_all(s.as_bytes()),
|
||||||
// 10: 6 decimal places + sep => whole seconds in range `0..=999` properly aligned
|
// 10: 6 decimal places + sep => whole seconds in range `0..=999` properly aligned
|
||||||
Token::BootTime => write!(&mut *out, "{duration_s:>10.6?}"),
|
Token::BootTime => write!(&mut *out, "{duration_s:>10.6?}"),
|
||||||
|
Token::WallClock => write!(
|
||||||
|
out,
|
||||||
|
"{}",
|
||||||
|
jiff::Zoned::now().strftime("%Y-%m-%dT%H:%M:%S%.6fZ")
|
||||||
|
),
|
||||||
|
Token::Pid => write!(&mut *out, "{}", self.pid),
|
||||||
|
// SAFETY: gettid(2) always succeeds
|
||||||
|
Token::Tid => write!(&mut *out, "{}", unsafe { libc::gettid() }),
|
||||||
Token::Thread => write!(
|
Token::Thread => write!(
|
||||||
&mut *out,
|
&mut *out,
|
||||||
"{}",
|
"{}",
|
||||||
@@ -182,6 +198,9 @@ mod tests {
|
|||||||
.map(|t| match t {
|
.map(|t| match t {
|
||||||
Token::Literal(s) => format!("L({s})"),
|
Token::Literal(s) => format!("L({s})"),
|
||||||
Token::BootTime => "B".to_string(),
|
Token::BootTime => "B".to_string(),
|
||||||
|
Token::WallClock => "W".to_string(),
|
||||||
|
Token::Pid => "P".to_string(),
|
||||||
|
Token::Tid => "I".to_string(),
|
||||||
Token::Thread => "T".to_string(),
|
Token::Thread => "T".to_string(),
|
||||||
Token::Level => "V".to_string(),
|
Token::Level => "V".to_string(),
|
||||||
Token::Location => "O".to_string(),
|
Token::Location => "O".to_string(),
|
||||||
@@ -205,8 +224,14 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_all_known_tokens() {
|
fn parse_all_known_tokens() {
|
||||||
let tokens = parse_format("[{boottime}] <{thread}> {level} {location} -- {msg}").unwrap();
|
let tokens = parse_format(
|
||||||
assert_eq!(render(&tokens), "L([)|B|L(] <)|T|L(> )|V|L( )|O|L( -- )|M");
|
"[{boottime}] {wallclock} {pid}/{tid} <{thread}> {level} {location} -- {msg}",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
render(&tokens),
|
||||||
|
"L([)|B|L(] )|W|L( )|P|L(/)|I|L( <)|T|L(> )|V|L( )|O|L( -- )|M"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -328,6 +353,68 @@ mod tests {
|
|||||||
assert!(!out.contains("foo.rs"), "got: {out}");
|
assert!(!out.contains("foo.rs"), "got: {out}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logger_wallclock_is_rfc3339() {
|
||||||
|
let buf = SharedBuffer::default();
|
||||||
|
let logger = Logger::new(Box::new(buf.clone()), "{wallclock}").unwrap();
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
&log::Record::builder()
|
||||||
|
.args(format_args!(""))
|
||||||
|
.level(log::Level::Info)
|
||||||
|
.target("t")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out = buf.contents();
|
||||||
|
let out = out.trim();
|
||||||
|
assert_eq!(out.len(), 27, "got: {out}");
|
||||||
|
assert!(out.ends_with('Z'), "got: {out}");
|
||||||
|
assert_eq!(&out[4..5], "-", "got: {out}");
|
||||||
|
assert_eq!(&out[7..8], "-", "got: {out}");
|
||||||
|
assert_eq!(&out[10..11], "T", "got: {out}");
|
||||||
|
assert_eq!(&out[13..14], ":", "got: {out}");
|
||||||
|
assert_eq!(&out[16..17], ":", "got: {out}");
|
||||||
|
assert_eq!(&out[19..20], ".", "got: {out}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logger_pid_token() {
|
||||||
|
let buf = SharedBuffer::default();
|
||||||
|
let logger = Logger::new(Box::new(buf.clone()), "{pid}").unwrap();
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
&log::Record::builder()
|
||||||
|
.args(format_args!(""))
|
||||||
|
.level(log::Level::Info)
|
||||||
|
.target("t")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out = buf.contents();
|
||||||
|
let out = out.trim();
|
||||||
|
assert_eq!(out, std::process::id().to_string(), "got: {out}");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn logger_tid_token() {
|
||||||
|
let buf = SharedBuffer::default();
|
||||||
|
let logger = Logger::new(Box::new(buf.clone()), "{tid}").unwrap();
|
||||||
|
|
||||||
|
logger.log(
|
||||||
|
&log::Record::builder()
|
||||||
|
.args(format_args!(""))
|
||||||
|
.level(log::Level::Info)
|
||||||
|
.target("t")
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out = buf.contents();
|
||||||
|
let out = out.trim();
|
||||||
|
let tid: i64 = out.parse().expect("tid should be numeric");
|
||||||
|
assert!(tid > 0, "got: {tid}");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn logger_appends_each_record() {
|
fn logger_appends_each_record() {
|
||||||
let buf = SharedBuffer::default();
|
let buf = SharedBuffer::default();
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ fn get_cli_options_sorted(
|
|||||||
.group("logging"),
|
.group("logging"),
|
||||||
Arg::new("log-format")
|
Arg::new("log-format")
|
||||||
.long("log-format")
|
.long("log-format")
|
||||||
.help("Log format. Available tokens: {boottime}, {thread}, {level}, {location}, {msg}")
|
.help("Log format. Available tokens: {boottime}, {wallclock}, {pid}, {tid}, {thread}, {level}, {location}, {msg}")
|
||||||
.num_args(1)
|
.num_args(1)
|
||||||
.default_value(logger::DEFAULT_FORMAT)
|
.default_value(logger::DEFAULT_FORMAT)
|
||||||
.group("logging"),
|
.group("logging"),
|
||||||
|
|||||||
Reference in New Issue
Block a user