Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Processing all exceptions in multiple streams

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.

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.

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

    "You can write your own stream class that forwards to multiple streams and call System.setOut on an instance of that class" – 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):

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

  • 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:

    /* 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.

  • Using a logger:

    "use log4j and set up a method to write to GUI and also to log to stdout, and file" – 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

    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.

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)?

like image 521
phil294 Avatar asked Dec 12 '14 00:12

phil294


People also ask

Can you handle exception in Stream?

From a stream processing, we can throw a RuntimeException. It is meant to be used if there is a real problem, the stream processing is stopped; Or if we don't want to stop the whole processing, we only need to throw a caught Exception. Then it has to be handled within the stream.

Can a function throw multiple exceptions?

You can have the possibility of throwing multiple different exceptions. For example: if (obj == null) throw new NullPointerException(); if (some other case) throw new IllegalArgumentException(); if (this == this) throw new IOException();


2 Answers

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)).

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.

Refer - 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.

like image 183
hemant1900 Avatar answered Oct 07 '22 22:10

hemant1900


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.

like image 42
Daniel M. Avatar answered Oct 07 '22 23:10

Daniel M.