Scalaでfluentd loggingするときのライブラリ比較
Scalaプログラム内でfluentdを使ったロギング処理を書きたい場合についての話です。
fluentdなので、わざわざプログラム内部でロギングせずとも、他の一般的な方法(例えばlog4j)でファイルにログ出力して、それをfluentdのtailプラグインで拾う・・・という手法が最も楽だと思います。
勿論これでも良いですが、折角ならばより柔軟にロギングしたいと思うかもしれません。
そんなときはライブラリを使いましょう。
ライブラリの候補
- https://github.com/InnovaCo/fluentd-scalaLogbackのappenderを経由してfluentdへログ出力できます。Logbackのappenderなので、Logbackと同じI/Fという利点があります。
- https://github.com/oza/fluent-logger-scala内部でfluent-logger-javaを利用したライブラリです。独自のLoggerクラスを使って単一の文字列やMapをログ出力します。独自のクラスを使うので、Logbackなどと親和性が無い点は辛いですが、その分自由度は高いです。 ozaさんのリポジトリとなっていますが、公式ライブラリのようです。
- https://github.com/sndyuk/logback-more-appendersLogbackのappenderを経由してfluentdへログ出力できます。1と同じ感じです。
- https://github.com/fluent/fluent-logger-javaJava用のライブラリです。公式。
実行例など(fluentd-scala)
インストールはREADMEに従ってsbtに依存性を書きます。logback.xmlも用意して、以下のようなプログラムを実行します。
import org.slf4j.LoggerFactory
object Main extends App {
val logger = LoggerFactory.getLogger(getClass)
logger.info("hello")
}
これだけでOKです。実行結果はこんな感じでfluentdにlog出力されます。
2015-02-01 12:30:40 +0900 my.project: {"level":"INFO","logger":"sample.Main$","host":"myhostname","timemillis":"1422775654604","thread":"fluentd-logger-akka.actor.default-dispatcher-4","message":"hello"}
見づらいのでjson部分のみ整形すると・・・
{
"level":"INFO",
"logger":"sample.Main$",
"host":"myhostname",
"timemillis":"1422775654604",
"thread":"fluentd-logger-akka.actor.default-dispatcher-4",
"message":"hello"
}
こんな感じです。
例外の場合も見てみます。
{
"level": "ERROR",
"logger": "sample.Main$",
"throwable": "java.lang.IllegalStateException: oops\n\tat sample.Main$.delayedEndpoint$sample$Main$1(Main.scala:8) [classes/:na]\n\tat sample.Main$delayedInit$body.apply(Main.scala:5) [classes/:na]\n\tat scala.Function0$class.apply$mcV$sp(Function0.scala:40) [scala-library-2.11.5.jar:na]\n\tat scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) [scala-library-2.11.5.jar:na]\n\tat scala.App$$anonfun$main$1.apply(App.scala:76) [scala-library-2.11.5.jar:na]\n\tat scala.App$$anonfun$main$1.apply(App.scala:76) [scala-library-2.11.5.jar:na]\n\tat scala.collection.immutable.List.foreach(List.scala:381) [scala-library-2.11.5.jar:na]\n\tat scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:35) [scala-library-2.11.5.jar:na]\n\tat scala.App$class.main(App.scala:76) [scala-library-2.11.5.jar:na]\n\tat sample.Main$.main(Main.scala:5) [classes/:na]\n\tat sample.Main.main(Main.scala) [classes/:na]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_25]\n\tat sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_25]\n\tat sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_25]\n\tat java.lang.reflect.Method.invoke(Method.java:483) ~[na:1.8.0_25]\n\tat com.intellij.rt.execution.application.AppMain.main(AppMain.java:134) [idea_rt.jar:na]\n",
"host": "myhostname",
"timemillis": "1422776273022",
"thread": "fluentd-logger-akka.actor.default-dispatcher-3",
"message": "error occurred!"
}
throwableが追加されています。
非常に簡単に使えて、既存のloggerの置き換え等にも対応できるというメリットがあります。反面、Logger.infoなどに任意のobjectを与えられないため、微妙に使いづらいです。
実行例など(fluentd-logger-scala)
これも同様にインストールはREADMEを参考にしてください。こちらはlogbackとは関係ないので、sbtの依存性と以下のコードだけで動きます。
import org.fluentd.logger.scala.FluentLoggerFactory
object Main extends App {
val logger = FluentLoggerFactory.getLogger("myTag", "localhost", 24224)
logger.log("myLabel", "myKey", "myValue")
}
実行結果はこちら。
2015-02-01 12:30:40 +0900 myTag.myLabel: {"myKey":"myValue"}
先ほどの例と比べると簡素ですが、その分柔軟性があります。
第二引数にはMapが使えるので、例えば以下のようなコードを書くと
logger.log("myLabel", Map(
"key1" -> "val1",
"key2" -> "val2",
"nestedMap" -> Map("nestkey1" -> "nestval1")
))
こんな風にネストした形で、正しいjsonで出力されます。
2015-02-01 12:30:40 +0900 myTag.myLabel: {"key1":"val1","key2":"val2","nestedMap":{"nestkey1":"nestval1"}}
logback-more-appendersについて
これも結局はlogback経由なので、初めのfluentd-scalaと同じです。用意されているappenderを使うだけです。ちなみにJavaライブラリです。
fluent-logger-javaについて
これもfluent-logger-scalaと同じです、というよりはfluent-logger-scalaの中身です。なので、やっていることは変わりません。
結局どれを使えばいいの?
オススメはfluent-logger-scalaですが、上記のようにメリットデメリットがあります。
- fluentd-scala なるべく楽したい人、logbackのI/F(Logger class)で統一したい人向け
- fluent-logger-scala 柔軟に色々出力したい人、自分でlogのjsonの各fieldをコントロールしたい人、とにかく何でもloggingしたい人向け
ちなみに自分はfluent-logger-scalaを利用しています。かなりいい感じです。
みなさんも使ってみてはいかがでしょうか。
以下の記事でfluent-logger-scalaをもう少し解説しています。