Blog の ログ -- 外部サーバ上の HTML に対するアクセスを,自サーバ上で記録する
このブログは Livedoor Blog を間借りしてるのですが,間借りなので Web サーバは当然 KLab の管理下には無く,そのため Web サーバの生ログも手軽には入手できません(有料メニューにはその機能はありますが,手軽とはいえません).Livedoor Blog の有料オプションにはログ集計機能も標準で付いているのですが,解析項目や期間が固定的なのでやっぱり独自にログ集計をしたくなってきました.ということで,この Blog に集計用のマーカを埋め込んでみました.
原理は簡単で,このページ内に KLab の管理下のサーバ上に置いたリソースを埋め込んでやっているだけです.こうすれば,例えばこのページにアクセスがあった場合,ブラウザはこのページを構成するリソースの一つとして,我々が埋め込んだファイルを取得しに来てくれます.そのアクセスを記録すれば,このページに対してアクセスがあったことが分かるわけです.ページ自体を自サーバでホストするのに比べれば,取得できる情報やアクセスに対する網羅性には限度がありますが,でも大概のアクセスに関してはログを取得できます.
- アクセス元ホスト
- ユニークユーザ数(再訪問率)
- アクセスしたページの URL
- そのページへのリンク元(いわゆる Referrer 情報)
1 は,我々の管理下のリソースされれば,普通に取得することができます.
2 は,一般的にクッキーを使ってトラッキングします.我々のサーバに対してリソースの要求があったときに,一緒にブラウザとの間でやりとりします.クッキーをやりとりするにはいろいろな方法がありますが,今回は Apache の mod_usertrack モジュールを使いました.
3 は,そのリソースにアクセスする際に,大概のブラウザは Referrer 情報として送信してくれます(我々が記録したいのはリソース自体のアクセスではなくて,そのリソースが埋め込まれたページへのアクセスなので,どのページへアクセスしたか=リソースに対する Referrer になります).
4 を取得するのは,一筋縄ではいきません.記事ページへの Referrer 情報を知っているのはブラウザだけですが,ブラウザからしてみればこのページ自体は我々のサーバとは無関係ですので,Referrer 情報を送る義理はありません.それを無理にでも送ってもらうために,Javascript を使って細工を施しました.つまり,記事ページ内に埋め込んだ我々のサーバ上のリソースにブラウザがアクセスする際に,Javascript を使ってそのリソースの URL に Referrer 情報を埋め込んでやってます.
実際に,記事ページに埋め込んだタグは次のようになっています.
<script type="text/javascript" language="javascript" src="http://log.blog.klab.org/counter.js"></script>
この <script> タグで指している Javascript ファイルは,今回の目的であるログ取得用のリソースではありません.我々のサーバにこのページの Referrer を送出するために,ログ取得用のリソースの URL に Referrer をくっつけたものを生成するためのコードが入っています.中身は次のようになっています.
document.write(
"<script src='http://log.blog.klab.org/log/dsas.js?" + document.referrer + "'></script>"
);
(実際にはこれだけではありませんが,コアの部分を抜き出すとこのようなコードです.)
このコードは,HTMLコード(<script> タグ)を吐き出します.Javascript が吐き出したコードは,その Javascript を記述した <script> タグの直後に,元々から挿入されていたかのように,ブラウザは解釈をします.この吐き出された <script> タグはその Javascript のソースファイルとして,先のとは別の Javascript ファイル(dsas.js = ログ取得用リソース)を参照しています.そしてその URL のパラメータとして,このページ自体の Referrer 情報(document.referrer)をくっつけています.こうすることで,ブラウザは dsas.js に対してアクセスしますが,その時に先程付加した Referrer 情報が URL の一部として我々のサーバに送られてきます.
更に,この dsas.js をやりとりする際に,クッキーを一緒に送受信しています.また,dsas.js 自体の Referrer 情報として,記事ページの URL が我々のサーバに送られてきます(これはブラウザが自主的に送ってくれます).
さて,実際にこのページのソースを見ると気づかれるかもしれませんが,初めの <script> タグの次の行に
<noscript><img width=1 height=1 src="http://log.blog.klab.org/log/dsas.gif"></noscript>
というものが入っています.これは,Javascript が動かないブラウザでアクセスされたときのための保険として入れています.Javascript が動かないブラウザ向けですので,リソースは Javascript ファイルではなく画像ファイル(GIF)にしています.この <img> タグは <noscript> タグで囲まれていますので,Javascript が動作しないブラウザでのみ解釈されます.width と height を指定しているのは,ブラウザの設定で画像表示を Off にしていた場合などに,見苦しくないようにするためです.それならば width も heigth も 0 にしてしまえば良いのでは,と思われるかもしれませんが,0 にしてしまうと(ブラウザが)そもそも表示する必要無しと判断して,dsas.gif にアクセスしないブラウザがあるので,その対策です.
肝心の dsas.gif や dsas.js へのアクセスを記録する我々のサーバ(Apache 2.0)の設定は,次のようになっています.
<VirtualHost *:80>
# 基本設定
ServerAdmin webmaster
ServerName log.blog.klab.org
DocumentRoot /home/bloglog/htdocs
# ログ出力の設定
LogFormat "%{%Y-%m-%d %H:%M:%S}t %h %{cookie}n %{Referer}i %>s %q \"%{User-Agent}i\" %U" bloglog
ErrorLog /var/log/httpd/bloglog.err
CustomLog /var/log/httpd/bloglog.acc bloglog
# クッキーの設定
CookieTracking on
CookieStyle Cookie
CookieExpires "30 days"
# ブラウザに対する,ファイルのキャッシュポリシーの支持
<Directory /home/bloglog/htdocs/log>
Header set Pragma no-cache
Header set Cache-Control no-cache
Header set Expires "Sun, 01 Jan 2006 00:00:00 UTC"
</Directory>
</VirtualHost>
肝心のログ出力の設定部では,クライアントの IP アドレス( %h ),送られてきたクッキーのデータ( %{cookie}n ),アクセスされたページの URL( %{Referer}i ),ステータスコード( %>s ),ページにアクセスしたときの Referrer ( %q ),ユーザエージェント( \"%{User-Agent}i\" ) 等を記録しています.それぞれの詳しい意味はこちらを参照してください.
ログ設定の次に,mod_usertrack の設定があります.特に決めなければいけないのは,クッキーの有効期限です.ここでは 30日に設定しています.
最後に,ある意味肝の設定を追加しています.即ち,dsas.js や dsas.gif がブラウザ等にキャッシュされないよう,指示しています.これがないと,ブラウザがあるページにアクセスして一度 dsas.js や dsas.gif を取得すると,その後一定期間はこれらに対するアクセスが発生しません.記事ページは複数ありますが,それらは全て同じ dsas.js や dsas.gif を見ています.ですので,ブラウザがこれらのリソースをキャッシュしてしまうと,A のページに対するアクセスは記録されるのに,その次に B のページにアクセスされてもそれは記録されません.これではあまり意味がありませんので,ブラウザにはいちいちこれらのファイルを取得しに来てもらうようにしてます.
逆に,counter.js に関しては毎回取得させる必要はありません.ですので,counter.js と dsas.js を設置するディレクトリを分離し,<Directory> ディレクティブを使って,dsas.js がある /home/bloglog/htdocs/log 下のファイルのみ,キャッシュされないように設定しています.