import java.io.*;

/* Capturing standard output and error in a log file
 * <P>
 * This from the good folks at Sun,
 * the Java Developers Connection.
 * If you don't subscribe you should -- it's free.
 * Quoting (with my valuable additions) from here >>>>>>>
 * <P>
 * 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.
 * <P>
 * 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. */

/** SaveOutput extends PrintStream,
 * and is constructed on an existing PrintStream,
 * plus a second existing PrintStream,
 * and acts like a tee.
 * Any characters it receives are forwarded to two places:
 * the log stream and the underlying print stream.
 * <P>
 * The PrintStream class is deprecated vs PrintWriter,
 * therefore <B>ALL</B> constructors produce a deprecated notice!
 * But System still requires PrintStream for stdout and stderr,
 * so there is nothing we can do about it.
 * <P>
 * SaveOutput has two static methods - start () and stop ().
 * <UL>
 * <LI>
 * Calling start () creates a new log file,
 * or empties an existing log file.
 * It copies into the log file,
 * any characters printed to standard output and standard error.
 * The start () method saves the current standard output and
 * standard error print streams.
 * These print streams will be restored when stop () is called.
 * <LI>
 * Calling stop () restores the original standard output and standard error.
 * It then closes the log file.
 * </UL>
 * <P>
 * The underlying PrintStreams
 * are the original standard output and standard error;
 * these are supplied to the SaveOutput constructor.
 * <P>
 * The redirection applies to
 * <B>all</B> output sent to standard output and standard error,
 * regardless of what <CODE>".java"</CODE> compile unit it appears in.
 * <P>
 * 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 PrintStream wants to check if an error occurred,
 * it can call checkError.
 * <P>
 * Wish List: <UL>
 * <LI> ability to set debug levels, zero being off, to allow finer control
 * <LI> redirecting output to a file upon request;
 * <LI> implementing an ASSERT (or VALID) mechanism if possible;
 * <LI> minimizing the overhead when it is turned off.
 * <LI> have each object know how to dump itself to a supplied Debug object.
 * </UL>
 * <P>
 * Although the Sun example doesn't show further elaborations, <UL>
 * <LI> wrappers could provide debug levels and assertion triggers,
 * while any "deeper" prints would still use the tee mechanism,
 * <LI> could output to a message window instead of (or plus) a log file,
 * <LI> could support filtering of what lines make it to which places.
 * </UL>
 */

public class SaveOutput extends PrintStream {

/* Because these are static (or Class) variables,
 * they are "global" to all SaveOutput instances,
 * thus any SaveOutput.method () call has access to them.
 *
 * Because they are private,
 * only SaveOutput.method () calls have access to them. */

    private static OutputStream logStream;
    private static PrintStream oldStdout;
    private static PrintStream oldStderr;

/** Constructor.
 * Not meant to be called by application code,
 * but internally from SaveOutput.start ().  */

    SaveOutput (PrintStream ps) { super (ps); }

/**  Starts copying of stdout and stderr to the log.
 * @param logName the name of the log file.
 * Creates a new log file,
 * or empties an existing log file.
 * @exception IOException if unable to Create/Open logStream.
 */

    public static void start (String logName) throws IOException {

/* Create/Open the named log file,
 * as the "tee" destination. */

	start (new PrintStream (
	    new BufferedOutputStream (
	    new FileOutputStream (logName))));
    }

/**  Starts copying of stdout and stderr to the log.
 * Note that PrintStream (OutputStream could be ByteArrayOutputStream,
 * 
 * @param logStream the "tee" destination.
 */

    public static void start (PrintStream logPStream) {

/* Save old settings. */

	oldStdout = System.out;
	oldStderr = System.err;

/* save log handle in a private static (Class) variable. */

	logStream = logPStream;

/* Start redirecting the output by re-assigning,
 * both "stdout" and "stderr" to the "tee-objects"
 * that we create and wrap around the existing outputs.
 * They will also write to the logStream,
 * via private static logStream.
 * (FYI -- there is a System.setIn (InputStream) method too)
 *
 * Because "stdout" and "stderr" are static (or Class) variables,
 * any System.method () call has access to them,
 * in particular any System.out and System.err calls,
 * regardless of what ".java" compile unit it is appears in. */

	System.setOut (new SaveOutput (System.out));
	System.setErr (new SaveOutput (System.err));
    }

/**  Restores the original settings of stdout and stderr. */

    public static void stop () {
	System.setOut (oldStdout);
	System.setErr (oldStderr);
        try { logStream.close (); }
	catch (Exception e) { e.printStackTrace (); }
    }

/* Although both standard output and standard error are written into the
 * same logStream,
 * there is no need to synchronize this operation.
 * The reason is that the logStream output stream is itself a print stream,
 * and write operations are synchronized.
 * <P>
 * 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 logStream,
 * and then to the underlying print stream (by calling super.write ()).
 */

/**  PrintStream override for single byte. */

    public void write (int b) {
        try { logStream.write (b); }
	catch (Exception e) {
            e.printStackTrace ();
            setError ();
        }
	super.write (b);
    }

/**  PrintStream override for byte array. */

    public void write (byte buf[], int off, int len) {
        try { logStream.write (buf, off, len); }
	catch (Exception e) {
            e.printStackTrace ();
            setError ();
        }
	super.write (buf, off, len);
    }

}
/* <IMG SRC="/cgi-bin/counter">*/
