Webサーバを運営する上で、実際のアクセスがどれくらいあるのか、アクセスにどのような傾向があるのかを調べることは非常に重要です。
しかしながら、apacheのログはアクセスのあったURLや時間などの情報を都度保存するだけなので(まあそれがログなんですけど)、それだけではアクセスの傾向や分布等が把握しづらかったりします。
このページでは、perlスクリプトを利用してapacheのログを解析する方法について説明します。
apacheをデフォルトの状態で導入した際のログ出力(CustomLog)は、combined形式になっています。
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
このcombined形式のログを各要素ごとに分解するには、下記のような正規表現を利用します。
/^(.*) (.*) (.*) \[(.*)\] "(.*)" (.*) (.*) "(.*)" "(.*)"$/;
上記正規表現を利用し、ログファイルから各属性を読み込みます。
ログファイルのサイズにもよりますが、処理にはかなりの負荷がかかりますのでご注意ください。
open(LOG,"</var/log/httpd-access.log"); my(@REMOTE_HOST)=(); my(@FI)=(); my(@REMOTE_USER)=(); my(@DATE_TIME)=(); my(@METHOD)=(); my(@REQUEST_URL)=(); my(@HTTP_VERSION)=(); my(@HTTP_RESPONSE)=(); my(@DATA_BYTES)=(); my(@REFERER)=(); my(@USER_AGENT)=(); my($tLine)=0; while(<LOG>) { /^(.*) (.*) (.*) \[(.*)\] "(.*)" (.*) (.*) "(.*)" "(.*)"$/; $REMOTE_HOST[$tLine]=$1; $FI[$tLine]=$2; $REMOTE_USER[$tLine]=$3; $DATE_TIME[$tLine]=$4; ($METHOD[$tLine],$REQUEST_URL[$tLine],$HTTP_VERSION[$tLine])=split(/ /,$5); $HTTP_RESPONSE[$tLine]=$6; $DATA_BYTES[$tLine]=$7; $REFERER[$tLine]=$8; $USER_AGENT[$tLine]=$9; $tLine++; }
apache上でVirtualHost(SSLを運用している場合も含む)を動かしている場合、それぞれのVirtualHostでCustomLogを正しく設定していないと、ログファイルにcommon等のcombinecd以外の形式でログが出力されることがあります。
この場合、上記のperlスクリプトでは各属性が正しく格納されない場合がありますので、ログにはcombined以外の形式のログが出力されないよう設定してください。
LogFormat "%h %l %u %t \"%r\" %>s %b" common
上記の方法で格納した配列を利用して日ごとのアクセス数を集計するには、下記のような集計関数を利用します。
sub fold1 { my($aArray)=@_; my(%tSummery)=(); for(my($tCount1)=0;$tCount1<=($#$aArray);$tCount1++) { $tSummery{$$aArray[$tCount1]}++; } foreach(sort keys %tSummery) { my($tKey)=$_; my($tValue)=$tSummery{$tKey}; $tValue+=0; print $tKey.' : '.$tValue."\n"; } }
この関数は、参照で渡された配列のうち、重複する要素を畳み込んでカウントし、要素文字列の昇順で表示します。
以下のように呼び出して利用します。
&fold1(\@DATE_TIME);
この実行結果は下記の例のようになります。
01/Jan/2007 : 749 02/Jan/2007 : 772 03/Jan/2007 : 624 04/Jan/2007 : 870 05/Jan/2007 : 846 06/Jan/2007 : 744
URLやUser-Agent等を集計する際には、下記のような集計関数を利用します。
sub fold2 { my($aArray)=@_; my(%tSummery)=(); for(my($tCount1)=0;$tCount1<=($#$aArray);$tCount1++) { $tSummery{$$aArray[$tCount1]}++; } foreach(sort {$tSummery{$b}<=>$tSummery{$a}} keys %tSummery) { my($tKey)=$_; my($tValue)=$tSummery{$tKey}; $tValue+=0; print $tValue.' : '.$tKey."\n"; } }
この関数は、参照で渡された配列のうち、重複する要素を畳み込んでカウントし、カウント数の降順で表示します。(先ほどの「fold1」と違うのは、foreach文のソート条件と表示の個所のみです)
以下のように呼び出して利用します。
&fold2(\@USER_AGENT);
この実行結果は下記の例のようになります。
4422 : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322) 3993 : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.0.3705) 1812 : Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727) 1413 : Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp) 1135 : msnbot/1.0 (+http://search.msn.com/msnbot.htm)
クライアントから送信されるUser-Agentには、HTMLのタグが埋め込まれている可能性があります。
このため、集計結果をHTMLに吐き出す際には、XSS脆弱性を衝かれないよう、各文字列をサニタイズするようにしてください。
・perlのサンプルに存在していた、不正にエントリ数を間引いて表示してしまう不具合を修正。(連想配列を使って書き直しました、しらけんさんご指摘ありがとうございました!)
・perlのサンプルを「use strict」でも動作するよう修正。
・頂いたメッセージは管理者のチェックの後、公開されます。
・メッセージの公開を希望されない場合には、「このメッセージを非公開にする」にチェックを入れてください。
・管理者が不適切と判断したメッセージは公開しませんので、予めご了承ください。
まだ評価がありません |
表示できるメッセージはありません。