mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 20:48:55 +00:00
d6e05576c4
Update the broker code to pass the logging arguments to child processes. This also updates the base syslog LogConfig to separate out the serializable logging arguments from the active logging configuration, and also updates the LogConfig to no longer require generics when they aren't used in practice. Test: passed --log-level to run-mp, verified child processes logs Change-Id: I98f046555610fa804df63b1e6ead3603e96f7666 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/4967905 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Colin Downs-Razouk <colindr@google.com>
319 lines
7.6 KiB
Rust
319 lines
7.6 KiB
Rust
// Copyright 2023 The ChromiumOS Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#![allow(clippy::field_reassign_with_default)]
|
|
|
|
use std::io;
|
|
use std::io::Write;
|
|
use std::sync::Arc;
|
|
|
|
use base::syslog::test_only_ensure_inited;
|
|
use base::syslog::LogArgs;
|
|
use base::syslog::LogConfig;
|
|
use base::syslog::Priority;
|
|
use base::syslog::State;
|
|
use base::syslog::Syslogger;
|
|
use env_logger::fmt;
|
|
use log::Level;
|
|
use log::Log;
|
|
use log::Record;
|
|
use sync::Mutex;
|
|
|
|
#[derive(Clone)]
|
|
struct MockWrite {
|
|
buffer: Arc<Mutex<Vec<u8>>>,
|
|
}
|
|
|
|
impl MockWrite {
|
|
fn new() -> Self {
|
|
Self {
|
|
buffer: Arc::new(Mutex::new(vec![])),
|
|
}
|
|
}
|
|
|
|
fn into_inner(self) -> Vec<u8> {
|
|
Arc::try_unwrap(self.buffer).unwrap().into_inner()
|
|
}
|
|
}
|
|
|
|
impl Write for MockWrite {
|
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
self.buffer.lock().write(buf)
|
|
}
|
|
|
|
fn flush(&mut self) -> io::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn syslog_log() {
|
|
let state = State::default();
|
|
state.log(
|
|
&log::RecordBuilder::new()
|
|
.level(Level::Error)
|
|
.file(Some(file!()))
|
|
.line(Some(line!()))
|
|
.args(format_args!("hello syslog"))
|
|
.build(),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn proc_name() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
proc_name: String::from("syslog-test"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
state.log(
|
|
&log::RecordBuilder::new()
|
|
.level(Level::Error)
|
|
.file(Some(file!()))
|
|
.line(Some(line!()))
|
|
.args(format_args!("hello syslog"))
|
|
.build(),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn macros() {
|
|
test_only_ensure_inited().unwrap();
|
|
log::error!("this is an error {}", 3);
|
|
log::warn!("this is a warning {}", "uh oh");
|
|
log::info!("this is info {}", true);
|
|
log::debug!("this is debug info {:?}", Some("helpful stuff"));
|
|
}
|
|
|
|
fn pipe_formatter(buf: &mut fmt::Formatter, record: &Record<'_>) -> io::Result<()> {
|
|
writeln!(buf, "{}", record.args())
|
|
}
|
|
|
|
#[test]
|
|
fn syslogger_char() {
|
|
let output = MockWrite::new();
|
|
let mut cfg = LogConfig::default();
|
|
cfg.pipe_formatter = Some(Box::new(pipe_formatter));
|
|
cfg.pipe = Some(Box::new(output.clone()));
|
|
let state = Mutex::new(State::new(cfg).unwrap());
|
|
|
|
let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
|
|
|
|
let string = "chars";
|
|
for c in string.chars() {
|
|
syslogger.write_all(&[c as u8]).expect("error writing char");
|
|
}
|
|
|
|
syslogger
|
|
.write_all(&[b'\n'])
|
|
.expect("error writing newline char");
|
|
|
|
std::mem::drop(syslogger);
|
|
std::mem::drop(state);
|
|
assert_eq!(
|
|
format!("{}\n", string),
|
|
String::from_utf8_lossy(&output.into_inner()[..])
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn syslogger_line() {
|
|
let output = MockWrite::new();
|
|
let mut cfg = LogConfig::default();
|
|
cfg.pipe_formatter = Some(Box::new(pipe_formatter));
|
|
cfg.pipe = Some(Box::new(output.clone()));
|
|
let state = Mutex::new(State::new(cfg).unwrap());
|
|
|
|
let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
|
|
|
|
let s = "Writing string to syslog\n";
|
|
syslogger
|
|
.write_all(s.as_bytes())
|
|
.expect("error writing string");
|
|
|
|
std::mem::drop(syslogger);
|
|
std::mem::drop(state);
|
|
assert_eq!(s, String::from_utf8_lossy(&output.into_inner()[..]));
|
|
}
|
|
|
|
#[test]
|
|
fn syslogger_partial() {
|
|
let output = MockWrite::new();
|
|
let state = Mutex::new(
|
|
State::new(LogConfig {
|
|
pipe: Some(Box::new(output.clone())),
|
|
..Default::default()
|
|
})
|
|
.unwrap(),
|
|
);
|
|
|
|
let mut syslogger = Syslogger::test_only_from_state(Level::Info, || state.lock());
|
|
|
|
let s = "Writing partial string";
|
|
// Should not log because there is no newline character
|
|
syslogger
|
|
.write_all(s.as_bytes())
|
|
.expect("error writing string");
|
|
|
|
std::mem::drop(syslogger);
|
|
std::mem::drop(state);
|
|
assert_eq!(Vec::<u8>::new(), output.into_inner());
|
|
}
|
|
|
|
#[test]
|
|
fn log_priority_try_from_number() {
|
|
assert_eq!("0".try_into(), Ok(Priority::Emergency));
|
|
assert!(Priority::try_from("100").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn log_priority_try_from_words() {
|
|
assert_eq!("EMERGENCY".try_into(), Ok(Priority::Emergency));
|
|
assert!(Priority::try_from("_EMERGENCY").is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn log_should_always_be_enabled_for_level_show_all() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("trace"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn log_should_always_be_disabled_for_level_silent() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("off"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(!state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn log_should_be_enabled_if_filter_level_has_a_lower_or_equal_priority() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("info"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Info)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
assert!(state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Warn)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn log_should_be_disabled_if_filter_level_has_a_higher_priority() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("info"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(!state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn path_overides_should_apply_to_logs() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("info,test=debug"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(!state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
assert!(state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.target("test")
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|
|
|
|
#[test]
|
|
fn longest_path_prefix_match_should_apply_if_multiple_filters_match() {
|
|
let state = State::new(LogConfig {
|
|
log_args: LogArgs {
|
|
filter: String::from("info,test=debug,test::silence=off"),
|
|
..Default::default()
|
|
},
|
|
..Default::default()
|
|
})
|
|
.unwrap();
|
|
|
|
assert!(!state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
|
|
assert!(state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Debug)
|
|
.target("test")
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
assert!(!state.enabled(
|
|
log::RecordBuilder::new()
|
|
.level(Level::Error)
|
|
.target("test::silence")
|
|
.build()
|
|
.metadata(),
|
|
));
|
|
}
|