たなかこういちの資料室

システム開発に携わる筆者があれこれ試したことや学んだことについてのまとめ

Log4j 2を使ってみよう

Log4j 2(※Log4jのver.2)が2014年7月にリリースされました。本記事執筆時点(※2014年12月)で2.1が最新です。

 
Log4j 1や"SLF4J with Logback"と比べて、どういった特徴があるかということについて、下記のような解説があります。
 
Apache Log4j 2、"Welcome to Log4j 2! - Introduction":

InfoQ、「Apache Log4j 2.0 - アップグレードする価値はあるか?」:
 
振り返るとLog4j 1.x系の安定最新版は1.2.9 1.2.17です<'16.10.2修正>。1.3の開発が進んでいましたがalphaで止まっていたようです。
 
 
その後はSLF4J w/ LogbackLog4j 1.xの実質的な後継となりました。
 
 
最後発となるLog4j 2は、SLF4J w/ Logbackが提供しているAPIや機能のほとんどを継承しつつ、さらなる機能追加や性能の向上を図っています。Apache Foundationとしてロギングフレームワークの実装を改めてラインナップに加えることができた、という意味もあるのでしょう。Log4j 2はSLF4J w/ Logbackの後継として主流の地位を得ることとなるかどうかは未知数ですが、内容的には素直に「SLF4J/Logbackの拡張版」と捉えていいと思いました。
 
Log4j 2の機能
 
気になった機能を列挙します。(※ここに挙げる機能はLog4j 1.xやLogbackで既に実現されているものも含んでいます。Log4j 1.x〜SLF4J/LogbackLog4j 2の系譜の最新状況を復習も兼ねつつ押さえておく、と捉えてください。)
 
非同期処理
 
Logger#debug/info/errorメソッド等呼び出し後直ちにリターンして、実際のログ出力処理は別スレッドで行うように構成できます。これでアプリケーション本体側のパフォーマンスが劇的に向上する、とのことです。

"Asynchronous Loggers for Low-Latency Logging":
 
MapMessage
 
Stringだけでなく、Map構造を持つMapMessageがサポートされました。PatternLayoutでMapMessageの要素を個別に参照する事ができます。
 
他にも様々な機能性をもったMessageの実装が用意されています。独自の実装も容易に追加できそうです。
 
 
ThreadContext
 
ロギングするとき"5W1H"情報を添えるのが常套かと思いますが、その"5W1H"情報を保持するための仕組みとして利用できるThreadContextが提供されています。ThreadLocalをラップした実装のようです。ThreadContextクラス一つでMap構造とStack構造をサポートしています。ThreadContextにセットした情報はPatternLayoutで個別に参照できます。
 
 
PatternLayout
 
Patternを指定することで任意の表現ができます。前述のとおりMapMessageやThreadContextの内容を参照する事もできます。
 
"PatternLayout":
 
JSONLayout
 
JSONLayoutを用いると、ログレコードをJSON形式の(半)構造化データとして出力する事ができます。
 
※が、ぐるぐるせんせいによると使い勝手に難がある様子です。MapMessageとJSONLayoutを組み合わせれば非常に簡易に(半)構造化データでのロギングが実現できるとも思われますが、少なくとも2.0時点ではうまくいかないようです。2.1でどうかは筆者は未確認です。
 
 
CouchDB、MongoDBをAppenderに使用できる
 
NoSQLAppenderなるAppenderが用意されています。具体的には現時点ではCouchDBとMongoDBがサポートされているとのことです。
 
※(半)構造化データでのロギング目的としてはこちらが本命かもしれません。
 
"NoSQLAppender":
 
多彩なFilter
 
Appender、Loggerに対してFilterを設定できます。Filterには下記のようなものが用意されています。(※主だったもの)
 
ThresholdFilter ... ログのLevel
RegexFilter ... ログのMessageテキストに対して正規表現マッチ
MapFilter ... MapMessageの項目の値
ThreadContextMapFilter ... ThreadContextの項目の値
 
 
設定ファイル内でPropertyが扱える
 
設定ファイルは新しい"log4j2.xml"となります。"log4j2.xml"では、Propertyを定義し別の箇所で参照する書き方ができます。例えば、PatternLayoutのpatternをPropertyで定義しておいて、Console AppenderとFile Appenderそれぞれで定義されたパターンを参照する書き方ができます。別の例としては、、複数のFile Appenderの出力先ファイルパスの共通ベース部分を、Propertyとして外出し定義することができます。
 
加えて、OSの環境変数Javaシステムプロパティも参照できるので、実行環境に応じてファイルパスを切り替えることなども実現できるでしょう。
 
"Property Substitution":
 
他ロギングフレームワークAPIからのブリッジ実装
 
下記のロギングAPILog4j 2にブリッジする実装が用意されています。
 
Log4j 1.2
SL4J
Apache Commons Logging
JUL(java.util.logging)
 
1.xとの互換性
 
packageが変更、クラスとメソッドは“ほぼ”互換
 
