ども、cloudpackかっぱ (@inokara) です。

はじめに

Play2 Framework アプリケーションから fluentd にアプリケーションログを送る為に fluent-logger を利用してみたいと思います。


準備

環境

テスト用のアプリケーション作成

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 してブラウザを叩くと…以下のように表示されます。

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 を使うメモ