Saturday, October 26, 2013

log4j2にログを集める

log4j, slf4j, logback, commons-logging... こんがらがりませんか?僕はこんがらがりました。

ある実験的なWebアプリの開発の過程では、ログを最終的に何に集約するか?logbackか?それとも...?と、迷い、 結局log4j2に集約しました。これはそのメモみたいなものです。

なお、「slf4j+logbackのほうがナウでヤングだろ」といったご意見はスルーです。好みとかポリシーの問題です。 また、いにしえより伝わるlog4j1.xではなく、log4j2.xっていうところもポイントです。 java.util.logging?えーっ?!却下。

1. 前提とか方針とか

  • 新規のJava-Webアプリを開発する。そのコード上で使うLoggerにはlog4j2を使う。
  • ベースはSpringMVCフレームワーク。spring自身は内部でcommons-loggingを使っているので、それもlog4j2に集められるように設定する。
  • Spring以外にもいろいろOSSライブラリを使うが、それが吐くログもできる限りlog4j2に集める。例えば、内部でslf4j-apiを使ってるOSSが最近はとても多い。
  • 設定はlog4j2.xmlに集める。とりあえずWebアプリ内部に同梱する。
  • ビルドツールはmavenを使う

2. mavenのpom.xml

dependenciesタグ内で先に書いてあるほうが優先されるので、ログ出力ライブラリは上のほうに書いておくのが無難です。log4j-coreがもちろんlog4j2の本体。 log4j-jclがcommons-loggingによる出力をlog4j2に中継させるやつ。log4j-slf4j-implはslf4jによる出力を(以下略)

    
        
            org.apache.logging.log4j
            log4j-core
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-jcl
            2.0-beta8
        
        
            org.apache.logging.log4j
            log4j-slf4j-impl
            2.0-beta8
        

        ... other dependency...
    

maven使いじゃない人のために、これで実際どんなjarがビルドパスに入ってくれるのか、書いておきます。こんだけ。

log4j-core-2.0-beta8.jar
log4j-api-2.0-beta8.jar
log4j-jcl-2.0-beta8.jar
commons-logging-1.1.1.jar
log4j-slf4j-impl-2.0-beta8.jar
slf4j-api-1.7.5.jar
slf4j-ext-1.7.5.jar
cal10n-api-0.7.4.jar

3. log4j2.xml

log4j2.xmlをsrc/main/resourcesに置く。root(要するにデフォルト値)を設定するほか、 ログ出力を制御したいクラス名、パッケージ名に対して出力レベルや出力先(appender)を設定します。

4. コードを書く

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Foo {
    private static Logger logger = LogManager.getLogger();
    public void bar(String a, String b) {
        logger.debug("a={},b={}", a, b);
    }
}

ログに変数をいれたいときに、logger.debug("a=" + a + ",b=" + b ) などとやってしまいがちですが、 実はこれ、ログ出力メソッドよりも先に+演算子による文字列連結の方が先に実行されますので、性能影響が出ます。 logger.debug()なんて本番環境ではオフにすると思いますが、それでも文字列連結は無駄に実行されてしまうのです。 どの程度の性能差が出るかは Performance - Apache Log4j 2 に詳しく解説されています。 せっかくlog4j2を使うのですから、{}記号によるプレースホルダ機能を使うようにしましょう。

また、log4j1.xのときは、
provate static Logger logger = Logger.getLogger(Foo.class);
のように、ロガー名として自クラスを明示的に与えてloggerを作る必要がありましたが、 log4j2のLogManagerでは省略できます。 log4j2のほうが好みな理由はこれが地味に便利だからかも。

No comments:

Post a Comment