2013年05月16日

SLF4J - Javaロギング実装・ライブラリの柔軟な切り替え・効率化を実現するロギング Facade

僕の中では、 Java でロギングするなら log4j 1.2 系で必要十分。 それ以外、最新のトレンドについて考えることなんて数年前から無くなっていたんです。 だって、ライブラリの性能、機能(出力先の種類)が多少増えても現場のロギング要件はそんなには変わりませんから。 でも、今日ご紹介する SLF4J (Simple Logging Facade For Java) は大変興味深いロギングAPIなんですよ。 SLF4J は各種ロギング・ライブラリのアダブタAPIと言ってもよく、これによってロギング・ライブラリの実装を開発フォーズと綺麗に分離することが出来るんです(実装の遅延評価)。 さらに、このアダプタとしての役割を使えば開発者の負担も減らすことが事が出来るんですよ。

slf4j log icon


SLF4Jの概要・特徴


SLF4J はその名(Simple Logging Facade For Java)が指し示す通り、それ自身がロギングの実装を目指すのでは無く、実装と利用者の間に入り柔軟な切り替えを実現する為のAPI/ライブラリです。

SLF4J

この Facade パターンを実現する為に、SLF4J ではコア、アダプタ、実装(log4j, logback, etc)の3階層に別れてロギング・ライブラリを構成します。

Log4j Logback Logging Simple no logging
operation (nop)
SLF4J/コア slf4j-api.jar
SLF4J/アダブタ slf4j-log4j12.jar logback-classic.jar, logback-core.jar slf4j-jdk14.jar slf4j-simple.jar slf4j-nop.jar
ロギング・ライブラリ log4j-1.2.x.jar java.util.logging
出力先 ファイル、標準出力など(log4j設定による) ファイル、標準出力など(logback設定による) ファイル、標準出力など(logging設定による) 標準エラー
(System.err)
無し
(/dev/null)


例えば、最終的に log4j を使ってロギングを行うとすれば、slf4j-api.jar、log4j-1.2.x.jar をクラスパスに通せば良いことになります。
(勿論、貴方のアプリケーション・コードにはlog4jのimport, class名は含まずにです)

また、上の表を見ると、SLF4J プロジェクトから提供される simple, nop に関してはアダプタとロギング層が単一ファイルで提供されるのは分かりますが、Logback がそれ単体でアダプタ層まで対応していることに疑問を持つ方もいるかもしれません。 これは SLF4J/Logback の開発者が同一人物であることに起因しています。


SLF4Jの使い方 - SLF4Jを使った開発とロギング


さあ、さあ早速使ってみましょう。 まずは、ダウンロード・ページからzipアーカイブ(slf4j-x.x.x.zip)をダウンロードし、その中に含まれる slf4j-api-1.7.5.jar、slf4j-simple-1.7.5.jar をクラス・パスに通します。

slf4j  - eclipse library setting


これで開発環境は整いました。 次に示すのは SLF4J を使った簡単なアプリケーションのコードです。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyApp {

 private static Logger log = LoggerFactory.getLogger(MyApp.class);
   
 /**
  * @param args
  * 
  */
 public static void main(String[] args) {
    
  String s = null;
  
  log.info("Starting my application...");
  
  if (args != null && args.length > 0) {
   s = args[0];
  }
  
  try {
   
   log.info("Parsing integer: {}", s);   
   
   Integer.parseInt(s); 
   
   log.info("success: parse integer");
   
  } catch (NumberFormatException nfe) {
   log.error("Exception caught: ", nfe);
  }
 }

}


基本的な使い方は log4j と変わりません。 Logger インスタンスを生成後、Logger の持つ info, error メソッドを呼び出しロギングを行なっています。

少し特徴的なのは、ロギング・メソッドのシグネチャ。 サンプル・コード中にもありますが、{}を変換指定子として printf ライクにメッセージを出力します。

log.info("Parsing integer: {}", s);


