CLFのソート

私が管理しているウェブサーバは、外部からのHTTPリクエストを2台のサーバで受けて、ここからリバースプロクシをかけています。二つの独立したApacheが動作しているのでアクセス解析に使用するログファイルが2つできてしまいます。一般的なログ解析ツールでログを解析するためには、それぞれのファイルをcatしたのち、日付、時間順でソートしてやる必要があります。

以前のエントリPythonでちょっとしたスクリプトをかいたことがあったのですが、昨今おかげさまでウェブアクセスが増えてしまって一ヶ月のアクセスログが1.5億行にも。これだけでかいと、上記スクリプトだとメモリが足りんようです。

RDBMSに突っ込んでからソートするとかいろいろ考えましたが、結局日付単位に分割してからソートして、それを結合することにしました。日付単位に分割するってのがちょっと力技なんだけどこんなシェルスクリプト

2つのファイルをcatしたものが$PROGDIR/access_log.1にあると仮定しています。

awk -F: '{print $1}' $PROGDIR/access_log.1 | awk -F[ '{print $2}' | sort -u > $PROGDIR/date_tmp
paste $PROGDIR/date_tmp $PROGDIR/date_tmp > $PROGDIR/date_log

#
# create shell script to make daily log file.
#
sed -e '
{
s/\([^  ][^     ]*\)    \([^    ][^     ]*\)/grep \1 .\/mkstats\/access_log.1 >> \2/g
s/ \([0-9][0-9]\)\/\([a-zA-Z][a-zA-Z][a-zA-Z]\)\/\([0-9][0-9][0-9][0-9]\)$/ access_log.\3\2\1/
s/\([0-9][0-9][0-9][0-9]\)Jan/\101/
s/\([0-9][0-9][0-9][0-9]\)Feb/\102/
s/\([0-9][0-9][0-9][0-9]\)Mar/\103/
s/\([0-9][0-9][0-9][0-9]\)Apr/\104/
s/\([0-9][0-9][0-9][0-9]\)May/\105/
s/\([0-9][0-9][0-9][0-9]\)Jun/\106/
s/\([0-9][0-9][0-9][0-9]\)Jul/\107/
s/\([0-9][0-9][0-9][0-9]\)Aug/\108/
s/\([0-9][0-9][0-9][0-9]\)Sep/\109/
s/\([0-9][0-9][0-9][0-9]\)Oct/\110/
s/\([0-9][0-9][0-9][0-9]\)Nov/\111/
s/\([0-9][0-9][0-9][0-9]\)Dec/\112/
} ' $PROGDIR/date_log > $PROGDIR/datelog.sh

rm $PROGDIR/date_log
rm $PROGDIR/date_tmp

/bin/bash $PROGDIR/datelog.sh

#
# sort every daily log and concatinate them.
#

rm -fr $PROGDIR/access_log

for i in `ls $STATDIR/access_log.200*`
do
    $PROGDIR/sort_clf.py $i >> $PROGDIR/access_log
    rm $i
done

やってることは

  • CLFファイル全体から日付部分を切り出す(awkが二回呼ばれている行)。"01/Aug/2009"っての。これをdate_tmpってファイルへ保存
  • pasteでくっつける。
  • sedで作ってるのは、 grep '01/Aug/2009:' access_log.1 >> access_log.20090801 ってなコマンドが羅列されたシェルスクリプト


入っている日付分で切り出したあと、sort_clf.pyでソートってことです。sedスクリプトの半分くらいは日付単位のログファイルの名前を月名(AugとかAprとか)じゃなくて数字に直しているだけ。そうしないとfor文でまわしてsortしてcatするときに順番にならないですからね。


このスクリプトを作成したのは半年ほど前なんですが、なにをやってるのかを解読するのに一苦労。自分がつくったスクリプトなんですがねぇ。やっぱり正規表現って暗号なんだな。