Make the logger in OSTD injectable

This commit is contained in:
Chen Chengjun
2024-11-15 16:57:48 +08:00
committed by Tate, Hongliang Tian
parent b17cbb2408
commit 7865469998
2 changed files with 58 additions and 68 deletions

View File

@ -34,7 +34,6 @@ num-derive = { version = "0.4", default-features = false }
num-traits = { version = "0.2", default-features = false } num-traits = { version = "0.2", default-features = false }
ostd-macros = { version = "0.10.0", path = "libs/ostd-macros" } ostd-macros = { version = "0.10.0", path = "libs/ostd-macros" }
ostd-test = { version = "0.10.0", path = "libs/ostd-test" } ostd-test = { version = "0.10.0", path = "libs/ostd-test" }
owo-colors = { version = "3", optional = true }
ostd-pod = { git = "https://github.com/asterinas/ostd-pod", rev = "c4644be", version = "0.1.1" } ostd-pod = { git = "https://github.com/asterinas/ostd-pod", rev = "c4644be", version = "0.1.1" }
spin = "0.9.4" spin = "0.9.4"
smallvec = "1.13.2" smallvec = "1.13.2"
@ -62,7 +61,6 @@ sbi-rt = "0.0.3"
fdt = { version = "0.1.5", features = ["pretty-printing"] } fdt = { version = "0.1.5", features = ["pretty-printing"] }
[features] [features]
default = ["cvm_guest", "log_color"] default = ["cvm_guest"]
log_color = ["dep:owo-colors"]
# The guest OS support for Confidential VMs (CVMs), e.g., Intel TDX # The guest OS support for Confidential VMs (CVMs), e.g., Intel TDX
cvm_guest = ["dep:tdx-guest", "dep:iced-x86"] cvm_guest = ["dep:tdx-guest", "dep:iced-x86"]

View File

@ -2,36 +2,61 @@
//! Logging support. //! Logging support.
//! //!
//! Currently the logger prints the logs to the console. //! This module provides a default log implementation while allowing users to inject
//! their own logger at a higher level.
//! //!
//! This module guarantees _atomicity_ under concurrency: messages are always //! Generally IRQs are disabled while printing. So do not print long log messages.
//! printed in their entirety without being mixed with messages generated
//! concurrently on other cores. use core::str::FromStr;
//!
//! IRQs are disabled while printing. So do not print long log messages.
use log::{LevelFilter, Metadata, Record}; use log::{LevelFilter, Metadata, Record};
use spin::Once;
use crate::{ use crate::boot::{kcmdline::ModuleArg, kernel_cmdline};
boot::{kcmdline::ModuleArg, kernel_cmdline},
timer::Jiffies,
};
const LOGGER: Logger = Logger {}; static LOGGER: Logger = Logger::new();
struct Logger {} struct Logger {
backend: Once<&'static dyn log::Log>,
}
impl Logger {
const fn new() -> Self {
Self {
backend: Once::new(),
}
}
}
/// Injects a logger as the global logger backend.
///
/// This method allows upper-level users to inject their own implemented loggers,
/// but only allows injecting once. Subsequent injection will have no effect.
///
/// **Caution**: The implementation of log operation in the injected logger should ideally be
/// heap-free and not involve sleep operations. Otherwise, users should refrain from calling `log`
/// in sensitive locations, such as during heap allocations, as this may cause the system to block.
pub fn inject_logger(new_logger: &'static dyn log::Log) {
LOGGER.backend.call_once(|| new_logger);
}
impl log::Log for Logger { impl log::Log for Logger {
fn enabled(&self, metadata: &Metadata) -> bool { fn enabled(&self, metadata: &Metadata) -> bool {
metadata.level() <= log::max_level() if let Some(logger) = self.backend.get() {
return logger.enabled(metadata);
};
// Default implementation.
true
} }
fn log(&self, record: &Record) { fn log(&self, record: &Record) {
if !self.enabled(record.metadata()) { if let Some(logger) = self.backend.get() {
return; return logger.log(record);
} };
// Default implementation.
let timestamp = Jiffies::elapsed().as_duration().as_secs_f64();
let level = record.level(); let level = record.level();
// Use a global lock to prevent interleaving of log messages. // Use a global lock to prevent interleaving of log messages.
@ -39,44 +64,19 @@ impl log::Log for Logger {
static RECORD_LOCK: SpinLock<()> = SpinLock::new(()); static RECORD_LOCK: SpinLock<()> = SpinLock::new(());
let _lock = RECORD_LOCK.disable_irq().lock(); let _lock = RECORD_LOCK.disable_irq().lock();
cfg_if::cfg_if! { crate::console::early_print(format_args!("{}: {}\n", level, record.args()));
if #[cfg(feature = "log_color")]{ }
use owo_colors::Style;
let timestamp_style = Style::new().green(); fn flush(&self) {
let record_style = Style::new().default_color(); if let Some(logger) = self.backend.get() {
let level_style = match record.level() { logger.flush();
log::Level::Error => Style::new().red(),
log::Level::Warn => Style::new().bright_yellow(),
log::Level::Info => Style::new().blue(),
log::Level::Debug => Style::new().bright_green(),
log::Level::Trace => Style::new().bright_black(),
}; };
crate::console::early_print(
format_args!("{} {:<5}: {}\n",
timestamp_style.style(format_args!("[{:>10.3}]", timestamp)),
level_style.style(level),
record_style.style(record.args()))
);
}else{
crate::console::early_print(
format_args!("{} {:<5}: {}\n",
format_args!("[{:>10.3}]", timestamp),
level,
record.args())
);
} }
}
}
fn flush(&self) {}
} }
/// Initialize the logger. Users should avoid using the log macros before this function is called. /// Initialize the logger. Users should avoid using the log macros before this function is called.
pub(crate) fn init() { pub(crate) fn init() {
let level = get_log_level().unwrap_or(LevelFilter::Off); let level = get_log_level().unwrap_or(LevelFilter::Off);
log::set_max_level(level); log::set_max_level(level);
log::set_logger(&LOGGER).unwrap(); log::set_logger(&LOGGER).unwrap();
} }
@ -84,22 +84,14 @@ pub(crate) fn init() {
fn get_log_level() -> Option<LevelFilter> { fn get_log_level() -> Option<LevelFilter> {
let module_args = kernel_cmdline().get_module_args("ostd")?; let module_args = kernel_cmdline().get_module_args("ostd")?;
let arg = module_args.iter().find(|arg| match arg { let value = {
ModuleArg::Arg(_) => false, let value = module_args.iter().find_map(|arg| match arg {
ModuleArg::KeyVal(name, _) => name.as_bytes() == "log_level".as_bytes(), ModuleArg::KeyVal(name, value) if name.as_bytes() == "log_level".as_bytes() => {
Some(value)
}
_ => None,
})?; })?;
value.as_c_str().to_str().ok()?
let ModuleArg::KeyVal(_, value) = arg else {
unreachable!()
}; };
let value = value.as_c_str().to_str().unwrap_or("off"); LevelFilter::from_str(value).ok()
Some(match value {
"error" => LevelFilter::Error,
"warn" => LevelFilter::Warn,
"info" => LevelFilter::Info,
"debug" => LevelFilter::Debug,
"trace" => LevelFilter::Trace,
// Otherwise, OFF
_ => LevelFilter::Off,
})
} }