grafix_toolbox/kit/policies/func/
logger.rs1pub 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;