ローカルの Mac に td-agent ( Fluentd ) を入れて、Amazon Athena から SQL ライクにログを抽出するまで。


今いま、あるところに

Rails のプロジェクトがありました。

そこでは、lograge という gem をいれて、/log/development.log に欲しいログ情報を JSON 形式で送りつけていました。

/config/initializers/lograge.rb
Rails.application.configure do
  config.lograge.enabled = true
  config.lograge.formatter = Lograge::Formatters::Json.new
  config.lograge.custom_options = lambda do |event|
    {
      user_id: event.payload[:user_id],
      user_email: event.payload[:user_email],
    }
  end
end
/log/development.log
# ...イメージ
{"method":"GET","path":"/dashboard/events","format":"json","controller":"DashboardController","action":"events","status":200,"duration":68.21,"view":0.44,"db":0.0,"user_id":"9999","user_email":"[email protected]"}

ここに td-agent を入れて、ログを S3 に送り、Amazon Athena から SQL ライクにログを抽出するということをやりたいと思います。

最終的なイメージ

まず td-agent (Fluentd) を入れるところから

Installing Fluentd using .dmg Installer (MacOS X) ここに書いてあるとおりに、Download して、td-agent を入れます。これはすぐにスルっと入ると思います。

つぎに、fluent-plugin-s3 というものを入れたいのですが、これが難しかったです。

インストール
/opt/td-agent/usr/sbin/td-agent-gem install fluent-plugin-s3

結論、上のコマンドでいけるのですが、...とにかく行けます。

ref. http://docs.fluentd.org/v0.12/articles/faq#i-installed-td-agent-and-want-to-add-custom-plugins-how-do-i-do-it

(自分用のメモ。以下のファイルから /opt/td-agent/usr/sbin/td-agent の場所を確認。)

/Library/LaunchDaemons/td-agent.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>KeepAlive</key>
  <true/>
  <key>Label</key>
  <string>td-agent</string>
  <key>ProgramArguments</key>
  <array>
    <string>/opt/td-agent/usr/sbin/td-agent</string>
    <string>--log</string>
    <string>/var/log/td-agent/td-agent.log</string>
    <string>--use-v1-config</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>WorkingDirectory</key>
  <string>/opt/td-agent</string>
</dict>
</plist>

つづいて設定ファイルを書きます。

いろいろ学びながら(ハマりながら)、完成したのが下です。(一応これで、やりたいことはできました。)

/etc/td-agent/td-agent.conf
<source>
  type forward
</source>

<source>
  type     tail
  tag      lograge
  format   json
  path     {project-path}/log/development.log
  pos_file {project-path}/log/development.log.pos
  time_key time
</source>

<match lograge>
  @type copy
  <store>
    @type stdout
  </store>
  <store>
    @type s3
    format json
    aws_key_id {aws_access_key_id}
    aws_sec_key {aws_secret_access_key}
    s3_bucket {my_bucket_name}
    s3_region ap-northeast-1
    path lograge_logs/ # 任意に指定できます
    buffer_path /var/log/fluent/lograge_s3 # 任意に指定できます
    buffer_chunk_limit 100k
    time_slice_format year=%Y/month=%m/day=%d/%Y%m%d-%H
    utc
  </store>
</match>

ポイント説明

  • @type stdout で、/var/log/td-agent/td-agent.log にログが流れてくるので、これを tail -f しながら作業を進めました。
  • format json で JSON形式で(余計な日時データなどを含まない形で)吐き出してくれます。
  • @type copy で複数のアウトプットをしてくれます。( stdout と s3 への吐き出し)

@type copy の存在に出会うまで、ぼくは一向に現れないログを永遠と 待ち続けていました。

(こんな風に書いていたわけです。)
<match lograge>
  @type stdout
</match lograge>
<match lograge>
  @type s3
  省略
</match>

td-agent を動かしておく

動かす
sudo launchctl load /Library/LaunchDaemons/td-agent.plist
止める
sudo launchctl unload /Library/LaunchDaemons/td-agent.plist

設定ファイル td-agent.conf をいじったら、上のコマンドで "止めて" ・ "動かして" 反映させます。reload みたいなコマンドないのかなぁとおもいつつ、&& でつなげてお手製リロードコマンドを何度も叩きました。

以上で、/log/development.log の JSON 形式のログが、S3://lograge にアップされるようになりました。

awscliでの確認
$ aws s3 ls --recursive  s3://my-bucket-name
2017-02-02 17:10:27        270 lograge_logs/year=2017/month=02/day=02/20170202-07_0.gz
2017-02-02 18:10:58       2580 lograge_logs/year=2017/month=02/day=02/20170202-08_0.gz
2017-02-02 19:10:31        757 lograge_logs/year=2017/month=02/day=02/20170202-09_0.gz

Amazon Athena の設定をする

クエリがかけるので、create database hoge などでデータベースをつくります。

そして、テーブルもクエリでつくれるのですが、それは大変そうなので、Catalog Manager メニューからつくります。

道なりにすすめば、ゴールにつきます。ポイントはフォーマを JSON にしておくことです。これで、正規表現をゴリゴリ書かなくてすみます。隣の席の CTO が、正規表現にすごく時間がかかったとおっしゃっていましたので、これを避けることができてよかったです。 (どうも単純な正規表現ではなく、癖があり、\\d などと書かなくてはいけないようです。)

年・月・日でパーティションを切っていますので、以下のコマンドで反映させます。(ちょっとここ勉強不足で詳細わかっていません。)

MSCK REPAIR TABLE database_name.table_name;

# 確認
show partitions database_name.table_name;

あとはクエリを叩くだけです

一番最初のキャプチャの通りです。select * from table_name; これで、みたいデータがとれます。

以上です。

途中で、いろいろハマりましたが、無事にやりたいところまでできてよかったです。今日はもうサウナに入って体を整えて でも飲みたいと思います。

明日以降は、これらを itamae とかで、リモート構築できるようにしなければなりません。

ありがとうございました。