Java APIとしてはpackageが異なっています。Loggerのインスタンス取得の手順まわりに多少変更があります。Logger#debug/info/error等のログ出力メソッドは完全に上位互換です。それ以外もsemanticsが変わる程のAPI変更は無いので、書き換えが必要でも機械的な対応で済むと思います。
 
 
設定ファイルは"log4j2.xml"、他JSONYAMLサポート
 
設定ファイルが"log4j.xml"から"log4j2.xml"に変更となります。syntaxも変更されていて互換性はありません。全般に実装クラスのフルネームを書かなくて済むようになっています。(※専用のXMLの要素名で表されます。"log4j2.xml"を定義するようなXML Schemaは存在せず、記述に自由度があります。)しかしこちらもsemanticsが変わる程ではないので、機械的な書き換えで済むでしょう。(※XSLTでいけるかもしれません。)
 
"log4j.properties"はもはや使えません。一方で"log4j2.json"、"log4j2.yml"がサポートされています。
 
 
1.2 to 2 Bridgeの提供
 
前述のように、Log4j 1.2 to 2のブリッジ実装が提供されています。これを用いれば、Javaソースコードは1.x系のAPIを用いたままでも、実装をLog4j 2に差し替えられます。サードパーティ製のプログラムでソースコードに手が入れられない場合でも、1.x系の.jarファイルを削除して、1.2 to 2 Bridgeの.jarファイルを配置すれば、実装を差し替えることができます。差し替えられた実装はもちろん"log4j2.xml"を必要とします。"log4j.xml"があっても読みません。
 
mavenで依存関係管理している場合、下記のような手順で依存関係ツリーからLog4j 1.xを除外できます。
 
1. 対象のプロジェクトに対して下記コマンドを適用してLog4j 1.xの存在をチェックします。
 
mvn dependency:tree
 
2. Log4j 1.xの存在が確認されたら、そのLog4j 1.xに依存しているサブプロジェクトについての指定に、Log4j 1.xについての指定を追記します。
 
Maven、"Dependency Exclusions":
http://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html#Dependency_Exclusions
 
3. 「2.」の後、再度「 mvn dependency:tree 」を実施します。すると別のサブプロジェクトにLog4j 1.xへの依存が見出されることがあります。そうしたら、そのサブプロジェクトについても「2.」を実施します。以上をLog4j 1.xが出現しなくなるまで繰り返します。
 
注意点
 
.jarファイルが細かく分かれている
 
必要な機能によって必要な.jarファイルが分かれています。主だったものを下記にまとめました。
 
機能 group id artifact id version scope
必須(I/F) org.apache.logging.log4j log4j-api 2.1  
必須(Impl.) org.apache.logging.log4j log4j-core 2.1  
Web App.サポート(*1) org.apache.logging.log4j log4j-web 2.1 runtime
Log4j 1.2 to 2 Bridge org.apache.logging.log4j log4j-1.2-api 2.1  
NoSQL Appender(*2) org.apache.logging.log4j log4j-nosql 2.1  
*1:Web App.サポートの.jarは実行時のみ必要、コンパイル時は不要です。「Web App.サポート」については次項を参照してください。
*2:NoSQL Appenderを用いる場合は、他にCouchDBやMongoDBのドライバーが必要です。
 
"NoSQL Appenders":
http://logging.apache.org/log4j/2.x/log4j-nosql/index.html
 
他詳細は下記ページを参照してください。
 
"Maven, Ivy, and Gradle Artifacts":
 
Webアプリケーションで使用する時は"log4j-web-2.x.jar"が必須、かつ"web.xml"を「Servlet 3.0」等に書き換え必要
 
※一番重要です!
 
Log4j 2の基本機能では、File Appenderなど外部資源を使用しているとき、アプリケーションのプロセス起動時にそれらの初期化が行われ、停止時に開放が行われます。ところでWebアプリケーションでは、一般にWebアプリが配置されているアプリケーションサーバーのプロセスを起動/停止することなく、Webアプリのdeploy/undeploy、startup/shutdownが行われます。このとき、Log4jの基本機能では外部資源の管理オブジェクトがリークしたりファイルのflushがなされず一部データが失われる等の可能性があります。
 
これらを避ける為の対応コードが「Web App.サポート」("log4j-web-2.x.jar")として提供されています。"log4j-web-2.x.jar"は実行時にWebアプリの.warファイル等に含まれていればよく、コンパイル時には不要です。
 
「Web App.サポート」を利用するためにアプリケーションのソースコードの書き換えは不要です。Servlet 2.5では"web.xml"への設定追記が必要です。Servlet 3.0では"log4j-web-2.x.jar"さえ存在すれば"web.xml"の設定追記も不要です。「Web App.サポート」はServlet 2.5以上をサポートします。Servlet 2.4以下はサポート対象外です
 
 
※筆者がTomcat 8.0で試した限り、配置するWebアプリが一つで、かつ、Webアプリ(再)配置時常にTomcatのプロセスを起動/停止することとしていれば、Servlet 2.4仕様で「Web App.サポート」が無くても、ロギングに問題は生じないように思われました。OSのファイルシステムにも寄るかもしれません。
 
◆以上
 

関連記事