// Copyright (c) 2025 shibedrill // SPDX-License-Identifier: MIT use crate::constants::LOG_DEFAULT_LEVEL; use crate::format; use crate::memory::alloc::boxed::Box; use crate::memory::alloc::string::String; use crate::memory::alloc::vec::Vec; use lazy_static::lazy_static; use spin::Mutex; pub type LogDevice = dyn Fn(&str) + Send + Sync; pub struct Logger { pub level: LogLevel, devices: Vec>, } lazy_static! { pub static ref LOGGER: Mutex = Mutex::new(Logger::new()); } #[macro_export] macro_rules! log_info { ($($arg:tt)*) => { LOGGER.lock().log(LogLevel::Info, &format!($($arg)*), file!(), line!(), column!()) }; } #[macro_export] macro_rules! log_trace { ($($arg:tt)*) => { LOGGER.lock().log(LogLevel::Trace, &format!($($arg)*), file!(), line!(), column!()) }; } #[macro_export] macro_rules! log_warning { ($($arg:tt)*) => { LOGGER.lock().log(LogLevel::Warning, &format!($($arg)*), file!(), line!(), column!()) }; } #[macro_export] macro_rules! log_error { ($($arg:tt)*) => { LOGGER.lock().log(LogLevel::Error, &format!($($arg)*), file!(), line!(), column!()) }; } #[macro_export] macro_rules! log_critical { ($($arg:tt)*) => { LOGGER.lock().log(LogLevel::Critical, &format!($($arg)*), file!(), line!(), column!()) }; } impl Logger { pub fn new() -> Self { Logger { level: LOG_DEFAULT_LEVEL, devices: Vec::new(), } } pub fn log( &mut self, level: LogLevel, message: &str, file: &'static str, line: u32, column: u32, ) { for dev in &self.devices { let message = format!( "{} {}:{},{}- {}", level.normalized_string(), file, line, column, message ); dev(&message) } } pub fn add_device(&mut self, device: Box) { self.devices.push(device); } } pub struct EnumParseError {} #[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 for LogLevel { type Error = EnumParseError; fn try_from(from: u8) -> Result>::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>::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 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() } } impl LogLevel { fn normalized_string(&self) -> &str { match self { LogLevel::Critical => "[CRIT ]", LogLevel::Error => "[ERROR]", LogLevel::Info => "[INFO ]", LogLevel::Trace => "[TRACE]", LogLevel::Warning => "[WARN ]", LogLevel::Disabled => "[DSBLD]", } } }