Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

CAPTURING STANDARD OUTPUT IN
A LOG FILE

This tip demonstrates how you can record in a log file everything you
print to standard out and standard error. This is especially useful 
if you deploy an application and your users encounter problems. You 
can have the users send you the log file for analysis.
The following example code demonstrates how to capture standard output. You can include the example code, as is, in your program. The example implements a class called SaveOutput, with two static methods - start() and stop(). Calling start() creates a new log file or empties an existing log file. It copies into the log file characters printed to standard output and standard error. Calling stop() closes the log file and restores the behavior of standard output and standard error (that is, their behavior before start() was called). import java.io.*; class Stdout { public static void main(String[] args) { try { // Start capturing characters into the log file. SaveOutput.start("log.txt"); // Test it. System.out.println("Here's is some stuff to stdout."); System.err.println("Here's is some stuff to stderr."); System.out.println("Let's throw an exception..."); new Exception().printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { // Stop capturing characters into the log file // and restore old setup. SaveOutput.stop(); } } } class SaveOutput extends PrintStream { static OutputStream logfile; static PrintStream oldStdout; static PrintStream oldStderr; SaveOutput(PrintStream ps) { super(ps); } // Starts copying stdout and stderr to the file f. public static void start(String f) throws IOException { // Save old settings. oldStdout = System.out; oldStderr = System.err; // Create/Open logfile. logfile = new PrintStream( new BufferedOutputStream( new FileOutputStream(f))); // Start redirecting the output. System.setOut(new SaveOutput(System.out)); System.setErr(new SaveOutput(System.err)); } // Restores the original settings. public static void stop() { System.setOut(oldStdout); System.setErr(oldStderr); try { logfile.close(); } catch (Exception e) { e.printStackTrace(); } } // PrintStream override. public void write(int b) { try { logfile.write(b); } catch (Exception e) { e.printStackTrace(); setError(); } super.write(b); }
// PrintStream override. public void write(byte buf[], int off, int len) { try { logfile.write(buf, off, len); } catch (Exception e) { e.printStackTrace(); setError(); } super.write(buf, off, len); } } The start() method first saves the current standard output and standard error print streams. These print streams will be restored when stop() is called. Next, the log file is opened. If the log file does not exist, it's created. Otherwise, the log file is emptied. Finally, System.setOut() and System.setErr() are called to replace the standard output and standard error print streams with SaveOutput print streams. The stop () method restores the original standard output and standard error. It then closes the log file. A SaveOutput object is a PrintStream object that acts like a tee. Any characters it receives are forwarded to two places: the log file and the underlying print streams. The underlying print streams in the example are the original standard output and standard error print streams; these are supplied to the SaveOutput constructor. Although both standard output and standard error are written into the same logfile, there is no need to synchronize this operation. The reason is that the logfile output stream is itself a print stream and write operations are synchronized. To implement this tee behavior, the program needs to override the two forms of the write method. These overrides simply write the characters into the logfile and then to the underlying print stream (by calling super.write()). The write() methods do not throw exceptions. Instead, they set a flag in the print stream if some problem occurs. They set the flag by calling setError(). If the client of the print stream wants to check if an error occurred, it can call checkError().

Any comments? email to:
richard@dreamscity.net