M12i.

学術書・マンガ・アニメ・映画の消費活動とプログラミングについて

SLF4Jについて

何だか微妙な局面を迎えている職場の、悠長に進められている技術的な議論のなかで出てきたSLF4Jについて。ユーザーマニュアル…というかこれはほとんど広告文なのですが、調査がてら翻訳してみました。

原典は、“SLF4J user manual”(QOS.ch)(2011/06/25 15:32取得)。

******************************

SLF4Jユーザー・マニュアル

Simple Logging Façade for Java(SLFJ)は、たとえばjava.util.logging、log4jそしてlogbackといった種々のロギング・フレームワークのための簡潔なファサードもしくは抽象としてはたらきます。エンドユーザー〔開発者〕は好ましいロギング・フレームワークを、配備の際に接続することができます。SLF4Jが有効化されたあなたのライブラリ/アプリケーションは、たった1つ必須の依存性──つまりslf4j-api-1.6.1.jarが追加されることを暗に示します。もしクラスパスに〔ロギング・フレームワークとの〕バインディングが見つからない場合、SLF4Jは何も行いません。

Hello World

プログラミングの伝統にのっとって、ここでSLF4Jを使用して“Hello world”を出力するもっとも簡単な方法の例をを示しましょう。はじめに“HelloWorld”という〔クラスの〕名前とともにloggerを取得します。続いてloggerはログ出力のために“Hello World”をとります。

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

public class HelloWorld {
  public static void main(String[] args) {
    Logger logger = LoggerFactory.getLogger(HelloWorld.class);
    logger.info("Hello World");
  }
}

この例を実行するには、まずslf4jの配布ファイルをダウンロードする必要があります。つづいてそれを解凍します。それが済んだら、slf4j-api-1.6.1.jarファイルをクラスパスに追加します。

HelloWorldのコンパイルと実行の結果は、コンソール上で次のように出力されるはずです。

SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
〔“org.slf4j.impl.StaticLoggerBinder”クラスの読み込みに失敗しました。何も実施しない(NOP)ロガー実装がデフォルトになっています。より詳細な情報についてhttp://www.slf4j.org/codes.html#StaticLoggerBinderを参照してください。〕

この警告は、slf4jバインディングがクラスパス上に見つからなかったために出力されています。

バインディングがクラスパスに追加されることで、この警告はただちになくなります。例えばslf4j-simple-1.6.1.jarファイルを追加することでクラスパスには、以下のjarファイルが含まれるようになります:

  • slf4j-api-1.6.1.jar
  • slf4j-simple-1.6.1.jar

HelloWorldをコンパイルして実行すると、今度は次のように出力されるはずです。

0 [main] INFO HelloWorld - Hello World

典型的な使用パターン

次のサンプルコードでは、SLF4Jの典型的な使用パターンを示しています。15行目にある{}プレースホルダーの使用に注意してください。より詳細な情報は、FAQの中の質問"What is the fastest way of logging?"を参照してください。

 1: import org.slf4j.Logger;
 2: import org.slf4j.LoggerFactory;
 3: 
 4: public class Wombat {
 5:  
 6:   final Logger logger = LoggerFactory.getLogger(Wombat.class);
 7:   Integer t;
 8:   Integer oldT;
 9:
10:   public void setTemperature(Integer temperature) {
11:    
12:     oldT = t;        
13:     t = temperature;
14:
15:     logger.debug("Temperature set to {}. Old temperature was {}.", t, oldT);
16:
17:     if(temperature.intValue() > 50) {
18:       logger.info("Temperature has risen above 50 degrees.");
19:     }
20:   }
21: } 
配備時のロギング・フレームワークとの結合

前述のように、SLF4Jはさまざまなロギング・フレームワークをサポートしています。SLF4Jは“SLF4J バインディング”と呼ばれるいくつかのjarファイルを伴って配布されています。それぞれのバインディングは種々のロギング・フレームワークに対応しています。

