Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

log4j redirect stdout to DailyRollingFileAppender

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 ?

like image 376
letronje Avatar asked Jul 29 '09 13:07

letronje


3 Answers

// 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);
            }
        };
    }
}
like image 141
Michael S. Avatar answered Nov 04 '22 18:11

Michael S.


I picked up the idea from Michael S., but like mentioned in one comment, it has some problems: it doesn't capture everything, and it prints some empty lines.

Also I wanted to separate System.out and System.err, so that System.out gets logged with log level 'INFO' and System.err gets logged with 'ERROR' (or 'WARN' if you like).

So this is my solution: First a class that extends OutputStream (it's easier to override all methods for OutputStream than for PrintStream). It logs with a specified log level and also copies everything to another OutputStream. And also it detects "empty" strings (containing whitespace only) and does not log them.

import java.io.IOException;
import java.io.OutputStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class LoggerStream extends OutputStream
{
private final Logger logger;
private final Level logLevel;
private final OutputStream outputStream;

public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream)
{
    super();

    this.logger = logger;
    this.logLevel = logLevel;
    this.outputStream = outputStream;
}

@Override
public void write(byte[] b) throws IOException
{
    outputStream.write(b);
    String string = new String(b);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}

@Override
public void write(byte[] b, int off, int len) throws IOException
{
    outputStream.write(b, off, len);
    String string = new String(b, off, len);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}

@Override
public void write(int b) throws IOException
{
    outputStream.write(b);
    String string = String.valueOf((char) b);
    if (!string.trim().isEmpty())
        logger.log(logLevel, string);
}
}

And then a very simple utility class to set out and err:

import java.io.PrintStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;

public class OutErrLogger
{
public static void setOutAndErrToLog()
{
    setOutToLog();
    setErrToLog();
}

public static void setOutToLog()
{
    System.setOut(new PrintStream(new LoggerStream(Logger.getLogger("out"), Level.INFO, System.out)));
}

public static void setErrToLog()
{
    System.setErr(new PrintStream(new LoggerStream(Logger.getLogger("err"), Level.ERROR, System.err)));
}

}
like image 13
Dario Seidl Avatar answered Nov 04 '22 20:11

Dario Seidl


In Skaffman code : To remove empty lines in log4j logs, just add "println" method to PrintStream of createLoggingProxy

public static PrintStream createLoggingProxy(final PrintStream realPrintStream) {
    return new PrintStream(realPrintStream) {
        public void print(final String string) {
            logger.warn(string);
        }
        public void println(final String string) {
            logger.warn(string);
        }
    };
}
like image 12
Fizou Avatar answered Nov 04 '22 18:11

Fizou