You want anything written to a
stream, such as the standard output
System.out
or the standard error
System.err
, to appear there but
also be logged into a file.
Subclass PrintStream
and have its write( )
methods write to two streams. Then use
system.setErr( )
or setOut( )
as in Section 9.7 to replace the existing standard
stream with this “tee” PrintStream
subclass.
Classes are meant to be subclassed. Here we’re just subclassing
PrintStream
and adding a bit of
functionality: a second PrintStream
! I wrote a
class called TeePrintStream
, named after the
ancient
Unix command tee.
That command allowed you to duplicate, or “tee off,” a
copy of the data being written on a
"pipeline” between two
programs.
The original Unix tee command is used like this:
the
|
character creates
a pipeline in which the standard output of one program becomes the
standard input to the next. This often-used example of pipes shows
how many users are logged into a Unix server:
who | wc -l
This runs the who program (which lists who is
logged into the system, one name per line along with the terminal
port and login time) with its output, instead of going to the
terminal, going into the standard input of the word count
(wc) program. Here wc is
being asked to count lines, not words; hence the
-l
option. To tee a copy of
the intermediate data into a file, you might say:
who | tee wholist | wc -l
which creates a file wholist
containing the
data. For the curious, the file wholist
might
look something like this:
ian ttyC0 Mar 14 09:59 ben ttyC3 Mar 14 10:23 ian ttyp4 Mar 14 13:46 (daroad.darwinsys.com)
So the previous commands would both print 3
as
their output.
TeePrintStream
is an attempt to capture the spirit
of the tee command. It can be used like this:
System.setErr(new TeePrintStream(System.err, "err.log")); // ...lots of code that occasionally writes to System.err... Or might.
System.setErr( )
is a means of specifying the
destination of text printed to System.err
(there
are also System.setOut( )
and
System.setIn( )
). This code results in any
messages that printed to System.err
to print both
to wherever System.err
was previously directed
(normally the terminal, but possibly a text window in an IDE) and
into the file err.log
.
This technique is not limited to the three standard streams. A
TeePrintStream
can be passed to any method that
wants a PrintStream
. Or, for that matter, an
OutputStream
. And you can adapt the technique for
BufferedInputStreams
,
PrintWriters
, BufferedReaders
,
and so on.
Since TeePrintStream
is fairly simple, I’ll
list the main parts of it here (see the online source for the
complete version):
import java.io.*; public class TeePrintStream extends PrintStream { protected PrintStream parent; protected String fileName; /* Construct a TeePrintStream given an existing Stream and a filename. */ public TeePrintStream(PrintStream os, String fn) throws IOException { this(os, fn, false); } /* Construct a TeePrintStream given an existing Stream, a filename, * and a boolean to control the flush operation. */ public TeePrintStream(PrintStream orig, String fn, boolean flush) throws IOException { super(new FileOutputStream(fn), flush); fileName = fn; parent = orig; } /** Return true if either stream has an error. */ public boolean checkError( ) { return parent.checkError() || super.checkError( ); } /** override write( ). This is the actual "tee" operation! */ public void write(int x) { parent.write(x); // "write once; super.write(x); // write somewhere else" } /** override write( ) */ public void write(byte[] x, int o, int l) { parent.write(x, o, l); super.write(x, o, l); } /** Close both streams. */ public void close( ) { parent.close( ); super.close( ); } }
It’s worth mentioning that I do not need
to override all the polymorphic forms of print()
and println()
. Since these all ultimately use one
of the forms of write( )
, if you override the
print
/println
methods to do the
tee-ing as well, you can get several additional
copies
of the data written out.
3.140.195.225