处理多个流中的所有异常

phi*_*294 7 java stdout exception throw stderr

我希望我的程序例外发送到以下每个,最好是同时发送:

  • 启动它的控制台(不一定)
  • 一个gui
  • 一个txt文件.

我怎样才能做到这一点?

我的尝试:

  • System.setErr(PrintStream err)将所有例外转发给新流.我不能说多个流.

  • 打电话System.setErr(PrintStream err)给人工书面OutputStream:

    "你可以编写自己的流类转发到多个流,并在该类的实例上调用System.setOut" - Jeffrey Bosboom

    我找到了一种方法来做到这一点.但这非常讨厌.它"收集" PrintStream's write-bytes,将它们放入一个吹气(500毫秒超时),最后显示给用户(Proceed):

    /* ErrorOutput.java */
    public static t_ErrBuffer t_activeErrBuffer = new t_ErrBuffer("");
    public static void setStdErrToFile(final File file) {
        ps = new PrintStream(fos) {
            @Override
            public void write(byte[] buf, int off, int len) {
                byte[] bn = new byte[len];
                for (int i = off, j = 0; i < (len + off); i++, j++) {
                    bn[j] = buf[i];
                }
                String msg = null;
                try {
                    msg = new String(bn, "UTF-8");
                } catch (UnsupportedEncodingException e1) {}
                if (msg.matches("[\\w\\W]*[\\w]+[\\w\\W]*")) { // ^= contains at least one word character
                    if( ! t_activeErrBuffer.isAlive() ) {
                        t_activeErrBuffer = new t_ErrBuffer(msg);
                        t_activeErrBuffer.start();
                    } else {
                        t_activeErrBuffer.interrupt();
                        t_activeErrBuffer = new t_ErrBuffer(t_activeErrBuffer.getErrBuffer() + "\n" + msg); // ^= append to buffer and restart.
                        t_activeErrBuffer.start();
                    }
                }
            }
        };
        System.setErr(ps);
    }
    
    /* t_ErrBuffer.java */
    public class t_ErrBuffer extends Thread {
        private String  errBuffer;
        public t_ErrBuffer(String buffer) {
            this.errBuffer = buffer;
        }
        protected class Proceed implements Runnable {
            public String msg = null;
            public Proceed(String msg) {
                this.msg = msg;
            }
            @Override
            public void run() {
                // todo PRINT ERROR MESSAGE: DO THINGS WITH msg: console, gui, JOptionPane
            }
        }
        @Override
        public void run() {
            try {
                Thread.sleep(500); // collect error lines before output. Needed because PrintStream's "write"-method writes ErrorMessages in multiple pieces (lines)
                // each time some new exception line comes in, the thread is stopped, buffer is being appended and thread new started
            } catch (InterruptedException e) {
                return; // stop
            }
            // after 500 ms of wait, no new error message line has come in. Print the message out:
            Thread t_tmp = new Thread(new Proceed("\n" + this.errBuffer));
            t_tmp.start();
            return;
        }
        public String getErrBuffer() {
            return this.errBuffer;
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这是我应该做的吗?

  • 创建新的异常类,它为我做.可能会工作,但除此之外的其他例外(IO,FileNotFound,...)仍然会以旧的方式处理

  • 而不是声明[method name] throws Exception我可以将所有代码都包含在try/catch-blocks中,获取异常并将其转发给我的方法,如下所示:

    /* AnyMethod.java */
    // ...
    try {
        // ... do everything here
    } catch (IOException | FileNotFoundException e) {   // as many as you like
        ErrorOutput.crash(e);
    }
    // ...
    
    /* ErrorOutput.java */
    public static void crash(Exception e) {
        FileOutputStream fos_errOutput = new FileOutputStream(new File("ErrorOutput.txt"), true);
    
        // 1st
        if (!System.out.equals(fos_errOutput)) {
            System.out.println(e.getMessage() + " :");  // to console or the preferred StdOut
            e.printStackTrace();
        }       
    
        // 2nd
        JOptionPane.showMessageDialog(Gui.frame, "THE PROGRAM HAS CRASHED!" + "\n\n" + e.getMessage() + "\n\nFor a more detailed report, see ErrorLog.txt");    // gui output
    
        // 3rd
        PrintStream ps = new PrintStream(fos_errOutput);
        ps.print(new Date().toString() + ":");  // write to file
        e.printStackTrace(ps);
        ps.close();
    
        // 4th
        System.exit(0); // this could also be "throw new Exception" etc., but I don't know why one should do that.
    }
    
    Run Code Online (Sandbox Code Playgroud)

    这可能也会奏效,但我必须把所有东西放到try/catch-blocks中.这根本不是好的编程风格.

  • 使用记录器:

    "使用log4j并设置一个方法来写入GUI并记录到stdout和文件" - Scary Wombat

    记录器只能帮助我将异常打印到所需的流中,但它们无法帮助我捕获它们,对吧?

    但你真的应该使用一个日志包 - 甚至java.util.logging可以做你需要的 - Jeffrey Bosboom

    我必须告诉我的日志包在哪里以及记录什么.但这正是我所寻找的.

我现在可以像user3159253建议的那样,Thread.UncaughtExceptionHandler专门用于捕获未处理的异常.

以我希望的方式处理所有抛出异常的正确方法是什么?除了Thread.UncaughtExceptionHandlerSystem.setErr()(见上文)我还需要考虑什么?

hem*_*900 5

首先,您需要获取从线程中/内部抛出的所有异常实例(可能是try/catch或Thread.UncoughtExceptionHandleror ThreadPoolExecutor.afterExecute(Runnable r, Throwable t)).

然后,一旦有了异常实例,您只需使用log4j进行日志记录,但可以配置Log4j appender将异常消息发送到多个目标.您可以根据需要使用文件,控制台,JDBC,JMS等类型的appender.最好用Async appender包装它们.

请参阅 - https://logging.apache.org/log4j/2.x/manual/appenders.html

关于将异常消息推送到GUI,它可以通过各种方式实现,具体取决于您在应用程序中使用的技术堆栈.在我们的应用程序中,我们将消息事件(仅限关键事件)存储在数据库中,然后由服务器的事件监视线程选择,然后使用http://cometd.org/documentation/cometd-将其推送回GUI(JQuery,JavaScript).java.