174 lines
4.6 KiB
Rust
174 lines
4.6 KiB
Rust
// Copyright (c) 2025 shibedrill
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
use core::fmt::Write;
|
|
|
|
use crate::constants::{LOG_DEFAULT_LEVEL, NEWLINE};
|
|
use crate::memory::alloc;
|
|
use alloc::boxed::*;
|
|
use alloc::string::*;
|
|
use alloc::vec::*;
|
|
use spin::Mutex;
|
|
|
|
#[macro_export]
|
|
macro_rules! log_info {
|
|
($($arg:tt)*) => {
|
|
LOGGER.log(LogLevel::Info, &format!($($arg)*), file!(), line!(), column!())
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! log_trace {
|
|
($($arg:tt)*) => {
|
|
LOGGER.log(LogLevel::Trace, &format!($($arg)*), file!(), line!(), column!())
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[allow(unused_macros)]
|
|
macro_rules! log_warning {
|
|
($($arg:tt)*) => {
|
|
LOGGER.log(LogLevel::Warning, &format!($($arg)*), file!(), line!(), column!())
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[allow(unused_macros)]
|
|
macro_rules! log_error {
|
|
($($arg:tt)*) => {
|
|
LOGGER.log(LogLevel::Error, &format!($($arg)*), file!(), line!(), column!())
|
|
};
|
|
}
|
|
|
|
#[macro_export]
|
|
#[allow(unused_macros)]
|
|
macro_rules! log_critical {
|
|
($($arg:tt)*) => {
|
|
LOGGER.log(LogLevel::Critical, &format!($($arg)*), file!(), line!(), column!())
|
|
};
|
|
}
|
|
|
|
pub struct Logger {
|
|
pub inner: Mutex<LoggerInner>,
|
|
}
|
|
|
|
impl Logger {
|
|
pub fn log(&self, level: LogLevel, msg: &str, file: &'static str, line: u32, column: u32) {
|
|
self.inner.lock().log(level, msg, file, line, column);
|
|
}
|
|
pub fn add_subscriber<T: LogSubscriber + Send + Sync + 'static>(&self, sub: T) {
|
|
self.inner
|
|
.lock()
|
|
.subscriber
|
|
.push(Mutex::new(alloc::boxed::Box::new(sub)));
|
|
}
|
|
pub fn set_level(&self, level: LogLevel) {
|
|
self.inner.lock().level = level;
|
|
}
|
|
}
|
|
|
|
// The logger exists for the entire lifetime of the kernel.
|
|
pub static LOGGER: Logger = Logger {
|
|
inner: Mutex::new(LoggerInner::new()),
|
|
};
|
|
|
|
pub struct EnumParseError {}
|
|
|
|
pub struct LoggerInner {
|
|
pub level: LogLevel,
|
|
pub subscriber: Vec<Mutex<Box<dyn LogSubscriber>>>,
|
|
}
|
|
|
|
pub trait LogSubscriber: Send + Sync {
|
|
fn log_write(&mut self, msg: &str);
|
|
}
|
|
|
|
impl Default for LoggerInner {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl LoggerInner {
|
|
pub const fn new() -> Self {
|
|
LoggerInner {
|
|
level: LOG_DEFAULT_LEVEL,
|
|
subscriber: Vec::new(),
|
|
}
|
|
}
|
|
|
|
// Calling log will sequentially acquire lock on all logging subscribers
|
|
// to write to them with a formatted log message.
|
|
pub fn log(&self, level: LogLevel, msg: &str, file: &'static str, line: u32, column: u32) {
|
|
// Nobody is EVER allowed to call log with the Disabled log level. It is a placeholder.
|
|
if level == LogLevel::Disabled {
|
|
// Nothing
|
|
} else if level <= self.level {
|
|
let level_string = String::from(level);
|
|
for sub in &self.subscriber {
|
|
let mut message = String::new();
|
|
write!(
|
|
&mut message,
|
|
"[{level_string}] {file}:{line},{column} - {msg}{NEWLINE}"
|
|
)
|
|
.unwrap();
|
|
sub.lock().log_write(&message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
|
|
pub enum LogLevel {
|
|
Disabled = 0,
|
|
Critical = 1,
|
|
Error = 2,
|
|
Warning = 3,
|
|
Info = 4,
|
|
Trace = 5,
|
|
}
|
|
|
|
impl TryFrom<u8> for LogLevel {
|
|
type Error = EnumParseError;
|
|
fn try_from(from: u8) -> Result<Self, <crate::log::LogLevel as TryFrom<u8>>::Error> {
|
|
match from {
|
|
0 => Ok(Self::Disabled),
|
|
1 => Ok(Self::Critical),
|
|
2 => Ok(Self::Error),
|
|
3 => Ok(Self::Warning),
|
|
4 => Ok(Self::Info),
|
|
5 => Ok(Self::Trace),
|
|
_ => Err(EnumParseError {}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&str> for LogLevel {
|
|
type Error = EnumParseError;
|
|
fn try_from(from: &str) -> Result<Self, <crate::log::LogLevel as TryFrom<u8>>::Error> {
|
|
match from.to_ascii_lowercase().as_ref() {
|
|
"disabled" => Ok(Self::Disabled),
|
|
"critical" => Ok(Self::Critical),
|
|
"error" => Ok(Self::Error),
|
|
"warning" => Ok(Self::Warning),
|
|
"info" => Ok(Self::Info),
|
|
"trace" => Ok(Self::Trace),
|
|
_ => Err(EnumParseError {}),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<LogLevel> for String {
|
|
fn from(from: LogLevel) -> String {
|
|
match from {
|
|
LogLevel::Disabled => "Disabled",
|
|
LogLevel::Critical => "CRIT ",
|
|
LogLevel::Error => "ERROR",
|
|
LogLevel::Warning => "WARN ",
|
|
LogLevel::Info => "INFO ",
|
|
LogLevel::Trace => "TRACE",
|
|
}
|
|
.into()
|
|
}
|
|
}
|