1use std::fs::OpenOptions;
2use std::io::Write;
3use std::path::Path;
4use std::sync::{Mutex, OnceLock};
5
6static LOG_SINK: OnceLock<Mutex<Box<dyn Write + Send>>> = OnceLock::new();
11
12#[derive(Debug, Clone, Copy)]
13pub enum Level {
14 Error,
15 Warn,
16 Info,
17 Debug,
18}
19
20impl Level {
21 fn as_str(self) -> &'static str {
22 match self {
23 Self::Error => "ERROR",
24 Self::Warn => "WARN",
25 Self::Info => "INFO",
26 Self::Debug => "DEBUG",
27 }
28 }
29}
30
31pub fn init_file(path: impl AsRef<Path>) {
34 let path = path.as_ref();
35 if let Some(parent) = path.parent() {
36 let _ = std::fs::create_dir_all(parent);
37 }
38 if let Ok(f) = OpenOptions::new()
39 .create(true)
40 .write(true)
41 .truncate(true)
42 .open(path)
43 {
44 let _ = LOG_SINK.set(Mutex::new(Box::new(f)));
45 }
46}
47
48pub fn init_stderr() {
50 let _ = LOG_SINK.set(Mutex::new(Box::new(std::io::stderr())));
51}
52
53pub fn write(level: Level, msg: &str) {
55 if let Some(mtx) = LOG_SINK.get() {
56 if let Ok(mut w) = mtx.lock() {
57 let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
58 let _ = writeln!(w, "[{}] {}: {}", ts, level.as_str(), msg);
59 }
60 }
61}
62
63#[macro_export]
67macro_rules! rlog {
68 ($level:ident, $($arg:tt)+) => {
69 $crate::log::write($crate::log::Level::$level, &format!($($arg)+))
70 };
71}