slf4j-log4j12-1.6.1.jar
広範に用いられているロギング・フレームワークlog4j version 1.2のためのバインディングです。log4j.jarをクラスパスに登録しておく必要があります。
slf4j-jdk14-1.6.1.jar
JDK1.4ロギングとも呼ばれるjava.util.loggingのためのバインディングです。
slf4j-nop-1.6.1.jar
すべてのログ内容を出力せず破棄するNOPのためのバインディングです。
slf4j-simple-1.6.1.jar
すべてのイベントをSystem.errに対して出力するSimple実装のためのバインディングです。“INFO”レベルかそれ以上のメッセージのみ出力されます。小規模なアプリケーションのためにはとても便利なバインディングです。
slf4j-jcl-1.6.1.jar
Jakarta Commons Loggingのためのバインディングです。このバインディングはSLF4Jのロギング処理のすべてをJCLに委譲します。

SLF4Jプロジェクト外から提供されているSLF4Jバインディングもあります。例えば、logbackはSLF4Jをネイティブにサポートしています。ログバックのch.qos.logback.classic.LoggerクラスはSLF4Jのorg.slf4j.Loggerインターフェースの直接の実装です。これによりlogbackとともにSLF4Jを使用する際のメモリーと演算のオーバーヘッド〔付帯的なコスト〕はまったくなくなっています。

ロギング・フレームワークの切り替えは、SLF4Jバインディングをクラスパス上で置き換えるだけです。例えば、java.util.loggingからlog4jへの切り替えは、slf4j-jdk14-1.6.1.jarをslf4j-log4j12-1.6.1.jarで置き換えるだけです。

SLF4Jはいかなる特定のクラスローダー機構にも依存しません。事実、いずれのSLF4Jバインディングも、コンパイル時にただ一つのロギング・フレームワークとがっちり結びつけられます。例えば、slf4j-log4j12-1.6.1.jarはコンパイル時に、log4jを使用するようバインドされます。あなたは、slf4j-api-1.6.1.jarに加えて、選択したただ1つのバインディングをクラスパスの通っている場所に配置するだけです。クラスパスに1つ以上のバインディングを置いてはいけません。ここで一般的な概念を示した図を示しておきます。〔訳者:たいした図ではありませんし著作権の問題もありそうなので省きます。〕

SLF4Jのインターフェースとその種々のアダプターは、非常にシンプルです。Java言語に親しんでいるほとんどの開発者はそれを読んで、1時間もしないうちにすべてを理解できるはずです。SLF4Jはいかなるクラスローダーにも直接的にアクセスしたりしないため、クラスローダーに関する知識は不要です。結果的に、SLF4JはJakarta Commons Logging (JCL)に見られるようなクラスローダー問題やメモリーリークと無縁です。

SLF4Jインターフェースと配備モデルのシンプルさは、新しいロギング・フレームワークの開発者たちがSLF4Jバインディングの記述する際の非常な簡便性をもたらしています。

ライブラリ

広範に配布されるコンポーネントやライブラリの制作者たちは、そうしたライブラリを利用するエンドユーザー〔開発者〕に特定のロギング・フレームワークの導入を押しつけることを避けるために、SLF4Jインターフェースを意識したコーディングをするでしょう。エンドユーザーは配備の段階で、対応するSLF4Jバインディングをクラスパスに追加することによって、好ましいロギング・フレームワークを選択します。あとになって既存のバインディングから別のものへと、クラスパスの上で置き換え、アプリケーションを再起動することで、ロギング・フレームワークを変更できます。このアプローチはシンプルかつ堅牢であることが証明されています。

SLF4Jのバージョン1.6.0以降、クラスパスにバインディングが見つからなかった場合、slf4j-apiはすべてのログ出力要求を破棄するNOP実装をデフォルトとして使用します。このため、org.slf4j.impl.StaticLoggerBinderクラスが見つからなかった際にNoClassDefFoundError例外を投げる代わりに、SLF4Jバージョン1.6.0とそれ以降ではバインディングが見つからなかった旨のメッセージを一回だけ出力して、その後一切のログ出力要求の棄却を粛々と実行しはじめます。例えば、〔先ほど登場した〕WombatはロギングのためSLF4Jに依存している生物学関係のフレームワークであるとしましょう。エンドユーザーに特定のロギング・フレームワークを押しつけることを避けるために、Wombatの配布ファイルにはslf4j-api.jarが含まれていますが、バインディングはありません。SLF4Jバインディングがクラスパスに見つからなかったとしても、Wombatは配布されたままの構成で実行できます。エンドユーザーはSLF4JのWebサイトから特定のバインディングをダウンロードする必要もないのです。エンドユーザーがロギングを有効にする決定を行ったときのみ、選択されたロギング・フレームワークに対応するバインディングを導入すればよいのです。

