71

log4j を使用する Java アプリがあります。

構成:

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 が失われます。例外スタック トレースと sysout を日次ロール ファイルにリダイレクトするにはどうすればよいですか?

4

10 に答える 10

108
// 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);
            }
        };
    }
}
于 2009-09-02T20:51:48.663 に答える
12

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);
        }
    };
}
于 2012-09-11T12:39:24.150 に答える
12

私は Michael S. からアイデアを受け取りましたが、あるコメントで述べたように、いくつかの問題があります。すべてをキャプチャするわけではなく、いくつかの空の行が出力されます。

System.outまた、 と を分離したかったSystem.errので、System.outログレベルでログ'INFO'System.err記録され、'ERROR'(または必要'WARN'に応じて) でログに記録されます。

これが私の解決策です。まず、拡張するクラスです( forよりもOutputStreamすべてのメソッドをオーバーライドする方が簡単です)。指定されたログ レベルでログを記録し、すべてを別の にコピーします。また、「空の」文字列 (空白のみを含む) を検出し、ログに記録しません。OutputStreamPrintStreamOutputStream

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

}
于 2011-09-20T01:20:24.783 に答える
6

上記の回答は、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  

}
于 2015-01-14T19:04:51.470 に答える
6

アプリケーション サーバー、サーブレット コンテナーなどを使用している場合は、kgiannakakis の回答を参照してください。

スタンドアロン アプリについては、こちらを参照してください。java.lang.Systemクラスを使用して、 stdinstdout、およびstderrを再設定できます。基本的に、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());
于 2009-07-29T13:35:23.307 に答える
2

経由でスタックトレースをログに記録していると思いますe.printStackTrace()か?例外オブジェクトを Log4j ロギング メソッドに渡すと、ログに表示されます ( Logger.error(Object obj, Throwable t)を参照) 。

System.out と System.err を、Log4j にリダイレクトする別のPrintStreamに変更できることに注意してください。System.out.println()これは簡単な変更であり、すべてのステートメントを変換する手間を省きます。

于 2009-07-29T13:22:59.930 に答える
2

標準出力とエラー ストリームは、コンテナーから管理されます。たとえば、Tomcat は JULI を使用して出力とエラー ストリームをログに記録します。

これらはそのままにしておくことをお勧めします。アプリケーションで System.out.print を使用しないでください。スタック トレースについては、こちらを参照してください。

于 2009-07-29T13:26:28.523 に答える
1

@Michael の anser は良いポイントです。void write(String)しかし、PrintStream の拡張は、内部メソッドを使用してすべてのものを OutputStream に書き込むため、あまり良くありません。

Log4J Contrib パッケージのLoggingOutputStreamクラスを使用することを好みます。

次に、システム ストリームを次のようにリダイレクトします。

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);
    }
}
于 2012-06-18T10:43:23.037 に答える