将恐慌重定向到指定的缓冲区
Redirect panics to a specified buffer
有什么办法吗?在终端图形库中,如果发生异常,异常将在显示之前被冲走,使得使用该库进行编程很难调试。
impl Drop for Terminal {
fn drop(&mut self) {
self.outbuffer.write_all(&self.driver.get(DevFn::ShowCursor)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::Reset)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::Clear)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::ExitCa)).unwrap();
self.flush().unwrap(); // If an exception occurs, this will reclear the screen and remove the output
self.termctl.reset().unwrap();
SIGWINCH_STATUS.store(false, Ordering::SeqCst);
RUSTTY_STATUS.store(false, Ordering::SeqCst);
}
}
如果我注释掉 self.flush().unwrap();
会打印异常,但是即使程序结束,终端也不会正确刷新屏幕并在终端上留下图形。
是否可以在程序开始时指定一个自定义缓冲区 panic 将用于写入?或者可能写一个 hacky 技巧来做到这一点?这样,在刷新之后我们可以检查这个缓冲区中是否有任何东西,如果是这样我们就知道发生了异常并可以将其打印出来。
运行一个故意算术溢出崩溃的程序,目前输出只有
然而,通过注释掉 self.flush().unwrap();
,我们看到了实际的异常,但现在是一个非常丑陋的终端。该解决方案将无法正常工作,因为正确执行的程序仍需要刷新,因为不需要显示任何错误
Panics 消息目前写入到 stderr,因此执行此操作的 hacky 方法是将 stderr 重定向到文件 (cargo run 2>/path/to/panic.log
)。
或者,您可以使用 gag 在您的程序本身中执行此操作(免责声明,我编写了这个库)。不幸的是,它不适用于 Windows.
以下将重定向 stderr,直到 stderr_redirect
被删除:
use std::fs::OpenOptions;
use gag::Redirect;
let log = OpenOptions::new()
.truncate(true)
.read(true)
.create(true)
.write(true)
.open("/path/to/panic.log")
.unwrap();
let stderr_redirect = Redirect::stderr(log).unwrap();
// Your code here...
您还可以通过执行以下操作在临时文件中缓冲 stderr:
use gag::BufferRedirect;
let mut stderr_buffer = BufferRedirect::stderr().unwrap();
您需要使用 std::panic::set_hook
that captures the output. You can then catch the panic with std::panic::catch_unwind
:
注册一个 panic hook
use std::{
panic,
sync::{Arc, Mutex},
};
fn main() {
let global_buffer = Arc::new(Mutex::new(String::new()));
let old_hook = panic::take_hook();
panic::set_hook({
let global_buffer = global_buffer.clone();
Box::new(move |info| {
let mut global_buffer = global_buffer.lock().unwrap();
if let Some(s) = info.payload().downcast_ref::<&str>() {
global_buffer.push_str(s);
}
})
});
let result = panic::catch_unwind(|| {
panic!("test panic");
});
panic::set_hook(old_hook);
match result {
Ok(res) => res,
Err(_) => {
println!("caught panic!");
println!("I captured:\n\n{}", global_buffer.lock().unwrap())
}
}
}
另请参阅:
有什么办法吗?在终端图形库中,如果发生异常,异常将在显示之前被冲走,使得使用该库进行编程很难调试。
impl Drop for Terminal {
fn drop(&mut self) {
self.outbuffer.write_all(&self.driver.get(DevFn::ShowCursor)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::Reset)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::Clear)).unwrap();
self.outbuffer.write_all(&self.driver.get(DevFn::ExitCa)).unwrap();
self.flush().unwrap(); // If an exception occurs, this will reclear the screen and remove the output
self.termctl.reset().unwrap();
SIGWINCH_STATUS.store(false, Ordering::SeqCst);
RUSTTY_STATUS.store(false, Ordering::SeqCst);
}
}
如果我注释掉 self.flush().unwrap();
会打印异常,但是即使程序结束,终端也不会正确刷新屏幕并在终端上留下图形。
是否可以在程序开始时指定一个自定义缓冲区 panic 将用于写入?或者可能写一个 hacky 技巧来做到这一点?这样,在刷新之后我们可以检查这个缓冲区中是否有任何东西,如果是这样我们就知道发生了异常并可以将其打印出来。
运行一个故意算术溢出崩溃的程序,目前输出只有
然而,通过注释掉 self.flush().unwrap();
,我们看到了实际的异常,但现在是一个非常丑陋的终端。该解决方案将无法正常工作,因为正确执行的程序仍需要刷新,因为不需要显示任何错误
Panics 消息目前写入到 stderr,因此执行此操作的 hacky 方法是将 stderr 重定向到文件 (cargo run 2>/path/to/panic.log
)。
或者,您可以使用 gag 在您的程序本身中执行此操作(免责声明,我编写了这个库)。不幸的是,它不适用于 Windows.
以下将重定向 stderr,直到 stderr_redirect
被删除:
use std::fs::OpenOptions;
use gag::Redirect;
let log = OpenOptions::new()
.truncate(true)
.read(true)
.create(true)
.write(true)
.open("/path/to/panic.log")
.unwrap();
let stderr_redirect = Redirect::stderr(log).unwrap();
// Your code here...
您还可以通过执行以下操作在临时文件中缓冲 stderr:
use gag::BufferRedirect;
let mut stderr_buffer = BufferRedirect::stderr().unwrap();
您需要使用 std::panic::set_hook
that captures the output. You can then catch the panic with std::panic::catch_unwind
:
use std::{
panic,
sync::{Arc, Mutex},
};
fn main() {
let global_buffer = Arc::new(Mutex::new(String::new()));
let old_hook = panic::take_hook();
panic::set_hook({
let global_buffer = global_buffer.clone();
Box::new(move |info| {
let mut global_buffer = global_buffer.lock().unwrap();
if let Some(s) = info.payload().downcast_ref::<&str>() {
global_buffer.push_str(s);
}
})
});
let result = panic::catch_unwind(|| {
panic!("test panic");
});
panic::set_hook(old_hook);
match result {
Ok(res) => res,
Err(_) => {
println!("caught panic!");
println!("I captured:\n\n{}", global_buffer.lock().unwrap())
}
}
}
另请参阅: