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 ... other dependency... org.apache.logging.log4j log4j-slf4j-impl 2.0-beta8
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のほうが好みな理由はこれが地味に便利だからかも。