【问题标题】:Redirect panics to a specified buffer将恐慌重定向到指定的缓冲区
【发布时间】:2015-10-15 14:33:13
【问题描述】:

有没有办法做到这一点?在终端图形库中,如果发生异常,异常会在显示之前被刷新掉,使用该库很难进行编程调试。

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();,则会打印异常,但是即使程序结束,终端也不会正确刷新屏幕并在终端上留下图形。

是否可以在程序开始时指定用于写入的自定义缓冲区恐慌?或者可能写一个hacky技巧来做到这一点?这样,在刷新之后我们可以检查这个缓冲区内是否有任何东西,如果有,我们就知道发生了异常并可以打印出来。


运行一个故意因算术溢出而崩溃的程序,目前的输出只有

然而,通过注释掉self.flush().unwrap();,我们看到了实际的异常,但现在是一个非常丑陋的终端。此解决方案将不起作用,因为正确执行的程序仍需要刷新,因为不需要显示错误

【问题讨论】:

标签: rust panic


【解决方案1】:

紧急消息当前写入 stderr,因此执行此操作的黑客方法是将 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();

【讨论】:

  • 这很完美!很棒的库来帮助抽象标准错误重定向的头痛
【解决方案2】:

您需要使用std::panic::set_hook 注册一个恐慌挂钩,以捕获输出。然后你可以通过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())
        }
    }
}

另见:

【讨论】:

    猜你喜欢
    • 2015-09-14
    • 1970-01-01
    • 2015-11-10
    • 2013-10-19
    • 2011-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多