views:

5129

answers:

5

I have a java app that uses log4j.

Config:

log4j.rootLogger=info, file

log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.File=${user.home}/logs/app.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d [%t] %c %p %m%n

So all the log statements are correctly appended to the file, but i am losing stdout and stderr. How do i redirect exception stack traces and sysouts to the daily rolled file ?

A: 

I presume you're logging stacktraces via e.printStackTrace() ? You can pass an exception object into the Log4j logging methods and those will appear in your log (see Logger.error(Object obj, Throwable t))

Note that you can change System.out and System.err to another PrintStream that redirects to Log4j. That would be a straightforward change and save you converting all your System.out.println() statements.

Brian Agnew
A: 

Standard output and error streams are managed from your container. For example Tomcat uses JULI to log output and error streams.

My recommendation is to leave these as it is. Avoid using System.out.print in your application. See here for stack traces.

kgiannakakis
How about standalone java apps that do not use a container? Surely there is a way to catch stdout and log it to log4j?
Juha Syrjälä
As suggested by Brian Agnew you can redirect the stream to log4j. I however recommend against it. The solution is not to print at standard output, but use log4j instead. There isn't a single decent java library that uses standard output. If you have third party code that does this, ask them to use log4j or commons logging instead. Redirecting standard output should be the last resort.
kgiannakakis
Do not use System.out.println() for logging. Use log4j (or another logging framework) in your code, and simply have the config send the output to stdout.
matt b
+1  A: 

If you are using an application server, servlet container or something similar, see kgiannakakis's answer.

For standalone apps see this. You can reassing stdin, stdout and stderr using java.lang.System class. Basically you create a new subclass of PrintStream and set that instance to System.out.

Something along these lines in start of your app (untested).

// PrintStream object that prints stuff to log4j logger
public class Log4jStream extends PrintStream {
      public void write(byte buf[], int off, int len) {
        try {
           // write stuff to Log4J logger
        } catch (Exception e) {
       }
    }
}

// reassing standard output to go to log4j
System.setOut(new Log4jStream());
Juha Syrjälä
+6  A: 
// I set up a ConsoleAppender in Log4J to format Stdout/Stderr
log4j.rootLogger=DEBUG, CONSOLE
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%t] %-5p %c - %m%n


// And I call this StdOutErrLog.tieSystemOutAndErrToLog() on startup

public class StdOutErrLog {

    private static final Logger logger = Logger.getLogger(StdOutErrLog.class);

    public static void tieSystemOutAndErrToLog() {
        System.setOut(createLoggingProxy(System.out));
        System.setErr(createLoggingProxy(System.err));
    }

    public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
        return new PrintStream(realPrintStream) {
            public void print(final String string) {
                realPrintStream.print(string);
                logger.info(string);
            }
        };
    }
}
Michael S.
Very useful. Thanks for this class!
flash
You totally rock
rogerdpack
A: 

works like a charm :)

btw, is there any way to prevent prints to go to STDOUT? (while still writing to log4j)?

Eyal