log4j перенаправляет stdout на DailyRollingFileAppender

У меня есть приложение Java, использующее log4j.

Конфиг:

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. Как перенаправить трассировки стека исключений и системные сообщения в файл с ежедневным обновлением?


person letronje    schedule 29.07.2009    source источник


Ответы (10)


В коде Скаффмана: чтобы удалить пустые строки в журналах 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);
        }
    };
}
person Fizou    schedule 11.09.2012

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

Также я хотел разделить 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)));
}

}
person Dario Seidl    schedule 20.09.2011
comment
Куда добавить путь к файлу журнала ?? - person Aniket; 19.09.2012
comment
Можете ли вы дать мне представление о том, как должен выглядеть файл log4j.properties или log4j.xml, чтобы это работало? - person Aniket; 20.09.2012
comment
@acoolguy См. Log4j XML Configuration Primer, используйте FileAppender. - person Dario Seidl; 22.09.2012
comment
Единственным недостатком этой реализации является то, что если у вас есть трассировка стека, напечатанная в StdErr, она будет печатать по одной строке за раз, например, 2014-12-22 19:55:04,581 [main] INFO {ERR} at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefiniti‌​ons(XmlBeanDefinitionReader.java:396) 2014-12-22 19:55:04,581 [main] INFO {ERR} at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinition‌​s(XmlBeanDefinitionReader.java:334) - person Mike Pone; 22.12.2014

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

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

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

// 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());
person Juha Syrjälä    schedule 29.07.2009

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

System.out.printf («Тестирование% s \ n», «ABC»);

Код из приведенных выше примеров разделит вывод на отдельные части на консоли и на несколько нечитаемых записей Log4j.

Решение состоит в том, чтобы буферизовать вывод до тех пор, пока триггер '\ n' не будет найден в конце буфера. Иногда буфер заканчивается на '\ r \ n'. Приведенный ниже класс решает эту проблему. Он полностью функциональный. Вызовите статический метод bind (), чтобы активировать его.

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

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

// Based on
// http://stackoverflow.com/questions/1200175/log4j-redirect-stdout-to-dailyrollingfileappender
public class Log4jStdOutErrProxy {

  public static void bind() {
    bind(Logger.getLogger("STDOUT"), Logger.getLogger("STDERR"));
  }

  @SuppressWarnings("resource")
  public static void bind(Logger loggerOut, Logger loggerErr) {
    System.setOut(new PrintStream(new LoggerStream(loggerOut, Level.INFO,  System.out), true));
    System.setErr(new PrintStream(new LoggerStream(loggerErr, Level.ERROR, System.err), true));
  }

  private static class LoggerStream extends OutputStream {
    private final Logger logger;
    private final Level logLevel;
    private final OutputStream outputStream;
    private StringBuilder sbBuffer;

    public LoggerStream(Logger logger, Level logLevel, OutputStream outputStream) {
      this.logger = logger;
      this.logLevel = logLevel;
      this.outputStream = outputStream;
      sbBuffer = new StringBuilder();
    }

    @Override
    public void write(byte[] b) throws IOException {
      doWrite(new String(b));
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
      doWrite(new String(b, off, len));
    }

    @Override
    public void write(int b) throws IOException {
      doWrite(String.valueOf((char)b));
    }

    private void doWrite(String str) throws IOException {
      sbBuffer.append(str);
      if (sbBuffer.charAt(sbBuffer.length() - 1) == '\n') {
        // The output is ready
        sbBuffer.setLength(sbBuffer.length() - 1); // remove '\n'
        if (sbBuffer.charAt(sbBuffer.length() - 1) == '\r') {
          sbBuffer.setLength(sbBuffer.length() - 1); // remove '\r'
        }
        String buf = sbBuffer.toString();
        sbBuffer.setLength(0);
        outputStream.write(buf.getBytes());
        outputStream.write('\n');
        logger.log(logLevel, buf);
      }
    }
  } // inner class LoggerStream  

}
person MarkG    schedule 14.01.2015

Для тех, кто ищет, как это сделать в log4j2. Теперь есть компонент для создания этих потоков для вас.

Требуется включение jar-файла log4j-iostreams
См .: https://logging.apache.org/log4j/2.x/log4j-iostreams/index.html

Пример:

PrintStream logger = IoBuilder.forLogger("System.out").setLevel(Level.DEBUG).buildPrintStream();
PrintStream errorLogger = IoBuilder.forLogger("System.err").setLevel(Level.ERROR).buildPrintStream();
System.setOut(logger);
System.setErr(errorLogger);
person Rowan    schedule 04.12.2019

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

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

person Brian Agnew    schedule 29.07.2009

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

Я рекомендую оставить все как есть. Избегайте использования System.out.print в вашем приложении. См. здесь для трассировки стека.

person kgiannakakis    schedule 29.07.2009
comment
Как насчет автономных приложений Java, которые не используют контейнер? Конечно, есть способ поймать stdout и записать его в log4j? - person Juha Syrjälä; 29.07.2009
comment
Как предложил Брайан Агнью, вы можете перенаправить поток на log4j. Однако я не рекомендую этого делать. Решение - не печатать на стандартном выходе, а использовать вместо этого log4j. Нет ни одной достойной библиотеки java, которая использовала бы стандартный вывод. Если у вас есть сторонний код, который делает это, попросите их вместо этого использовать log4j или общедоступное ведение журнала. Перенаправление стандартного вывода должно быть крайней мерой. - person kgiannakakis; 29.07.2009
comment
Не используйте System.out.println () для ведения журнала. Используйте log4j (или другую структуру ведения журнала) в своем коде и просто попросите конфигурацию отправить вывод на стандартный вывод. - person matt b; 29.07.2009
comment
о, мальчики ;-) по-видимому, все дело в том, чтобы третьи лица печатали в System.out - например, если вы включите log4j.debug, он будет печатать в System.out (см. также LogLog) - person Jörg; 05.04.2016

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

Я предпочитаю использовать LoggingOutputStream Class из пакета 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);
    }
}
person itshorty    schedule 18.06.2012

Перед использованием методов System.setOut и System.setErr мы должны сбросить объект java.util.logging.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();
    }
}
person Vinoth Sakthivadivel    schedule 07.05.2017
comment
Что такое createLoggingProxy? - person WindyFields; 23.08.2019

person    schedule
comment
это работает с исключениями, но не захватывает весь вывод, записанный в стандартный вывод. Например, если вы вызвали System.out.print (5); он будет обходить поток печати прокси. также, даже с исключениями, это приведет к печати лишних пустых строк в stderr, если вы выполните someException.printStackTrace () - person sbridges; 04.08.2011
comment
@sbridges вы можете просто переопределить другие методы, если вам нужно. Например, printStackTrace вызывает метод println(Object o), который вы также можете переопределить, чтобы удалить эти неприятные пустые строки. - person tysonjh; 18.09.2013
comment
Изучая ваш код, я узнал гораздо больше, чем просто ведение журнала. молодец! Спасибо. - person Lalith J.; 13.08.2014
comment
Работает ли это для всего экземпляра Tomcat или только для веб-приложения с этим классом? - person Dojo; 19.09.2014
comment
Если вы используете этот метод, я думаю, вам лучше всего реализовать все методы в PrintStream. Если вы хотите быть ленивым, вы можете использовать ответ Дарио, который реализует OutputStream. Просто обратите внимание на оговорку с этим методом, которую я отметил в комментариях. - person Mike Pone; 24.12.2014
comment
Перейдет ли это в бесконечный цикл, если возврат в исходное состояние указывает на стандартный выход? - person AlienOnEarth; 17.05.2016