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