これには可読性と遅延評価による性能向上という2つの意味があります。 以前 log5j の紹介と合わせて詳しい話をしましたが可読性だけでなく、運用時のログ設定によって出力レベルが最終決定するまで文字列の連結によるStringオブジェクトの生成を遅延出来るのでメモリ使用量、処理スピードでも有利になります(但し、小規模なアプリケーションでは逆効果になることもあります)。

・・・少し話は変わりますが僕はロギングに関するコードは一番最後に実装することが多かったんです。

だって、例えば log4j を使うとしたらコードを実装した後にはロギング設定ファイルと JVM への Java プロパティ(-Dlog4j.configuration)設定が必要になるでしょう? 真面目な人は問題無いかもしれませんが、僕は一々 IDE の Java アプリケーション起動設定を使うのが面倒なんです。

設定ファイルを無視して Java アプリケーションを起動すれば、こんな警告が表示されますよね。

log4j:WARN No appenders could be found for logger (MyApp).
log4j:WARN Please initialize the log4j system properly.
log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.


しかし、SLF4J ならば違います。開発フェーズとロギングの実装を分離出来るので、開発者は slf4j-api-1.7.5.jar、slf4j-simple-1.7.5.jar をクラスパスに通し、標準エラーにログ出力をリダイレクトしながら快適にコーディング、テストを続ける事が出来るんです。

slf4j  - eclipse console


無事開発が終わったら、jarファイルにエクスポートし、別途用意した log4j-1.2.x.jar ファイル及び設定ファイルと合わせてサーバーにデプロイします。

ちなみに今回利用する log4j 設定(xml)ファイルは次の通り。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" >

  <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
     <param name="Target" value="System.out" /> 
     <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %5p %c{1} %m%n" />
     </layout>
  </appender>

  <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
     <param name="File" value="${user.home}/logs/mylog.log" />
     <param name="Append" value="true" />
     <param name="MaxFileSize" value="5MB" /> 
     <param name="MaxBackupIndex" value="5" />
     <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss} %5p %c{1} %m%n" />
     </layout>
  </appender>
  
  <root>
    <priority value ="INFO" />
    <appender-ref ref="STDOUT"/>
    <appender-ref ref="FILE"/>
  </root>
  
</log4j:configuration>


さあ、log4j を使ってアプリケーションを起動してみましょう。

$ java -classpath :/path/to/MyApp.jar:/path/to/slf4j-api-1.7.5.jar: \
/path/to/slf4j-log4j12-1.7.5.jar:/path/to/log4j-1.2.17.jar \
-Dlog4j.configuration=file:///path/to/my.log4j.xml -Duser.home=/path/to/home MyApp 1
2013-05-15 11:37:53 INFO MyApp Starting my application... 2013-05-15 11:37:53 INFO MyApp Parsing integer: 1 2013-05-15 11:37:53 INFO MyApp success: parse integer


無事、log4j に切り替える事が出来ました。 勿論、ファイルへの出力だって大丈夫。

$ cat /path/to/logs/mylog.log 
2013-05-15 21:37:53  INFO MyApp Starting my application...
2013-05-15 21:37:53  INFO MyApp Parsing integer: 1
2013-05-15 21:37:53  INFO MyApp success: parse integer


Facade パターンの魅力を十二分に実感させてくれる SLF4J。 僕も今後の新規プロジェクトでは絶対に採用しようと思ってます!

それでは、より良いJava ロギング・ライフを。


Code Complete 第2版 上 完全なプログラミングを目指して
日経BP社 (2014-04-02)
売り上げランキング: 145

現場で使えるJavaライブラリ
竹添 直樹 島本 多可子 小津 美夕紀 亀井 隆司
翔泳社
売り上げランキング: 292,349

Posted by netbuffalo at 19:30│Comments(0)TrackBack(0)Java | プログラミング


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/4466209

コメントする

名前
URL
 
  絵文字