ScalaではJavaのライブラリがフルに使えるということで、Javaでも有名なLogback + SLF4J (今回はScalaなので + ScalaLogging)の組み合わせでどのようにログを取ったらいいのかをサンプルコードを見ながら説明します。

これに関するドキュメントは結構あるんですが、それぞれの役割、導入、実際の使い方を初めからずらーっと書いている記事がなさそうだったのでまとめました。

Scalaやっててログを取りたいけど、何していいかわからない、SLF4J, Logback, ScalaLoggingって何かも全くわからんという人も理解できるようにしました。(Play Framework使っている人もそうでない人も大丈夫です)

SLF4J, Logback, ScalaLoggingのそれぞれの役割

SLF4J

ロギング機能のインターフェイスをもったFacadeです。なので、インターフェイスだけ提供してログ機能の実態は持ちません。
Javaはいろんなロガー(commons-logging、log4j、slf4j-api、jcl-over-slf4j、logback-classic、logback、log4j、java.util.logging …)があるので、共通のAPIがないと使いにくいということで、とりあえずSLF4Jを導入しておけば、後から気軽にログの実装方法を変更できます。

Logback

  • ログの実際の設定部分を書くところです。logback.xmlなどを定義する(詳細は後述)ことでLogging設定がとても柔軟に行えます。
  • レベル別 [trace, debug, info, warn, error]に挙動を変更できます。
    • infoのログはコンソールだけに出すけど、warn以上(warnとerror)のログはここのファイルにも出力したい、など
  • ログ出力先ファイルの圧縮、非同期でのログ出力、ローテーション (ある条件(ファイルサイズや時間)を越えたら新しいファイルに出力先を変えること)などを柔軟に設定できます。

ScalaLogging

SLF4JをScalaで使用する際のヘルパーです。

  • 使用例: logger.info("message here") // これだけでinfoレベルのログを残せる。

ちなみにlogger.debug("debug message") と書くだけで

という風にコンパイルしてくれるので、isDebugEnabledなどをいちいち書く必要ありません。

SLF4J, Logback, ScalaLoggingの実際の使い方手順

導入方法

LibraryDependenciesに以下のものを追加します。(Scala Versionは 2.10.5でテスト済み)。Play Frameworkを使用している場合は既にplay.api.LoggerがあるのでPlayが使える状態なら何もしなくても構いません。

Facadeの働きをするslf4jとそのヘルパーであるScalaLoggingはcompile時、実装部分であるlogbackはruntimeに読み込むようにします。
これは実装部分 (Logback) もcompileのスコープにあると、柔軟にロギング実装の方法を変更しにくいからです。compile時にslf4jが読み込まれていれば「このライブラリは SLF4J を使う」ということがとりあえず明確になるので問題ありません。

Logbackの設定

  • ファイルの設置場所
    • src/main/resources/ ディレクトリにlogbackの設定ファイルを置く。
    • test用のlogbackは src/test/resources/ に配置
    • Play Frameworkを使用している場合は conf/ ディレクトリに設定ファイルを置く。
  • ファイル名
    • 上の「ファイルの設置場所」内で以下の順番でファイルを探し、初めに見つかったものをログの設定ファイルとして使う。
    • logback.groovy -> logback-test.xml -> logback.xml
    • PlayFrameworkの場合は application-logger.xml -> logger.xml -> logback.xml
  • 設定ファイル (logback.groovy, logback-test.xml, logback.xml, application-logger.xml, or logger.xml)の書き方の例
    • appenderタグ
      • 「どの場所にどういうフォーマットでログを出力するのか」を定義する
      • filterでどのログレベル(trace, debug, info, warn, error)のログを出すのか決める。
      • patternでログのフォーマットを決める
      • ローテーション、非同期、ファイルの圧縮もここで決める。
    • propertyタグ
      • 設定ファイルのプロパティ。変数みたいに名前と値を決めてファイル内で使いまわせる。

実際にScalaLoggingを使ってログを出力する作業

  • Play Frameworkの例
    “log message”が上のappenderタグのfileNamePatternの%msgの値として出力されます。
    fileNamePattern(appenderタグ内にあるログのフォーマット決めるところ)の %logger に関しては「Logger.」を使う場合は “application” になります。この名前を変えたい場合は下のサンプルコードの方に新しいロガーを名前付きで生成します。

  • 上で書いたlogback.xmlの設定で「Logger.info(“log message”)」と書いた場合のコンソールへの出力例

  • Play 以外の例
    LazyLoggingが「logger」というメンバ変数を持っているので、LazyLoggingをmix-in (LazyLoggingを継承)したらそれだけでlogger変数が使えます。LazyLoggingに対してStrictLoggingというtraitもありますが、こっちは変数loggerを遅延評価しないという特徴を持ちます。なので、特に遅延評価で問題なければLazyLoggingで良いでしょう。

最後に

ログは非常に有効ですが、とりあえず取ってみたけどフォーマットもよくわからないし日付にファイル分けてないし使いにくいから結局ログ取りっぱなしで何も活用できていないという時もあると思うので、SLF4J (ScalaLogging) + Logbackを使って整理されたログを残していきましょう。
そして、あとはログをFluentなどでうまく活用していきましょう!