ようやく、ハッシュの使い方とソートの仕方を覚えたので、データの出現頻度が高い順にソートして出力するブログラムに改良しました。
#!/usr/bin/perl -w
use strict;
# ログファイルの整理プログラム log.pl [-p|r|h|P|H] ver.1.2
# 入力するログファイルのフォーマット
# host ident authuser date "request" status bytes "referer"
# "user-agent"\n
# date = [day/month/year:hour:minute:second zone]
# day 2桁, month 3文字, year 4桁, hour 2桁, minute 2桁, second 2桁
# zone (+|-)4桁
# request = command pass HTTP/*.*
# status 3桁
# 出力オプション
# p アクセス先を回数でソート(標準)
# ********** 999999 pass(前半40文字)
# r アクセス元を回数でソート
# ********** 999999 referer(前半40文字)
# h アクセス者を回数でソート
# ********** 999999 host(後半40文字)
# P クライアントエラーを出したアクセス先を回数でソート
# ********** 999999 pass(前半40文字)
# H クライアントエラーを出したアクセス者を回数でソート
# ********** 999999 host(後半40文字)
# 回数をカウントするリクエストのパスの末尾(-p|r|hの場合に有効)
my @count = qw( / .html .xml .rdf );
my %counter = ();
my $option = '-p';
if (@ARGV) {
if ($ARGV[0] =~ /^-[prhPH]/) {
$option = shift @ARGV;
}
}
foreach (<STDIN>) {
/^(\S+).+\[\S+ \S+\] "([^"]+)" (\d{3}) .+ "([^"]+)" "[^"]+"\n/;
my $host = $1;
my $request = $2;
my $status = $3;
my $referer = $4;
my $key = '';
my $out = 0;
if ($option =~ /[prh]/) { # -p|r|hの場合
foreach (@count) {
if (($request =~ /\S+ .*$_ \S+$/) &&
($status == 200)) {
$out = 1;
last;
}
}
} elsif ($option =~ /[PH]/) { # -P|Hの場合
if ($status =~ /4\d\d/) {
$out = 1;
}
}
if ($out == 1) {
if ($option =~ /[pP]/) { # -p|Pの場合
$request =~ /^\S+ (\S+) \S+$/;
$key = $1;
} elsif ($option =~ /r/) { # -rの場合
$key = $referer;
} elsif ($option =~ /[hH]/) { # -h|Hの場合
$key = $host;
}
if (exists $counter{$key}) {
$counter{$key}++; # キーがあればカウントアップ
} else {
$counter{$key} = 1; # キーがなければ作成
}
}
}
my @list = sort { # カウントの大きい順にソート
$counter{$b} <=> $counter{$a} or
$a cmp $b # カウントが同じ場合はキーでソート
} keys %counter;
foreach (@list) { # 出力
my $star = '*' x int $counter{$_} / $counter{$list[0]} * 10 + 0.5;
my $access = $_;
if ($option =~ /[hH]/) { # アクセス者の場合は後半40文字
$access = substr($_, -40, 40);
}
printf "%10s %6d %-.40s\n", $star, $counter{$_}, $access;
}
C'S SERVER Personalのログファイルは、日毎にzipで圧縮されているので、zcat
の出力をパイプで入力すると便利です。
7月のログファイルを入力してみました
なお、このプログラムのソートでは、出現頻度が同じ場合はキーの文字列を先頭からソートしています。本来、ソートの対象がアクセス者(host)の場合は、ドット(.)区切りのワード毎に後ろからソートすべきだと思いますが、使用上問題がないので簡略化しています。(一言でいうと手抜きですね...(^_^;)