Перенаправить панику в указанный буфер

Есть какой-либо способ сделать это? В графической библиотеке терминала, если возникает исключение, исключение будет сброшено перед отображением, что затрудняет отладку программирования с использованием этой библиотеки.

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();, исключение было бы напечатано, однако терминал не смог бы правильно очистить экран и оставить графику на терминале даже после завершения программы.

Можно ли в начале программы указать настраиваемый буфер, который паника будет использовать для записи? Или, может быть, написать для этого хитрый трюк? Таким образом, после сброса мы можем проверить, есть ли что-нибудь внутри этого буфера, если это так, мы знаем, что произошло исключение, и можем его распечатать.


Запуск программы, которая намеренно дает сбой из-за арифметического переполнения, в настоящее время выводится только  введите описание изображения здесь

Однако, комментируя self.flush().unwrap();, мы встречаемся с фактическим исключением, но теперь очень уродливым терминалом. Это решение не будет работать, так как программа, которая выполняется правильно, все равно должна быть очищена, поскольку не требуется отображать ошибку.

введите здесь описание изображения


person Syntactic Fructose    schedule 15.10.2015    source источник
comment
Я сомневаюсь, что есть хороший способ сделать это, но, возможно, эта функция будет полезна: doc.rust-lang.org/std/rt/unwind/fn.panicking.html   -  person Adrian    schedule 15.10.2015
comment
@ Адриан, это нестабильная мысль; (. Спасибо, попробую   -  person Syntactic Fructose    schedule 15.10.2015
comment
Это не нестабильно: doc.rust-lang.org/std /thread/fn.panicking.html   -  person Adrian    schedule 15.10.2015


Ответы (2)


Сообщения о панике в настоящее время записываются в 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();
person Steven    schedule 15.10.2015
comment
Это прекрасно работает! Отличная библиотека, которая поможет избавиться от головной боли, связанной с перенаправлением stderr - person Syntactic Fructose; 28.10.2015

Вам необходимо зарегистрировать панику с помощью 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())
        }
    }
}

Смотрите также:

person Shepmaster    schedule 30.01.2019