grafix_toolbox/kit/policies/func/
logging.rs

1pub use super::result::{UniformUnwrap, UniformUnwrapOrDefault};
2pub use text_color::term::*;
3
4pub async fn Term() -> io::Stdout {
5	io::stdout()
6}
7
8pub async fn TermErr() -> io::Stderr {
9	io::stderr()
10}
11
12pub async fn File() -> fs::File {
13	term_color::disable();
14	unwrap(fs::File::create("log.txt").await, "couldn't create log file")
15}
16
17pub async fn Null() -> io::Sink {
18	io::sink()
19}
20
21pub struct Logger;
22impl Logger {
23	pub fn initialize<F>(out: impl FnOnce() -> F + SendStat, l: Level) -> Self
24	where
25		F: Future<Output: AsyncWrite + Unpin + Send> + Send,
26	{
27		Self::init_logger(out, l);
28		Self
29	}
30	pub fn log(msg: String) {
31		Self::init_logger(check_order, Level::INFO);
32
33		unwrap(unwrap_o(unsafe { LOGGER.get() }, "already exited").sender.send(Message::M(msg)), "failed to send");
34	}
35	pub fn shutdown_hook(f: impl FnOnce() + SendStat) {
36		POSTMORTEM.lock().expect("E| Shutdown hook add failed").push(Box(f));
37	}
38
39	pub fn level() -> i32 {
40		unsafe { LEVEL as i32 }
41	}
42	pub fn set_level(l: Level) {
43		unsafe { LEVEL = l }
44	}
45	fn init_logger<F>(out: impl FnOnce() -> F + SendStat, l: Level)
46	where
47		F: Future<Output: AsyncWrite + Unpin + Send> + Send,
48	{
49		unsafe {
50			LOGGER.get_or_init(move || {
51				std::panic::set_hook(Box(|i| {
52					let bt = process_backtrace(Backtrace::force_capture());
53					let p = i.payload();
54					let p = p.downcast_ref::<String>().cloned().or_else(|| p.downcast_ref::<&str>().map(|s| s.to_string()));
55					let P = "P|".red().bold();
56					Logger::log(format!(
57						"{P} {bt}{P} {} |{}|{}\n",
58						p.unwrap_or("???".into()).red(),
59						i.location().map_or("???".to_string(), |s| format!("{s}")),
60						thread::current().name().unwrap_or("???")
61					));
62				}));
63				LEVEL = l;
64				let (sender, mut rx) = chan::unbounded::<Message>();
65				let writer = task::Runtime().spawn(async move {
66					let mut out = out().await;
67					while let Some(Message::M(msg)) = rx.recv().await {
68						unwrap(out.write_all(msg.as_bytes()).await, "failed write");
69					}
70					unwrap(out.flush().await, "failed flush")
71				});
72				LoggerState { writer, sender }
73			});
74		}
75	}
76}
77impl Drop for Logger {
78	fn drop(&mut self) {
79		Self::init_logger(check_order, Level::INFO);
80		POSTMORTEM.lock().expect("E| Shutdown hooks failed").drain(..).for_each(|f| f());
81		let LoggerState { writer, sender } = unsafe { LOGGER.take() }.valid();
82		unwrap(sender.send(Message::Close), "failed to close system");
83		task::Runtime().finish(writer);
84	}
85}
86
87#[derive(Clone, Copy)]
88pub enum Level {
89	SILENT = 0,
90	PRINT = 1,
91	WARNING = 2,
92	INFO = 3,
93	DEBUG = 4,
94}
95static mut LEVEL: Level = Level::INFO;
96static mut LOGGER: OnceLock<LoggerState> = OnceLock::new();
97static POSTMORTEM: Mutex<Vec<Box<dyn FnOnce() + Send>>> = Mutex::new(vec![]);
98
99struct LoggerState {
100	writer: Task<()>,
101	sender: Sender<Message>,
102}
103
104enum Message {
105	M(String),
106	Close,
107}
108
109pub fn process_backtrace(bt: Backtrace) -> String {
110	let ignore = [
111		"          at /rustc/",
112		"kit/policies/logging.rs:",
113		": std::panic",
114		": std::rt::lang_start",
115		": std::sys_common::backtrace::",
116		": core::ops::function::",
117		": rust_begin_unwind",
118		": core::panicking::",
119		": __libc_start_",
120		": _start",
121		": <alloc::boxed::Box<F,A> as core::ops::function::Fn<Args>>::call",
122		": grafix_toolbox::kit::policies::logging::Logger::init_logger::",
123	];
124
125	let bt = bt
126		.to_string()
127		.lines()
128		.filter(|l| {
129			for i in ignore {
130				if l.contains(i) {
131					return false;
132				}
133			}
134			true
135		})
136		.chain(["  ↑"])
137		.fold("BACKTRACE\n".to_string(), |a, l| a + l + "\n");
138	bt
139}
140
141fn unwrap<T, E: std::fmt::Debug>(v: Result<T, E>, msg: &str) -> T {
142	match v {
143		Ok(v) => v,
144		Err(e) => {
145			let bt = process_backtrace(Backtrace::force_capture());
146			println!("{bt}Logger panic: {msg} {e:?}");
147			panic!();
148		}
149	}
150}
151fn unwrap_o<T>(v: Option<T>, msg: &str) -> T {
152	match v {
153		Some(v) => v,
154		None => {
155			let bt = process_backtrace(Backtrace::force_capture());
156			println!("{bt}Logger panic: {msg}");
157			panic!();
158		}
159	}
160}
161
162async fn check_order() -> io::Stdout {
163	panic!("E| No logger! Add 'LOGGER!(logging::Term, INFO);' as first line in main()");
164}
165
166use crate::{asyn::*, lib::*, text_color};
167use std::backtrace::Backtrace;