Treasure Data Analytics 第6回 〜Heroku Addon からアクションログを取得する〜
本記事は移転しました。新サイトにリダイレクトします。
はじめに
今回は Treasure Data が提供している Heroku Addon を利用して,任意のアクションログを取得する方法を紹介します。また,アクションログの取得については td-logger-ruby または td-logger-java を利用することによって Heroku 上のアプリケーションに限らず任意の Ruby および Java アプリケーションから同じようにアクションログを取得することができます。はじめにアクションログを取得することの有用性について触れ,その次に Heroku Addon の紹介およびアクションログを取得するまでの流れを説明します。
アクションログをフルに活用した解析は次回から始まる Social Gaming Analytics にて詳しく紹介します。
アクションログについて
つい最近までの Web サービスにおける解析というのは,アクセスログに基づく解析とほぼ同義でありました。アクセスログに基づく伝統ある解析は現在に至るまでに非常に多くのナレッジが蓄積されており,無料のツールも多々用意されていますので利用価値は現在でも非常に高いものです。
しかしアクションという枠組みで考えるならば,アクセスログとは『(あるページから)あるページへの「アクセス」』という 1 つのアクションのみから成るログに過ぎないことがわかります。
今までこの「ページ」という対象への「アクセス」というアクションのみのログで非常に多くの KPI が作られ,活用されてきた事は非常にすごい事だと思います。逆に言えばアクションのバリエーションを増やすことができ,かつそれらを用いた解析手法を精錬させることができれば,解析の秘める可能性は大きく拡がっていくことになります。
※ もちろん,アクセスログの中で他のアクション,いわゆる「ログイン」や「登録」といったログも参照できる場合がありますが,それはあくまでログインや登録を表す「ページ」が存在する場合に,そのページへの「アクセス」というアクションが「ログイン」として代替されているに過ぎません。
図1:「ユーザー」「ブック」というノードとそれらの関係が "Review" というアクションによって表現されている Book-Crossing Dataset の例。ノード・アクションの持つステータスに基づいてセグメントを作成し,その「切り口」からデータを俯瞰するのが前回のテーマでした。
図2:アクセスログもまた「ページ」というノードとそれらの関係が "Access" というアクションによって表現されることになります。また,アクセスログの他にページ毎にステータステーブルを持たせておくと有用です。コンバージョンページとなり得るか,などのフラグを複数持たせておくと解析に色々役立ちます。
Heroku-Addon Treasure Data Hadoop について
図3:Treasure Data の Heroku Addon は 2012/08/01 より一般公開されました。料金プランなどはリンク先をご確認下さい。
それでは上記のアクションログを容易に取得する方法について簡単に紹介します。Heroku Addon の導入に関しては他のアドオンと変わらず非常に簡単ですのでここでは省略します。こちらのドキュメントを参照下さい。第3回のチュートリアルではアカウント取得後,Treasure Data Toolbelrt をインストールするとコマンドラインから
$ td usage: td [options] COMMAND [args] ...
$ heroku plugins:install https://github.com/treasure-data/heroku-td.git $ heroku td usage: heroku td [options] COMMAND [args] ...
gem 'td', "~> 0.10.22"
と記述した上で
$ bundle install
しましょう。これによってあなたの Rails プロジェクト内で td-logger-ruby の各種メソッドが使えるようになります。
最も活用されるのは TD.event.post() コマンドです。このコマンドはアプリケーション内から発生したアクション(イベント)を補足し,引数で与えられた JSON 形式の構造化ログを Treasure Data Storage にアップロードするコマンドです。(実際には約 5 分に 1 回のインターバルでその間に収集されたログがまとめてアップロードされるようになっています。)
コード内の任意の箇所にこの TD.event.post() コマンドの 1 行を追記することによってあらゆるアクションログを取得することが可能になるのです。第一引数には "login" や "register" などのアクション名を,第二引数にはそのアクションのステータス情報を JSON 形式で与えます。
言い換えると,第一引数に与えたアクション名がテーブル名に対応し,そのテーブル内のレコードとして第二引数で与えた構造化データが蓄積されていく形になります。例えば "login" というアクションが起こったときに post するようにするのならば該当するコード箇所で
TD.event.post('login', {:uid=>123, :member_type=>'paying',...})
と記述します。"login" アクションに関しては最低限,ユーザーを特定するための :uid があれば十分ですが,ここでは :member_type (有料会員/無料会員/非登録会員)の識別もアクションステータスとして与えています。そうしておけば,会員タイプをセグメントとして login UU を数える場合にはこの "login" テーブルだけを参照して集計すれば良いことになります。
※ 第二引数で与える login に対するステータス情報は,後の集計に利用するかどうか関わらずできるだけ多い情報を含ませておく方が望ましいと僕は考えています。また,ステータス情報には post 毎に過不足があっても構いません。これに関しては,
- とりあえず利用可能な情報を併せて記録しておく
- レコードによって項目に過不足があっても構わない
という,いわゆるスキーマレスな NoSQL である MongoDB 的な思想と同じです。その後の集計の際にできるだけ JOIN せずに 1 つのテーブルで集計できてしまう方が効率は良いですし,必ずしも他のテーブルが最新に更新されているとも限りません。また,アプリケーションから集計結果を参照する場合には集計効率は重要になってきます。
話が少しそれてしまいましたが,post された "login" アクションログはデフォルトでは 'rails_production' という db 名に記録され,
$ heroku plugins:install https://github.com/treasure-data/heroku-td.git $ heroku td tables +------------------+--------+------+-------+--------+ | Database | Table | Type | Count | Schema | +------------------+--------+------+-------+--------+ | rails_production | login | log | 10 | | +------------------+--------+------+-------+--------+
として参照することができ,
$ heroku td table:tail rails_production login {"action":"login","uid":"123","member_type":"paying","time":1320857514}
として生ログにアクセスできます。レコード内の "action" と "time" は自動で付与されます。"time" に関しては post 時に自動で付与されますが,第二引数のステータスに :time キーとして付与しておけばそちらの値が優先して利用されます。
集計クエリも同じように,
$ heroku td query -w -d rails_production " SELECT v['member_type'] AS member_type, COUNT(distict v['uid']) AS cnt GROUP BY v['member_type'] ORDER BY cnt DESC "
などと実行することができます。
また, Rails アプリケーション内からクエリを実行→結果を取得→利用することももちろん可能で,その場合には
require 'td' require 'td-client' cln = TreasureData::Client.new(ENV['TREASURE_DATA_API_KEY']) job = cln.query('rails_production', 'SELECT member_type, COUNT(distict v['uid']) AS cnt GROUP BY member_type ORDER BY cnt DESC') until job.finished? sleep 2 job.update_status! end if job.success? job.result_each { |row| p row } end
などと記述します。バックグラウンドでは Hadoop が実行るので結果はすぐに返ってきませんので, job.finished? を定期的にジョブの終了を確認しながら待機することになります。
ただ集計処理自身は別スレッドで非同期に行われますのでアプリケーション自身に大きな負荷を与えることはありません。
以上より,クライアントからアドホックにクエリを実行するのではなく,アプリケーション側がバックグラウンドで定期的に集計ジョブを実行するようにしておき,結果を Postgres Addon に書き込むようにしておく(クライアントは Postgres に結果を参照する)使い方のが良いでしょう。
有用な活用方法として,例えば 第5回の「6. 同時参照されているブックペアに関する分析(共起分析)」 で紹介したように,検索キーワードや購入アイテムの共起度計算などを定期的に実行して更新しておくようにすれば,大規模データセットに基づくレコメンデーション機能を構築することも可能かもしれません。
最後に
"login" の他にも様々なアクションが考えられますが,ほとんどのアプリに利用できそうな代表的なものを最後に紹介しておきます。
TD.event.post('register', {:uid=>123,...}) # 登録 TD.event.post('pay', {:uid=>123, :category=>'gacha', :item_name=>'comp_gacha', :unit_price=>'300', 'count'=> 10,...}) # 課金 TD.event.post('first_access', {:uid=>123,:page_id=>3,...}) # 最初のページアクセス TD.event.post('search', {:uid=>123, :keyword=>['word1','word2'], :type=>'AND', ...}) # 検索 TD.event.post('follow', {:uid=>123, :to_uid=>456,...}) # フォロー TD.event.post('invite', {:uid=>123, :to_uid=>456,...}) # 招待 TD.event.post('step_tutorial', {:uid=>123, :step=>1,...}) # チュートリアルの進捗
あなたのテーブルには多種多様のアクションログが日々蓄積されることになります。
$ heroku td tables +------------------+---------------+------+-------+--------+ | Database | Table | Type | Count | Schema | +------------------+---------------+------+-------+--------+ | rails_production | login | log | 10 | | | rails_production | register | log | 10 | | | rails_production | pay | log | 10 | | | rails_production | first_access | log | 10 | | | rails_production | search | log | 10 | | | rails_production | follow | log | 10 | | | rails_production | invite | log | 10 | | | rails_production | step_tutorial | log | 10 | | +------------------+---------------+------+-------+--------+
どのようなアクションログ(とそのステータス)を取得し,複数のアクションをどう組み合わせて解析していくかはあなたの自由です。
次回からは Social Gaming Analytics シリーズとして,アクションログを最大限に活用した解析ユースケースを紹介します。
今日はここまで。