背景
gumi で提供しているソーシャルゲームは, サーバ/クライアント共に様々なログを Fluentd 経由で各種データストアに入れており, 適宜, 解析を行っている.
ログの種別により, Schema-full or Schema-less を選択しているが, 本記事では Schema-less なログの取り扱いを話題とする.
Schema-less なログの必要性
サーバ/クライアントの開発時から運用時まで, 常に次のような要望がある.
「気軽に適当なフォーマットでログを出力したい」
Schema-full なデータストアを採用する場合, 開発者に次のルールを強いる必要がある.
- ログ出力前にテーブルのスキーマを定義せよ
- ログ出力中にテーブルのスキーマを変更するなら, 別テーブルにせよ
これらのルールを開発者が守らない場合, Fluentd 側でエラーが発生するため, 開発者はインフラ担当のヘイトを稼ぐことになる. そして, インフラ担当に怒られた開発者は, 別の手段を模索し始め, 各プロジェクトごとに別々の仕組みが構築される.
よって, Schema-less なデータストアを使用し, Schema-less なログを出力する必要がある.
データストアの選定
Schema-less なデータストアを選定する場合, 筆者は, まず始めに MongoDB や Elasticsearch を候補にあげる. その後, Schema-full だが, データ取得時にフィールドに格納されている JSON フォーマット文字列から必要な情報を部分的に抜き取れる Redshift や BigQuery を連想する.
それぞれのデータストアに得手不得手があるため, gumi では, 適宜, 用途により使い分けている(一つのログを複数の用途で使用するため, ログの複製を複数のデータストアに保存することが多い).
今回の要件は, 「気軽に適当なフォーマットのログを出力したい」 であるため, 次の観点から BigQuery を採用した.
- SQL なら書ける人が多い
- ディスクスペースを考慮する必要がない
- JSON 関連の関数の応答速度が速い
- ModeAnalytics などで楽にグラフを作成できる
蛇足だが, 前述した他のデータストアを, gumi では次の用途で使用している.
- MongoDB - 行動ログを入れ, カスタマーサポート時に使用
- ElasticSearch - 自由入力文字列を入れ, 伏字処理や特定単語の検索に使用
- Redshift - 検索頻度は高いが, 年単位で遡る事がない場合, BigQuery の代わりに使用
- S3 & Glacier - 蛇足の蛇足. あらゆる全てのログのバックアップに使用
その他の前提条件
サーバ/クライアントから何らかの経路を辿り Fluentd へログが渡されていることを前提条件とする.
ログの取り扱い
ログを JSON フォーマット文字列にシリアライズする
前述した通り, BigQuery は Schema-full なデータストアであるため, ログを JSON フォーマット文字列に変換し, 一つのフィールドに入れる必要がある.
gumi では, ログを JSON フォーマット文字列にシリアライズする Fluentd Plugin fluent-plugin-record-serializer を作成し, 運用している.
Plugin をインストール後, Fleuntd に次のような設定を追加し,
<filter pattern>
type record_serializer
</filter>
次の様なログを送ると,
{
"spam": "ham",
"egg": "cheese"
}
次の様にシリアライズされる.
{
"tag": "pattern",
"payload": "{\"spam\": \"ham\", \"egg\": \"cheese\"}"
}
ちなみに, fluent-plugin-record-serializer は Oj をインストールすると CPU 負荷が減る.
BigQuery への保存
Fleuntd から BigQuery にログを入れるため, fluent-plugin-bigquery を使用する.
次の様な本末転倒な Schema ファイルを設置し,
[
{
"name": "tag",
"type": "STRING"
},
{
"name": "time",
"type": "INTEGER"
},
{
"name": "payload",
"type": "STRING"
}
]
Fleuntd に次のような設定を追加すると,
<match pattern>
type bigquery
method insert
auto_create_table true
schema_path /path/to/schemaless_schema.json
# ..snip..
</match>
指定したプロジェクトのテーブルに, Schema-less なログが入る.
ログの確認
JSON_EXTRACT 関数を使用し, 必要なログを取得する.
SELECT JSON_EXTRACT(payload, '$.spam') as spam
FROM [pattern_20151204]
WHERE JSON_EXTRACT(payload, '$.egg') = 'cheese'
まとめ
SELECT や WHERE 句に payload を指定すると, 非常にサイズの大きいフィールドを扱うため, BigQuery 破産の可能性が上昇する. ご利用は計画的に.