概要
第一弾としてElasticSearch + LogStash の連携を紹介しました
第二弾ではlog4j2でJson形式のログを出力できるようにしました
そして今回第三弾としてlog4j2で出力したJsonログをLogStashで拾ってElasticSearchに投げるところまでやってみたいと思います
環境
- CentOS 6.6 64bit
- Java 1.8.0_25
- ElasticSearch 1.5.2
- LogStash 1.4.2
- log4j2 2.3
事前準備
ElasticSearch + LogStashのインストールおよび設定は第一弾の記事を参考に構築してください
log4j2のJSONLayoutの設定は第二弾を参考にコーディングしてください
今回は上記が完了している体で話を進めます
log4j2でファイルにJsonログを出力できるようにする
log4j2.xmlappenders
タグに以下を追加します
<RollingFile name="RollingFile" fileName="logs/rolling_app.json" filePattern="logs/rolling_app_%d{yyyy-MM-dd}.json">
<JSONLayout compact="true" eventEol="true" locationInfo="true">
</JSONLayout>
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
</RollingFile>
このAppenderはJsonのログを出力する専用のAppenderになります
日時でローテートするように設定しています
loggersには「RollingFile」という名前のappenderを参照するloggerを定義しましょう
<appender-ref ref="RollingFile" />
パッケージやログレベルは適宜調整してください
これを定義した状態で一度実行してみましょう
うまくいっていればプロジェクト配下に logs/rolling_app.json ができています
messageのフォーマットを決定する
今回はJava側でロギングするときにはスペースで区切らたデータをロギングするようにします
例えば以下のような感じで3つの項目をロギングするようにします
log.trace("kakakikikeke 20 Japan");
(この辺がだいぶいけてないのですが)上記のようにロギングすると「message」というフィールドの値としてJson出力されます
値は必ず文字列として出力されます
LogStash でJsonログをパースするconfigファイルを作成する
Java側が出力するJsonログをパースするconfigファイルは以下のような感じです
input {
file {
type => 'my-type'
path => '/path/to/log/rolling_app.json'
codec => json
}
}
filter {
date {
match => [ "timeMillis", "UNIX_MS" ]
target => "@timestamp"
locale => "ja"
timezone => "Asia/Tokyo"
}
grok {
match => [ "message", "%{GREEDYDATA:username} %{GREEDYDATA:age} %{GREEDYDATA:country} ]
}
}
output {
stdout {
codec => rubydebug
}
elasticsearch {
host => localhost
}
}
ポイントはfilter -> grok -> match
の部分です
Jsonロギングされたmessageフィールドの情報を正規表現を使ってマッチさせることができます
今回はスペースで区切られたフィールドをパースするだけなので正規表現は使っていません
「GREEDYDATA」の部分はパースした値の型を指定することができます
今回は全部 GREEDYDATA を指定していますが数字ならば「NUMBER」短い単語ならば「WORD」なども使うことができます
試していて「_grokparsefailure」のエラーになる場合は全部「GREEDYDATA」にしておけばOKかと思います
(もしかしたらちゃんと型を指定することで何かしらの恩知が受けれるかもしれません)
各種起動
ElasticSearch -> LogStash -> log4j2 の順番で起動していきます
ElasticSearchは特に設定ファイル等は何もいじっていないのでそのまま起動すればOKです
LogStashは作成した上記の設定ファイルを指定して起動しましょう
ElasticSearch と LogStash が起動できたらJsonロギングできるJavaアプリを起動させてJsonロギングさせてみましょう
LogStashは「rubydebug」しているのでログをうまくパースできればコンソール上に表示されると思います
最終的にはElasticSearchにちゃんとデータが入っているか確認したほうがいいでしょう
curl 'http://localhost:9200/_search?pretty'
とりあえずこれでデータが入っていればひと通りの連携は完了です
最後に
いけてないのはJsonロギングするJava側が必ず同じフォーマットでロギングしなければいけない点です
今回は3項目出力しましたが、これが4つとかになったらコードを改修しなければいけないですしLogStash側のconfigファイルも修正が必要です
ネットを調べているとmessageフィールドにJSONオブジェクトを出力させてLogStash側でRubyスクリプトを組んでログをパースする方法が紹介されていました
http://kapaski.github.io/blog/2014/07/24/logstash-to-parse-json-with-json-arrays-in-values/
これ自体もかなり力技なのですが、log4j2を使ってJson出力するとmessageフィールドが必ず文字列で出力されてしまいます
なので上記の方法すら使うことができません
またLogStash側のgrokもmessageフィールド以外をパースすることができない(?)みたいでlog4j2のThreadContextを使って別のフィールドにJsonオブジェクトを出力してもそれをパースすることができません
(ちょっとこの辺は調査不足かもしれませんが自分はできずに諦めました)
という理由もあって今回はスペースで区切った値をロギングをすることでそれをLogStash側でパースしたのですが、そもそもスペースで区切るならロギングするJava側もJsonで出力する必要はなかったんですよね
スペース区切りのロギングをしてそのファイルをLogStash側でパースするだけなので
わざわざJsonにして良かった点はLogStashのinputのcodecにjsonが使えたことくらいでしょうか