Skip to main content

grafix_toolbox/kit/policies/func/
logger.rs

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