35

My application has many System.out.println() statements.

I want to catch messages from println and send them to the standard logger (Log4j, JUL etc).

How to do that ?

Ian Zhang
  • 11
  • 8
EK.
  • 2,890
  • 9
  • 36
  • 49
  • possible duplicate of [Removing access to System.out in java](http://stackoverflow.com/questions/1710914/removing-access-to-system-out-in-java) – Robert Munteanu Jul 12 '10 at 12:38
  • While there are crossovers in the answers for that possible dupe, I don't think it's close enough to merit a match. It's more asking for ways to block output altogether - this one is for capturing it elsewhere. – paxdiablo Jul 12 '10 at 13:30

6 Answers6

41

The System class has a setOut and setErr that can be used to change the output stream to, for example, a new PrintStream with a backing File or, in this case, probably another stream which uses your logging subsystem of choice.


Keep in mind you may well get yourself into trouble if you ever configure your logging library to output to standard output or error (of the infinite recursion type, possibly).

If that's the case, you may want to just go and replace your System.out.print-type statements with real logging calls.

falconepl
  • 398
  • 1
  • 3
  • 15
paxdiablo
  • 854,327
  • 234
  • 1,573
  • 1,953
  • To make your solution complete, I suggest you add instruction on how to revert to the normal standard output once one has intercepted everything required, ie System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out))); – Vic Seedoubleyew Apr 06 '16 at 16:38
  • @VicSeedoubleyew wouldn't it be better to store the original `PrintStream` and restore it afterward -- if something else is also redirecting `System.out`, this won't necessarily restore it correctly. – neuralmer Jun 21 '17 at 20:55
35

I had a similar need once. I needed to intercept the output of some 3rd party component and react on a error message. The concept looks like this:

private class Interceptor extends PrintStream
{
    public Interceptor(OutputStream out)
    {
        super(out, true);
    }
    @Override
    public void print(String s)
    {//do what ever you like
        super.print(s);
    }
}
public static void main(String[] args)
{
    PrintStream origOut = System.out;
    PrintStream interceptor = new Interceptor(origOut);
    System.setOut(interceptor);// just add the interceptor
}
Unai Vivi
  • 3,073
  • 3
  • 30
  • 46
Wizard of Kneup
  • 1,863
  • 1
  • 18
  • 35
9

The better solution is to go through and change all the println statements to use a proper logging library. What you're trying to do is a big hack.

Stefan Kendall
  • 66,414
  • 68
  • 253
  • 406
  • 1
    Maybe he is trying to redirect System.out to a Logger ;-) – Riduidel Jul 12 '10 at 12:30
  • 1
    +1: the whole *point* of a logging framework is to be able to abstract where you are recording the output, you should call its logging methods as your "definitive" output from the code. Not to mention, redirecting `System.out` to call the logger *appears* to work; right up to the point where one day you configure the logger to output to stdout, and **bam** - stack overflow. – Andrzej Doyle Jul 12 '10 at 12:30
  • And this is not *that* difficult. +1 – Nivas Jul 12 '10 at 12:37
  • 3
    I generally agree, but as mentioned in my answer above, I needed to intercept a 3rd party System.out message. No other way. Therefore, there is a use case for this. There is even a use case for GOTO :-) – Wizard of Kneup Feb 23 '14 at 10:57
6

Here is how to capture prints to System.out, and then put things back in order :

// Start capturing
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
System.setOut(new PrintStream(buffer));

// Run what is supposed to output something
...

// Stop capturing
System.setOut(new PrintStream(new FileOutputStream(FileDescriptor.out)));

// Use captured content
String content = buffer.toString();
buffer.reset();
Vic Seedoubleyew
  • 9,888
  • 6
  • 55
  • 76
3

extending PrintStream is a bad solution as you will have to override all print() and println() methods. Instead, you can capture the stream:

public class ConsoleInterceptor {

    public interface Block {
        void call() throws Exception;
    }

    public static String copyOut(Block block) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        PrintStream printStream = new PrintStream(bos, true);
        PrintStream oldStream = System.out;
        System.setOut(printStream);
        try {
            block.call();
        }
        finally {
            System.setOut(oldStream);
        }
        return bos.toString();
    }
}

Now you can capture it like this:

   String result = ConsoleInterceptor.copyOut(() ->{
        System.out.print("hello world");
        System.out.print('!');
        System.out.println();
        System.out.println("foobar");
    });
    assertEquals("hello world!\nfoobar\n", result);
sergeych
  • 791
  • 7
  • 11
-1

I applied the base idea of this and it workes fine. No need to change all the System.out.### and System.err.### stuff.

import java.io.OutputStream;
import java.io.PrintStream;   

public class Interceptor extends PrintStream
{
  /** the logger */
  private Logger log;
  /** the origin output stream */
  PrintStream orig;

  /**
   * Initializes a new instance of the class Interceptor.
   *
   * @param out the output stream to be assigned
   * @param log the logger
   */
  public Interceptor( OutputStream out, Logger log )
  {
    super( out, true );
    this.log = log;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void finalize() throws Throwable
  {
    detachOut();
    super.finalize();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void print( String s )
  {
    //do what ever you like
    orig.print( s );
    log.logO( s, true );
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public void println( String s )
  {
    print( s + Defines.LF_GEN );
  }

  /**
   * Attaches System.out to interceptor.
   */
  public void attachOut()
  {
    orig  = System.out;
    System.setOut( this );
  }

  /**
   * Attaches System.err to interceptor.
   */
  public void attachErr()
  {
    orig = System.err;
    System.setErr( this );
  }

  /**
   * Detaches System.out.
   */
  public void detachOut()
  {
    if( null != orig )
    {
      System.setOut( orig );
    }
  }

  /**
   * Detaches System.err.
   */
  public void detachErr()
  {
    if( null != orig )
    {
      System.setErr( orig );
    }
  }
}


public class InterceptionManager
{
  /** out */
  private Interceptor out;

  /** err */
  private Interceptor err;

  /** log  */
  private Logger log;

  /**
   * Initializes a new instance of the class InterceptionManager.
   *
   * @param logFileName the log file name
   * @param append the append flag
   */
  public InterceptionManager( String logFileName, boolean append )
  {
    log = new Logger();
    log.setLogFile( logFileName, append );
    this.out = new Interceptor( System.out, log );
    this.out.attachOut();
    this.err = new Interceptor( System.err, log );
    this.err.attachErr();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void finalize() throws Throwable
  {
    if( null != log )
    {
      log.closeLogFile();
    }
    super.finalize();
  }
}

This view lines will enable logging without further code changes:

  if( writeLog )
  {
    logFileName = this.getClassName() + "_Log.txt";
    icMan = new InterceptionManager( logFileName, false );
    System.out.format( "Logging to '%s'\n", logFileName );
  }
froes
  • 11
  • 3