SLF4Jによってロギングを一元管理する

しばしば既存のプロジェクトはSLF4J以外のロギングAPIを使用した種々のコンポーネントに依存しています。プロジェクトがJCL、java.util,logging、log4jやSLF4Jの組み合わせに依存するのは一般的なことです。こうした場合、ロギング機能は単一のチャネルを通じた一元管理の下にあるほうが好ましいでしょう。SLF4JはJCL、java.util,loggingやlog4jのためのブリッジモジュール〔bridging modules〕を提供することでこの一般的なユースケースの要件を満たします。より詳細な情報については、レガシーAPIの橋渡しのページを参照してください。

Mapped Diagnostic Context (MDC)のサポート

"Mapped Diagnostic Context"はロギング・フレームワークにより保持されるマップです。アプリケーションはそこにキーと値のペアを登録することができます。これらの値はロギング・フレームワークによりログ・メッセージの中に挿入されます。
SLF4JはMDC──"Mapped Diagnostic Context"をサポートしています。もし下層のロギング・フレームワークがMDC機能を用意しているのなら、SLF4Jはその下層のフレームワークのMDCに処理を委譲します。現在のところ、MDC機能を提供しているのはlog4jlogbackだけです。もしそのフレームワークがMDCを提供していない場合──例えばjava.util.loggingなどです──SLF4JはMDCデータを保持してはいるものの、〔必要ならば〕その情報は開発者が独自に記述したコードから取得しなくてはなりません。
このためSLF4Jユーザーとしては、log4jもしくはlogbackが使用されている状況下でMDC情報の優位性を得ることがきます。しかしあなたのユーザーたち〔あなたが制作したライブラリのユーザー=開発者たち〕に対してこれらのフレームワークを依存性として強制はしません〔つまりあなたがSLF4Jでロギングを行いMDCの機能を使用したからといって、あなたのコードのユーザーたちは依然としてMDC対応・非対応に関係なく、自分自身が選んだロギング・フレームワークを採用することができるわけです〕。
MDCについてより多くの情報を必要とする場合、logbackマニュアルのMDCについての章を参照してください。

要旨
優位性 解説
配備時にロギング・フレームワークを選択できる 適切なjarファイル(バインディング)をクラスパスに配置することで、所望のロギング・フレームワークを配備時に結合することができます。
フェイル・ファストな処理 〔必要な〕クラスをJVMにより読み込む方法によって、フレームワークバインディングは非常に早い段階で自動的に検証されます。
人気のフレームワークのためのバインディング SLF4Jは人気のあるフレームワーク──log4jjava.util.logging、 Simple loggingそしてNOPをサポートしています。logbackプロジェクトはネイティブにSLF4Jをサポートしています。
レガシー・ロギングAPIとの橋渡し JCL over SLF4Jの実装──すなわちjcl-over-slf4j.jarはあなたのプロジェクトを、JCLを使用している既存のソフトウェアとの互換性を破壊することなく、SLF4Jへと段階的に移行することを可能にします。これと同様にlog4j-over-slf4j.jarモジュールとjul-to-slf4jモジュールは、log4jjava.util.loggingの呼び出しをSLF4Jのそれへと変更できるようにします。詳細についてはレガシーAPIの橋渡しを参照してください。
ソースコードの移行 slf4j-migratorユーティリティはあなたのソースコードをSLF4Jを使用するものへと移行するのを手助けします。
パラメータ化されたログメッセージのサポート すべてのSLF4Jバインディングはパラメータ化されたログ・メッセージをサポートしています。パラメータ化されたメッセージによる方法はあなたのコードのパフォーマンス改善に有意な結果をもたらします。

******************************

原典は、“SLF4J user manual”(QOS.ch)(2011/06/25 15:32取得)。