к log4j перенаправить стандартный вывод в DailyRollingFileAppender


У меня есть java-приложение, которое использует 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

таким образом, все операторы журнала правильно добавляются в файл, но я теряю stdout и stderr. Как перенаправить трассировки стека исключений и sysouts в ежедневный свернутый файл ?

9 58

9 ответов:

// 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);
            }
        };
    }
}

в коде Скаффмана: чтобы удалить пустые строки в журналах log4j, просто добавьте метод "println" в PrintStream 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);
        }
    };
}

я взял идею от Майкла С., Но, как упоминалось в одном комментарии, у него есть некоторые проблемы: он не захватывает все, и он печатает некоторые пустые строки.

также я хотел отделить System.out и System.err, Так что System.out регистрируется с уровнем журнала 'INFO' и System.err войти с 'ERROR' (или 'WARN' если вам нравится).

так что это мое решение: Первый класс, который расширяет OutputStream (проще переопределить все методы OutputStream чем для PrintStream). Он регистрируется с указанным уровнем журнала, а также копирует все на другой OutputStream. А также он обнаруживает "пустые" строки (содержащие только пробелы) и не регистрирует их.

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);
}
}

и затем очень простой класс утилиты для установки out и 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)));
}

}

Если вы используете сервер приложений, сервлетов контейнер или что-то подобное, см. kgiannakakis это.

для автономных приложений см. этой. Вы можете reassing stdin,stdout и stderr используя java.ленг.Система класса. В основном вы создаете новый подкласс PrintStream и устанавливаете этот экземпляр в System.из.

что-то в этом роде в начале вашего приложения (непроверенный.)

// 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) {
       }
    }
}

// reassign standard output to go to log4j
System.setOut(new Log4jStream());

Я предполагаю, что вы регистрируете stacktraces через e.printStackTrace() ? Вы можете передать объект исключения в методы ведения журнала Log4j, и они появятся в вашем журнале (см. регистратор.ошибка (объект obj, Throwable t))

обратите внимание, что вы можете изменить систему.и системы.подстраховаться в другой PrintStream, который перенаправляет к log4j. Это будет простой изменить и спасти вам преобразовывать все ваши System.out.println() заявления.

ответы выше дают отличную идею использовать прокси для ведения журнала STDOUT/ERR. Однако приведенные примеры реализации не работают хорошо для всех случаев. Например, попробовать

стандартные потоки вывода и ошибок управляются из вашего контейнера. Например, Tomcat использует JULI для регистрации выходных данных и потоков ошибок.

моя рекомендация-оставить их как есть. Избегайте использования системы.из.печать в вашем приложении. Смотрите здесь для трассировки стека.

Ансер @Michael-это хороший момент. Но расширение PrintStream не очень приятно, потому что он использует внутренний метод void write(String) чтобы записать все вещи в выходной поток.

Я предпочитаю использовать LoggingOutputStream класс из пакета log4j Contrib.

затем я перенаправляю системные потоки следующим образом:

public class SysStreamsLogger {
    private static Logger sysOutLogger = Logger.getLogger("SYSOUT");
    private static Logger sysErrLogger = Logger.getLogger("SYSERR");

    public static final PrintStream sysout = System.out;
    public static final PrintStream syserr = System.err;

    public static void bindSystemStreams() {
        // Enable autoflush
        System.setOut(new PrintStream(new LoggingOutputStream(sysOutLogger, LogLevel.INFO), true));
        System.setErr(new PrintStream(new LoggingOutputStream(sysErrLogger, LogLevel.ERROR), true));
    }

    public static void unbindSystemStreams() {
        System.setOut(sysout);
        System.setErr(syserr);
    }
}

перед использованием системы.установка и система.метод setErr мы должны сбросить java.утиль.лесозаготовительный.Объект LogManager с помощью метода reset ().

public static void tieSystemOutAndErrToLog() {

    try{

        // initialize logging to go to rolling log file
        LogManager logManager = LogManager.getLogManager();
        logManager.reset();

        // and output on the original stdout
        System.out.println("Hello on old stdout");
        System.setOut(createLoggingProxy(System.out));
        System.setErr(createLoggingProxy(System.err));

        //Following is to make sure whether system.out and system.err is redirecting to the stdlog.log file
        System.out.println("Hello world!");

        try {
            throw new RuntimeException("Test");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }catch(Exception e){
        logger.error("Caught exception at StdOutErrLog ",e);
        e.printStackTrace();
    }
}