Kengo's blog

Technical articles about original projects, JVM, Static Analysis and TypeScript.

SLF4Jが可変長引数をサポート

Javaのログ系ライブラリSLF4Jが、今月6日にリリースされた1.7.0で可変長引数をサポートしました。つまり今まで自分でObject配列を作っていた部分を、可変長引数でシンプルに書き直せるということです。

SLF4Jをご存じない方にこのインパクトをお伝えするには、コードを比べていただくのが一番でしょう。

// before
logger.info("expected is {} but given is {}", new Object[] {
    expected,  given
});

// after
logger.info("expected is {} but given is {}", expected,  given);

こうしてSLF4Jの残念さを代表するインタフェースが改善されたのはとても嬉しいですね。
同時にJDK5が必要になりました*1が、現状そこまで問題はないのではと思います。

互換性の話

で、様々なレイヤで広く利用されるSLF4Jだからこそ、互換性が気になりますよね。
嬉しいことに公式MLで"Client code compiled with slf4j-api-N.jar will run perfectly fine with slf4j-api-M.jar for any N and M."、つまり過去のAPI(JAR)にあわせて書かれたクライアントコードは最新のAPI(JAR)で問題なく動くと言及れています。これは可変長引数が実際には配列の受け渡しとして実装されていることに由来します。詳しい話が気になる方は、hishidamaさんのサイトなどをご覧になると良いでしょう。

APIバインディングのバージョンは揃える必要がある

MLでひとつ指摘されているのが、APIバインディングのバージョンを揃えなければならないということです。ここでAPIは各クライアントコードが依存するインタフェース、バインディングは実際にログを吐き出す実装を指します。
今回の変更がインタフェースの変更に相当することを考えると、過去の実装は新しいインタフェースを「知らない」ため問題になるのでしょう。

結局、アップグレード時に何を検討すべきか

APIバインディングをパッケージしている場合は特に気にせずアップグレードして問題ないでしょう。APIバインディングが環境によって提供される場合(Mavenのprovidedスコープなど)も、クライアントコードの互換性が保たれていることからコードに手を加える必要はありません。

APIバインディングが別々のクラスローダに読み込まれる場合や依存ライブラリが旧バージョンを同梱している場合、CLASSPATHに複数のJARが入っている場合などはバージョンが揃わないこともあるかもしれません。初期化時のログを確認しておく必要があります。
幸いSLF4JはMITライセンスなので、JAR衝突のおそれがある場合はrepackageする(classファイルを編集してpackageを変更してしまう)のも有用です。実行時はバインディングのライセンスも確認しておきましょう。

*1:ちなみに今JenkinsがJDK6への移行を検討しています → http://jenkins.361315.n4.nabble.com/VOTE-Bump-Jenkins-to-Java-6-td4639288.html