views:

1870

answers:

5

I have a library that writes to stdout in Java. I would like to capture this output in a log using log4j. (I didn't write this library, so I have no control over the code inside the library).

Is there an easy way to do this? Is System.setOut the right approach? What do I pass to System.setOut?

Also, how would you do this in .NET/C#?

A: 

Why would you want to do that. You shouldn't write to sysout directly, you should write to log4j and configure console appender if you want output to sysout.

Dev er dev
I didn't write this library, I'm just using it, and I would like to capture the output it's producing.
Andy White
A: 

You should be able to hookup the GUI app chainsaw to listed to the stdout and get the results you need.

Dillie-O
+2  A: 

One solution is to subclass OutputStream. You only have to implement write( int b ). Then create a PrintStream with your OutputStream in the constructor.

It could look like :

public class L4JOS extends OutputStream {
  logger = Logger.getLogger( "std.out" );
  private int lineEnd = (int)'\n';
  private ByteArrayOutputStream baos = new ByteArrayOutputStream();

  public void write( int b ) throws IOException {
    baos.write( b );
    if ( b == lineEnd ) {
      logger.info( baos.toString() );
      baos.reset();
    }
  }

}

If it is multi threaded then you have more work. And make sure that the appender does not go to System.out.

Clint
+2  A: 

You could implement your own OutputStream which just logs whatever it gets out to log4j and then user System.setOut to an instance of that OutputStream.

Be aware that this might cause an infinite loop in the case of log4j outputing stuff on standard out as well.

So you probably should do some filtering to determine what to pass to log4j and what to pass on to the original stdout stream

Jens Schauder
A: 

I would not like to use setOut() because it's a global variable. You'll run into "action at a distance": you want to intercept stdout just for this library, but not to trample on anything else that might need to use stdout.

If at all possible, run the code that calls the library functions in a separate process space; then you can write a thread that reads stdout from that process and send to a logger.

new Thread() {
  public void run() {
    ProcessBuilder pb = new ProcessBuilder(command);
    Process p = pb.start();
    BufferedReader in = new BufferedReader(p.getInputStream());
    String line;
    while ((line = in.readLine()) != null) {
      log.info(line);
      // or you could parse line and try to dynamically pick a log level
    }
  }
}

You'll need a good way to get output from the called process back to your caller, but if it's as simple as returning a value, then it ought to be easy to return that as the last line of your stdout, or a line with a special signal code, or something.

skiphoppy