hermit/
logging.rs

1use core::fmt;
2
3use anstyle::AnsiColor;
4use log::{Level, LevelFilter, Metadata, Record};
5
6/// Data structure to filter kernel messages
7struct KernelLogger;
8
9impl log::Log for KernelLogger {
10	fn enabled(&self, _: &Metadata<'_>) -> bool {
11		true
12	}
13
14	fn flush(&self) {
15		// nothing to do
16	}
17
18	fn log(&self, record: &Record<'_>) {
19		if self.enabled(record.metadata()) {
20			println!(
21				"[{}][{}] {}",
22				crate::arch::core_local::core_id(),
23				ColorLevel(record.level()),
24				record.args()
25			);
26		}
27	}
28}
29
30struct ColorLevel(Level);
31
32impl fmt::Display for ColorLevel {
33	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34		let level = self.0;
35
36		if no_color() {
37			write!(f, "{level}")
38		} else {
39			let color = match level {
40				Level::Trace => AnsiColor::Magenta,
41				Level::Debug => AnsiColor::Blue,
42				Level::Info => AnsiColor::Green,
43				Level::Warn => AnsiColor::Yellow,
44				Level::Error => AnsiColor::Red,
45			};
46
47			let style = anstyle::Style::new().fg_color(Some(color.into()));
48			write!(f, "{style}{level}{style:#}")
49		}
50	}
51}
52
53fn no_color() -> bool {
54	option_env!("NO_COLOR").is_some_and(|val| !val.is_empty())
55}
56
57pub unsafe fn init() {
58	log::set_logger(&KernelLogger).expect("Can't initialize logger");
59	// Determines LevelFilter at compile time
60	let log_level: Option<&'static str> = option_env!("HERMIT_LOG_LEVEL_FILTER");
61	let mut max_level = LevelFilter::Info;
62
63	if let Some(log_level) = log_level {
64		max_level = if log_level.eq_ignore_ascii_case("off") {
65			LevelFilter::Off
66		} else if log_level.eq_ignore_ascii_case("error") {
67			LevelFilter::Error
68		} else if log_level.eq_ignore_ascii_case("warn") {
69			LevelFilter::Warn
70		} else if log_level.eq_ignore_ascii_case("info") {
71			LevelFilter::Info
72		} else if log_level.eq_ignore_ascii_case("debug") {
73			LevelFilter::Debug
74		} else if log_level.eq_ignore_ascii_case("trace") {
75			LevelFilter::Trace
76		} else {
77			error!("Could not parse HERMIT_LOG_LEVEL_FILTER, falling back to `info`.");
78			LevelFilter::Info
79		};
80	}
81
82	log::set_max_level(max_level);
83}
84
85#[cfg(any(not(target_arch = "riscv64"), feature = "pci", feature = "tcp"))]
86macro_rules! infoheader {
87	// This should work on paper, but it's currently not supported :(
88	// Refer to https://github.com/rust-lang/rust/issues/46569
89	/*($($arg:tt)+) => ({
90		info!("");
91		info!("{:=^70}", format_args!($($arg)+));
92	});*/
93	($str:expr) => {{
94		::log::info!("");
95		::log::info!("{:=^70}", $str);
96	}};
97}
98
99#[cfg_attr(target_arch = "riscv64", allow(unused))]
100macro_rules! infoentry {
101	($str:expr, $rhs:expr) => (infoentry!($str, "{}", $rhs));
102	($str:expr, $($arg:tt)+) => (::log::info!("{:25}{}", concat!($str, ":"), format_args!($($arg)+)));
103}
104
105#[cfg(any(not(target_arch = "riscv64"), feature = "pci", feature = "tcp"))]
106macro_rules! infofooter {
107	() => {{
108		::log::info!("{:=^70}", '=');
109		::log::info!("");
110	}};
111}