gila/src/kernel/log.rs

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()
}
}