处理多个流中的所有异常

时间:2021-04-14 00:03:49

I want my program exceptions to be sent to each of the following, preferably simultaneously:

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

  • the console which starts it (not necessarily)
  • 启动它的控制台(不一定)

  • a gui
  • a txt file.
  • 一个txt文件。

How can I achieve this?

我怎样才能做到这一点?

My attempts:

  • System.setErr(PrintStream err) will forward all exceptions to a new stream. I am not able to state more than one stream though.

    System.setErr(PrintStream err)将所有异常转发到新流。我不能说多个流。

  • Calling System.setErr(PrintStream err) on a manually written OutputStream:

    在手动编写的OutputStream上调用System.setErr(PrintStream err):

    "You can write your own stream class that forwards to multiple streams and call System.setOut on an instance of that class" – Jeffrey Bosboom

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

    I found a way to do this. It is very nasty though. It "collects" PrintStream's write-bytes, puts them in a puffer (500 ms timeout) and finally shows it to the user (Proceed):

    我找到了一种方法来做到这一点。但这非常讨厌。它“收集”PrintStream的写字节,将它们放入一个吹气(500毫秒超时),最后显示给用户(继续):

    /* 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;
        }
    }
    

    is this what I am expected to do?

    这是我应该做的吗?

  • Create new exception class which does it for me. Would probably work, but other exceptions than that (IO, FileNotFound, ...) will still be treated the old way

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

  • Instead of stating [method name] throws Exception I could enclose all of my code in try/catch-blocks, get the exception and forward it to a method of mine, like this:

    而不是声明[方法名称]抛出异常我可以将我的所有代码都包含在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.
    }
    

    this would probably also work, but I'd have to put everything into try/catch-blocks. This cannot be good programming style at all.

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

  • Using a logger:

    使用记录器:

    "use log4j and set up a method to write to GUI and also to log to stdout, and file" – Scary Wombat

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

    Loggers only help me printing my exceptions into desired streams, but they don't help me catching them, right?

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

    But you really should use a logging package for this -- even java.util.logging can do what you need – Jeffrey Bosboom

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

    I have to tell my logging package where and what to log. But this is exactly what I am searching for.

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

I now can, as user3159253 suggested, use Thread.UncaughtExceptionHandler to catch unhandled exceptions specifically.

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

What is the right way to handle all thrown exceptions the way I want them to? What else do I have to consider apart from Thread.UncaughtExceptionHandler and System.setErr()(see above)?

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

2 个解决方案

#1


5  

First you need get hold of all exception instances thrown from/within your thread (may be try/catch or Thread.UncoughtExceptionHandler or ThreadPoolExecutor.afterExecute(Runnable r, Throwable t)).

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

Then once you have the exception instance you can simply log it using log4j but configure Log4j appenders to send your exception messages to multiple destinations. You can use File, Console, JDBC, JMS etc types of appenders depending upon your requirement. Also it is best to wrap them with Async appender.

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

Refer - https://logging.apache.org/log4j/2.x/manual/appenders.html

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

About pushing the exception message to GUI, it can be implemented in various ways depending upon what tech stack your are using in your application. In our application we are storing the message events (only critical ones) in database which are then picked by event monitoring threads from server and then pushed back to GUI (JQuery, JavaScript) using http://cometd.org/documentation/cometd-java.

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

#2


2  

Creating an object that extends PrintStream should work. Whenever it receives a line, it can display it and log it as required. Alternatively, all exceptions can be caught and redirected into a method that receives an Exception as a parameter, and the method can take care of logging/displaying the exception, and terminating the program cleanly.

创建扩展PrintStream的对象应该可以工作。只要它收到一行,它就可以显示它并根据需要记录它。或者,可以捕获所有异常并将其重定向到接收Exception作为参数的方法,该方法可以负责记录/显示异常,并彻底终止程序。

#1


5  

First you need get hold of all exception instances thrown from/within your thread (may be try/catch or Thread.UncoughtExceptionHandler or ThreadPoolExecutor.afterExecute(Runnable r, Throwable t)).

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

Then once you have the exception instance you can simply log it using log4j but configure Log4j appenders to send your exception messages to multiple destinations. You can use File, Console, JDBC, JMS etc types of appenders depending upon your requirement. Also it is best to wrap them with Async appender.

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

Refer - https://logging.apache.org/log4j/2.x/manual/appenders.html

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

About pushing the exception message to GUI, it can be implemented in various ways depending upon what tech stack your are using in your application. In our application we are storing the message events (only critical ones) in database which are then picked by event monitoring threads from server and then pushed back to GUI (JQuery, JavaScript) using http://cometd.org/documentation/cometd-java.

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

#2


2  

Creating an object that extends PrintStream should work. Whenever it receives a line, it can display it and log it as required. Alternatively, all exceptions can be caught and redirected into a method that receives an Exception as a parameter, and the method can take care of logging/displaying the exception, and terminating the program cleanly.

创建扩展PrintStream的对象应该可以工作。只要它收到一行,它就可以显示它并根据需要记录它。或者,可以捕获所有异常并将其重定向到接收Exception作为参数的方法,该方法可以负责记录/显示异常,并彻底终止程序。