Java log4j 将标准输出重定向到 DailyRollingFileAppender

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1200175/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:50:19  来源:igfitidea点击:

log4j redirect stdout to DailyRollingFileAppender

javafileredirectlog4jstdout

提问by letronje

I have a java app that uses log4j.

我有一个使用 log4j 的 java 应用程序。

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 ?

所以所有日志语句都正确附加到文件中,但我丢失了标准输出和标准错误。我如何将异常堆栈跟踪和系统输出重定向到每日滚动文件?

采纳答案by Michael S.

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

回答by Brian Agnew

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

我想你是通过e.printStackTrace()? 您可以将异常对象传递到 Log4j 日志记录方法中,这些对象将出现在您的日志中(请参阅Logger.error(Object obj, Throwable t)

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

请注意,您可以将 System.out 和 System.err 更改为另一个重定向到 Log4j 的PrintStream。这将是一个简单的更改,并节省您转换所有System.out.println()语句。

回答by kgiannakakis

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

标准输出和错误流由您的容器管理。例如,Tomcat 使用 JULI 来记录输出和错误流。

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

我的建议是保持原样。避免在您的应用程序中使用 System.out.print。有关堆栈跟踪,请参见此处

回答by Juha Syrj?l?

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

如果您使用的是应用程序服务器、servlet 容器或类似的东西,请参阅kgiannakakis 的回答

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

对于独立的应用程序,请参阅。您可以使用java.lang.System类重新评估stdinstdoutstderr。基本上,您创建 PrintStream 的一个新子类并将该实例设置为 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) {
       }
    }
}

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

回答by Dario Seidl

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.

我从 Michael S. 那里得到了这个想法,但就像在一条评论中提到的那样,它有一些问题:它没有捕获所有内容,并且会打印一些空行。

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

此外,我想单独System.outSystem.err,使System.out获取与日志记录级别'INFO'System.err获取与登录'ERROR'(或者'WARN',如果你喜欢)。

So this is my solution: First a class that extends OutputStream(it's easier to override all methods for OutputStreamthan 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.

所以这是我的解决方案:首先是一个扩展类OutputStream(重写所有方法OutputStream比 for更容易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);
}
}

And then a very simple utility class to set outand err:

然后是一个非常简单的实用程序类来设置outerr

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

}

回答by itshorty

The anser of @Michael is a good Point. But extending PrintStream is not very nice, because it uses a internal method void write(String)to write all things to an OutputStream.

@Michael 的 anser 是一个很好的观点。但是扩展 PrintStream 并不是很好,因为它使用内部方法void write(String)将所有内容写入 OutputStream。

I prefer to use the LoggingOutputStreamClassfrom the Log4J Contrib package.

我更喜欢使用Log4J Contrib 包中的LoggingOutputStreamClass

Then i redirect the system-streams like this:

然后我像这样重定向系统流:

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

回答by Fizou

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

在 Skaffman 代码中:要删除 log4j 日志中的空行,只需在 createLoggingProxy 的 PrintStream 中添加“println”方法

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

回答by MarkG

The answers above give an excellent idea to use proxy for the STDOUT/ERR logging. However provided implementation examples do not work nicely for all cases. For example, try

上面的答案提供了使用代理进行 STDOUT/ERR 日志记录的好主意。然而,提供的实现示例并不适用于所有情况。例如,尝试

System.out.printf("Testing %s\n", "ABC");

System.out.printf("测试%s\n", "ABC");

The code from examples above will cut the output into separate pieces on a console and in multiple not readable Log4j entries.

上面示例中的代码将在控制台和多个不可读的 Log4j 条目中将输出切割成单独的部分。

The solution is to buffer the output until a trigger '\n' is found at buffer's end. Sometimes the buffer ends with '\r\n'. The class below addresses this issue. It is fully functional. Call the static method bind() to activate it.

解决方案是缓冲输出,直到在缓冲区的末尾找到触发器 '\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  

}

回答by Vinoth Sakthivadivel

Before using the System.setOut and System.setErr method we should reset the java.util.logging.LogManager object by using reset() method.

在使用 System.setOut 和 System.setErr 方法之前,我们应该使用 reset() 方法重置 java.util.logging.LogManager 对象。

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

回答by Rowan

For those looking for how to do this in log4j2. There is now a component to create these streams for you.

对于那些在 log4j2 中寻找如何做到这一点的人。现在有一个组件可以为您创建这些流。

Requires including the log4j-iostreams jar
See: https://logging.apache.org/log4j/2.x/log4j-iostreams/index.html

需要包括 log4j-iostreams jar
参见:https: //logging.apache.org/log4j/2.x/log4j-iostreams/index.html

Example:

例子:

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