ほわいとぼーど

ぷろぐらまのメモ帳

Kibana向けFluentd設定例①アクセスログ可視化

どちらかというとFluentdの設定の話なのですが、
Kibana上でデータを扱うことも考慮して、くらいの感じで。

アクセスログの可視化はよくあるやつなので細かい所は飛ばします。

1段目:ログ収集先からの転送設定

<source>
  type     tail
  path     /path/to/apache_access.log
  format   /(?<message>.*)/
  pos_file /var/log/td-agent/apache_access.pos
  tag      apache.access.HOSTNAME
</source>

<match **>
  type           forward
  flush_interval 1s
  <server>
    host         FORWARD_HOST
  </server>
</match>

以前の記事ではin_tailにパースを書いていましたが、
パラメータが増減するのに毎回対応したり、パースするための負荷がかかることを考えると、
ログ収集ホスト上のin_tailで正規表現をゴリゴリ書くのはあまり得策じゃない気がしています。
そこで1行を1メッセージとして送るような設定にしています。
fluentdが0.10.39以降であればformat noneとすることで同様のことが出来ます。
収集ログに日付が付与する場合はin_tailの変わりにfluent-plugin-tail-exを使うといいですね。


2段目:ログ受信側パース

<source>
  type forward
</source>

<match apache.access.**>
  type        parser
  add_prefix  parsed
  format      /(正規表現)/
  time_format %d/%b/%Y:%H:%M:%S %z
  key_name    message
</match>

ここからはログ解析サーバ上です。
forwardされたイベントをfluent-plugin-parserでパースします。
key_nameには解析したいフィールド名、ここではmessageになります。
formatには正規表現を書きますが、ログフォーマット側でLTSVにしておけば
format ltsvするだけでよくて楽ですね。


3段目:データ加工

<match parsed.apache.access.**>
  type         record_reformer
  output_tag   reformed.${tags[1]}.${tags[2]}
  
  ip           ${forwardedfor[/([^,]*),*/,1]}
  method       ${req[/^([^ ]*) .*$/,1]}
  uri          ${req[/^[^ ]* ([^ ]*) .*$/,1]}
  sessionId    ${req[/^[^ ]* \/path\/to\/api\?sessinId\=(.*?)(\&param\=[^ ]*)? .*$/,1]}
  log_host     ${tags[3]}
</match>

2段目の時点で生のデータを扱う分には十分なのですが、
時にはデータ加工したい場合があり、fluent-plugin-record-reformerが便利です。
整形したデータを生のレコードに+αしてくれるのが良いです。
しかも複数項目書けます。
例えば「req (referer情報)」に「GET /path/to/api?sessionId=10000001&param=1 HTTP/1.0」
という情報が入っているとして、1つ目をmethod、2つ目の部分をuriとして取り出しています。
あるいはuri全体ではなく、getパラメータに入っているsessionIdを取り出したりします。
またforwardedforにはproxyを通すとカンマ区切りで複数のアドレスが入っているので、
1番先頭だけを取り出して続くgeoipの判定に使います。
整形は${フィールド名[/(正規表現)/,n]}のように書いて取り出したい部分をグループ化して
nで何番目のグループを取り出したいかを指定します。
この辺りはrubyの正規表現のとり扱いになるので、
rubyのコンソールとかで少し試してみればすぐわかると思います。

log_hostは以前の記事でfluent-plugin-elasticsearch上でtag_key @log_nameで行っていた
収集元ホストの登録をここで行うように変更したものです。
fluent-plugin-elasticsearchのtag_keyだとtagをかなりうまいこと編集する必要がありましたが、
tags使うと簡単に必要な部分だけ取り出せますね。
${tags[3]}はtagをカンマで分割した4番目を取得しています。
なお、fluent-plugin-record-reformer 0.1.1からはfluent-plugin-forestと表記を合わせて
tag_partsが使えるようになります。

今回の記事のポイントはほぼ3段目に集約されます。
methodやuriはログフォーマットで直接指定すれば加工しなくても済みますが、
既に運用されているような場合に出力元に変更を加えるのが簡単じゃない場合も多いし、
そういう場合に受け取った先で簡単に加工できるという例です。


4段目:

<match reformed.**>
  type                    geoip
  geoip_lookup_key        ip
  enable_key_country_code geoip_country
  add_tag_prefix          es.
</match>

fluent-plugin-geoip用の設定。
pluginとは別にライブラリのインストールが必要なので注意してください。

ここまで見てきてplugin毎にtagの扱い方が異なるので若干混乱しますね。


5段目:

<match es.reformed.**>
  type              forest
  subtype           elasticsearch
  remove_prefix     es.reformed
  <template>
    type_name       ${tag}
    host            localhost
    port            9200
    logstash_format true
    logstash_prefix logstash
    flush_interval  10s
  </template>
</match>

fluent-plugin-elasticsearchはplaceholderに対応してないのでfluent-plugin-forest推奨。
fluent-plugin-record-reformerでのoutput_tagとの組み合わせにより
type_nameに入る値は今回の例では「apache.access」となります。
以前はtype_name、host名なんかを登録するのにtag操作が結構大変でしたが、
tagのplaceholderが便利になったお陰でかなり楽になりました。


fluentdではこんな感じでログ出力をいい感じに加工してデータストアに投入できます。
Kibana関係ないじゃないかって言われそうですが、Kibanaで表示する内容を考えた結果、
fluent-plugin-record-reformerで途中で加工するのが便利っていう話です。
Kibanaで解析するときにユーザIDやセッションIDみたいなのをfacet集計したい事例はよくあると思うので、
切り出しておいてmappingをnot_analyzedで用意するとランキング集計しやすいですね。
Kibana上ではtermsパネルで対象項目を指定するだけで表示できます。
デフォルトで5項目になってるけど数字を増やせばもっと表示可能。
(1000くらいにはしたことある)

f:id:a3no:20131215090919j:plain
参考例。本当はもっとランキングぽいの出したかったけど手元に出せるデータがなく。


3つ例を書くつもりだったけど、調査修正して動確してたらハマって時間かかってしまった。
record_reformerは前に設定書いた時点ではtag_parts使えてたんだけど、
手元で実施したら0.1.1ではなく0.1.0が入ってしまって使えなかったり、
Elasticsearch-1.0.0.Beta2を使ったら0.90.7で動いてたmapping templateが動かなかった。
記事は書いたものの色々と調査するものが増えてしまった・・・