ども、cloudpack の かっぱ (@inokara) です。
はじめに
Play2 Framework アプリケーションから fluentd にアプリケーションログを送る為に fluent-logger を利用してみたいと思います。
準備
環境
- Ubuntu 13.10
- Java 1.7.0_55
- Play 2.2.5
- fluent-logger 0.3.1
テスト用のアプリケーション作成
play new testapp
Java アプリケーションで作成します。
fluent-logger の導入
testapp の build.sbt を以下のように修正します。
--- build.sbt.original 2014-11-03 00:03:09.898355946 +0900 +++ build.sbt 2014-11-03 00:03:14.046356066 +0900 @@ -5,7 +5,8 @@ libraryDependencies ++= Seq( javaJdbc, javaEbean, - cache + cache, + "org.fluentd" % "fluent-logger" % "0.3.1" ) play.Project.playJavaSettings
controller の作成
fluent-logger-java の README.md を参考にして Application.java は以下のように書きました。
package controllers; import play.*; import play.mvc.*; import views.html.*; import java.util.HashMap; import java.util.Map; import org.fluentd.logger.FluentLogger; public class Application extends Controller { private static FluentLogger LOG = FluentLogger.getLogger("app","127.0.0.1",24224); public static Result index() { //Map<String, String> data = new HashMap<String, String>(); Map<String, Object> data = new HashMap<>(); data.put("from", "userA"); data.put("to", "userB"); LOG.log("app", data, 0); return ok(index.render("Your new application is ready.")); } }
ちょっとハマってしまったのは…
Map<String, String> data = new HashMap<String, String>();
と書いていると以下のようなエラーとなりコンパイルに失敗してしまいました。
エラー: logに適切なメソッドが見つかりません(String,Map<String,String>) LOG.log("follow", data);
むむむ…こうなってしまうと Java 一年生の自分は積んでしまいそうになりましたが、ここのメソッドを見ると、以下のように書かれていたので Application.java の型の指定がミスマッチでコンパイルエラーになっていたようです。
public boolean log(String tag, String key, Object value, long timestamp) { Map<String, Object> data = new HashMap<String, Object>(); data.put(key, value); return log(tag, data, timestamp); }
ちなみに、fluent-logger を利用方法をまとめると以下のようになると思います。
- FluentLogger を初期化して Logger を生成
- 生成した Logger にログを HashMap 型にして log メソッドを利用して送る
- 初期化の際に tag や fluentd のホスト、ポートを指定することが出来る
- log メソッドでは tag の prefix と HashMap 化したレコードを指定する
ふう、やれやれ。
fluentd の起動
fluentd は以下のような設定で localhost にて稼働させておきます。
<source> type forward port 24224 bind 0.0.0.0 </source> <match app.app> type stdout </match>
動かしてみる
play run
play run
を実行すると fluent-logger も一緒にコンパイルされてアプリケーションが起動します。
play run
コンパイルに失敗する場合にはソースコードの見直し等を行いましょう。
ブラウザでアクセスしてみると…
ブラウザでアクセスする度に fluentd のログ(/var/log/td-agent/td-agent.log)には以下のようにレコードが記録されはじめました。
2014-11-03 00:16:31 +0900 app.app: {"to":"userB","from":"userA"} 2014-11-03 00:16:32 +0900 app.app: {"to":"userB","from":"userA"} 2014-11-03 00:16:34 +0900 app.app: {"to":"userB","from":"userA"}
おおっ。なんか嬉しい。
ちょっと作ってみた
リクエストヘッダ内の x-forwarded-for 等を取得して fluent-logger でログに出力するやつ。
private static final FluentLogger LOG = FluentLogger.getLogger("app","127.0.0.1", 24224); // public static Result ip() { ObjectNode result = Json.newObject(); Request req = play.mvc.Http.Context.current().request(); String x = req.getHeader("X-Forwarded-For") ; String p = req.getHeader("X-Forwarded-Proto") ; String ua = request().getHeader("User-Agent"); String r = request().remoteAddress(); // result.put("x-forwarded-for", x ); result.put("x-forwarded-prot", p ); result.put("user-agent", ua ); result.put("remote_address", r ); // Map<String, Object>; data = new HashMap<>(); data.put("x-forwarded-for", x ); data.put("x-forwarded-prot", p ); data.put("user-agent", ua ); data.put("remote_address", r ); // LOG.log("remote-test", data); return ok(result); }
お恥ずかしい限り…ベタ書きですいません。(もっと工夫のしようはあると思いますが取り敢えず)
これを play run してブラウザを叩くと…以下のように表示されます。
また、localhost の td-agent.log には以下のように記録されることを確認致しました。
2014-11-02 16:04:39 +0000 app.remote-test: {"x-forwarded-for":"xxx.xxx.xxx.xxx","remote_address":"xxx.xxx.xx.xx","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:33.0) Gecko/20100101 Firefox/33.0","x-forwarded-prot":"http"}
最後に
Play2 には標準で logback という logger が動いていますが、開発中や運用が始まった後でもログの収集、集計等は必要だと思うので fluent-logger を利用しておけば色々と嬉しいかなあなんて考えています。
今回は fluent-logger が動いたこと以外にも Play2 でモジュールを利用する際に build.sbt に記述しておけばモジュールを一括で管理出来て幸せになれることやライブラリ探すなら The Central Repository ってところがあるってことを知ってうおおおってなっています。
まだまだ勉強が必要なようです…。
元記事はこちらです。
「Play2 から fluent-logger を使うメモ」