Errors not printing using custom PrintStream even after setting System.setErr()

Viewed 144

Basically, I wanted console to do 2 things:

  1. I wanted console to color code errors and general info messages (which errors being red and everything else green).
  2. I wanted to save all console messages to a log file.

So, I created a print stream that looked something like this:

public static class GeneralStream extends PrintStream {

    public GeneralStream(OutputStream out) {
        super(out);
    }

    @Override
    public void println(String string) {
        String time = DateTimeFormatter.ofPattern("HH:mm:ss").format(LocalDateTime.now());

        String output = "["+time+"] ["+type.n()+"] "+string;
        Logs.logToFile(output);
        
        String coloredOutput = ANSI_RESET+ANSI_WHITE+"["+ANSI_CYAN+time+ANSI_WHITE+"] "+
                ANSI_WHITE+"["+ANSI_RESET+type.c()+type.n()+ANSI_WHITE+"] "+type.c()+string+ANSI_RESET;
        super.println(coloredOutput);
    }
}

Great. Then I set this print stream at the start of my program as the default PrintStream using:

// Set console output settings
    System.setOut(new Console.GeneralStream(System.out));
    System.setErr(new Console.GeneraStream(System.err));

Awesome. Finally, upon doing System.out.println("Hello World"), I get the result I expected. My messages are colored. They are logged to a file. Great! In fact even if I do System.err.println("Error!"), I still get a result as expected. However, "automatic" exceptions do not print through the System.err that I set.

Here is an example:

// Set console output settings
    System.setOut(new Console.GeneralStream(System.out));
    System.setErr(new Console.ErrorStream(System.err));

    System.out.println("Hello world!");
    System.err.println("Printing an error!");

    // Real exception (NPE)
    Integer nullInteger = null;
    System.out.println(nullInteger.toString()); // won't print and will produce a NullPointException

Here is the result: As you can see, my System.out and System.err print fine but as soon as there is a real exception, it prints regularly.

So my question is how can I set a custom PrintStream for errors like this so they are logged to a file (and preferably follow my custom message formating).

1 Answers

If you dig into how the Throwable class prints the stack trace, you'll see it uses the println(Object) method, so you'll need to add this method to your custom ErrorStream class:

@Override
public void println(Object object) {
    println(String.valueOf(object));
}

Even then, you may want to change the "uncaught exception handler" to change how it logs exceptions. It seems that the default handler calls first System.err.print to output Exception in thread "{ThreadName}" followed by Throwable.printStackTrace, so you end up with the time stamp and other stuff in the middle of the message. For example:

    Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
        System.err.println("Uncaught exception in thread " + thread.getName());
        throwable.printStackTrace(System.err);
    });
Related