Copy STDOUT to file without stopping it showing on

2019-01-19 23:27发布

问题:

The program I am making is designed to be run unattended, because of this I have redirected the stdout and stderr streams to a log file. While this works without any problems, while I am still making and debugging the software I would like it to show on the screen as well. Is this possible?

To redirect the streams I have used

System.setErr(logWriter);
System.setOut(logWriter);

Thanks.

回答1:

a bit crude perhaps, but you could try this:

private static final isDebugMode = true;

...

if (!isDebugMode) {
  System.setErr(logWriter);
  System.setOut(logWriter);
} 

Alternatively you could write your own PrintStream implementation which simultaneously writes to both your log file and the screen. It doesn't sound like you need this behaviour except in development though so the latter whilst actually a more accurate answer to your question is probably not what you actually want.



回答2:

Yes. Logging frameworks (i.e. log4j) are best, isDebugMode is handy in a development environment but if you really need to "tee" your stdout you can like so.

import java.io.PrintStream;
import java.io.File;
public class TeeStream extends PrintStream {
    PrintStream out;
    public TeeStream(PrintStream out1, PrintStream out2) {
        super(out1);
        this.out = out2;
    }
    public void write(byte buf[], int off, int len) {
        try {
            super.write(buf, off, len);
            out.write(buf, off, len);
        } catch (Exception e) {
        }
    }
    public void flush() {
        super.flush();
        out.flush();
    }
}

http://www.exampledepot.com/egs/java.lang/Redirect.html

// import java.io.FileOutputStream;
String dateString = new SimpleDateFormat("yyyyMMdd").format(new Date());
File logFile = new File("mylogfile_" + dateString +".log");
PrintStream logOut = new PrintStream(new FileOutputStream(logFile, true));

PrintStream teeStdOut = new TeeStream(System.out, logOut);
PrintStream teeStdErr = new TeeStream(System.err, logOut);

System.setOut(teeStdOut);
System.setErr(teeStdErr);

You'll soon find LOG.somelevel(msg) much more manageable than System.out.println(msg). It is great to raise the log level when things are working well and lower the level when they aren't without deploying a debug build.



回答3:

If you're on a Unix-like platform (anything except Windows) you can use the tee program:

java myprogram | tee output

This will write the standard output to the console as well as the file called output.



回答4:

No, with your setup this is not possible. Possible solutions:

  • Use some software to monitor the log file. On Linux/Unix, less has a "follow"-mode (Shift-F), which tracks the log file. Other systems should have similar code. This has the advantage of using the same setup in debugging & production.
  • You should really consider using a proper logging framework (java.util.logging, Log4j or similar). This makes your logging setup much more flexible. Among many advantages, you can flexibly configure (without code changes), where your logs should go.


回答5:

If you're on UNIX you could use tail -f logfile to see lines as they are written to your logfile



回答6:

If you don't want a proper logging framework

Standard Unix way is to redirect the output streams

ie run the program exex as exe > log will redirect stdout to log but exe on its own will leave the output to the console.

If you want to do this in the program you would need a variable to test on and only redirect the streams if not testing



回答7:

On both Linux and Windows (with cygwin installed) I always use log4j to log to a file and then use "tail" to display it. By default, tail -f (and less -F) update every second which I find too slow. Also, there are often several interesting log files that are worth looking at, and some of them include the date as part of their name. Here's the command I use on one of my systems:

( cd /var/log/myapp/; tail -Fq --lines=0 -s 0.05 $(find . -type f -name "*$(date '+%Y-%m-%d').log" ) ) &

This simultaneously tails each log file under /var/log/myapp/ which contains today's date in the file name. Very handy with log4j rolling log files. And -s 0.05 means only pause 0.05 seconds between checks for new output.