How could I read Java Console Output into a String buffer

Ok, this was a fun problem. Dosen't seem to be an elegant way of solving it for all PrintStream methods at once. (Unfortunately there is no FilterPrintStream.)

I did write up an ugly reflection-based workaround though (not to be used in production code I suppose :)

class LoggedPrintStream extends PrintStream {

    final StringBuilder buf;
    final PrintStream underlying;

    LoggedPrintStream(StringBuilder sb, OutputStream os, PrintStream ul) {
        super(os);
        this.buf = sb;
        this.underlying = ul;
    }

    public static LoggedPrintStream create(PrintStream toLog) {
        try {
            final StringBuilder sb = new StringBuilder();
            Field f = FilterOutputStream.class.getDeclaredField("out");
            f.setAccessible(true);
            OutputStream psout = (OutputStream) f.get(toLog);
            return new LoggedPrintStream(sb, new FilterOutputStream(psout) {
                public void write(int b) throws IOException {
                    super.write(b);
                    sb.append((char) b);
                }
            }, toLog);
        } catch (NoSuchFieldException shouldNotHappen) {
        } catch (IllegalArgumentException shouldNotHappen) {
        } catch (IllegalAccessException shouldNotHappen) {
        }
        return null;
    }
}

...that can be used like this:

public class Test {
    public static void main(String[] args) {

        // Create logged PrintStreams
        LoggedPrintStream lpsOut = LoggedPrintStream.create(System.out);
        LoggedPrintStream lpsErr = LoggedPrintStream.create(System.err);

        // Set them to stdout / stderr
        System.setOut(lpsOut);
        System.setErr(lpsErr);

        // Print some stuff
        System.out.print("hello ");
        System.out.println(5);
        System.out.flush();

        System.err.println("Some error");
        System.err.flush();

        // Restore System.out / System.err
        System.setOut(lpsOut.underlying);
        System.setErr(lpsErr.underlying);

        // Print the logged output
        System.out.println("----- Log for System.out: -----\n" + lpsOut.buf);
        System.out.println("----- Log for System.err: -----\n" + lpsErr.buf);
    }
}

Resulting output:

hello 5
Some error
----- Log for System.out: -----
hello 5

----- Log for System.err: -----
Some error

(Note though, that the out field in FilterOutputStream is protected and documented, so it is part of the API :-)


You can use PipedInputStream and PipedOutputStream.

//create pairs of Piped input and output streasm for std out and std err
final PipedInputStream outPipedInputStream = new PipedInputStream();
final PrintStream outPrintStream = new PrintStream(new PipedOutputStream(
    outPipedInputStream));
final BufferedReader outReader = new BufferedReader(
    new InputStreamReader(outPipedInputStream));
final PipedInputStream errPipedInputStream = new PipedInputStream();
final PrintStream errPrintStream = new PrintStream(new PipedOutputStream(
    errPipedInputStream));
final BufferedReader errReader = new BufferedReader(
    new InputStreamReader(errPipedInputStream));
final PrintStream originalOutStream = System.out;
final PrintStream originalErrStream = System.err;
final Thread writingThread = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            System.setOut(outPrintStream);
            System.setErr(errPrintStream);
            // You could also set the System.in here using a
            // PipedInputStream
            DoSomething();
            // Even better would be to refactor DoSomething to accept
            // PrintStream objects as parameters to replace all uses of
            // System.out and System.err. DoSomething could also have 
            // an overload with DoSomething() calling: 
            DoSomething(outPrintStream, errPrintStream);
        } finally {
            // may also want to add a catch for exceptions but it is
            // essential to restore the original System output and error
            // streams since it can be very confusing to not be able to
            // find System.out output on your console
            System.setOut(originalOutStream);
            System.setErr(originalErrStream);
            //You must close the streams which will auto flush them
            outPrintStream.close();
            errPrintStream.close();
        }
    } // end run()
}); // end writing thread
//Start the code that will write into streams
writingThread.start();
String line;
final List<String> completeOutputStreamContent = new ArrayList<String>();
while ((line = outReader.readLine()) != null) {
    completeOutputStreamContent.add(line);
} // end reading output stream
final List<String> completeErrorStreamContent = new ArrayList<String>();
while ((line = errReader.readLine()) != null) {
    completeErrorStreamContent.add(line);
} // end reading output stream

You can't do that once the program is finished running. You need to do it before the program starts to write output.

See this article(archive.org) for details on how to replace stdout and stderr. The core calls are System.setOut() and System.setErr().

Tags:

Java

Console