Apacheログ解析(Ruby)
2003-8-9

たけうちひとし
hitoshi@namaraii.com

処理の流れ66|if
66|if __FILE____FILE__ ==
== $0
$067|
67| include
include Report
Report68|
68| ip_addr
ip_addr == ARGV.shift
ARGV.shift
69|
69| access_log
access_log == AccessLog.new(ARGF)
AccessLog.new(ARGF)
70|
70| access_log.ip_addr
access_log.ip_addr == ip_addr
ip_addr71|
71| report(access_log.sort)report(access_log.sort)72|end72|endコマンドライン引数から解析対象のIPアドレスを取得(68) ARGF(69)

コマンドライン引数で指定された文字列をファイル名と解釈
 ひとかたまりの仮想的なIOオブジェクトとして扱える

指定されたIPアドレスの"アクセス日時"を抽出(71) "滞在時間"と"訪問回数"を算出(71)AccessLogクラス
3|class
3|class AccessLog
AccessLog
4|
4| include
include Enumerable
Enumerable
5|
5| attr_accessor
attr_accessor :ip_addr:ip_addr
7|
7| LogFormat
LogFormat == /¥A(¥S+)/¥A(¥S+) ¥S+¥S+ ¥S+¥S+ ¥[([^¥]]+)¥].*¥Z/¥[([^¥]]+)¥].*¥Z/
8|
8| DateFormat
DateFormat == %r!¥A(¥d¥d)/([A-Z][a-z][a-z])/(¥d¥d¥d¥d):(¥d¥d):(¥d¥d):(¥d¥d)!
%r!¥A(¥d¥d)/([A-Z][a-z][a-z])/(¥d¥d¥d¥d):(¥d¥d):(¥d¥d):(¥d¥d)!
9|== %w(Jan9| MonthMonth%w(Jan FebFeb Mar
Mar AprApr May
May JunJun Jul
Jul Aug
Aug Sep
Sep Oct
Oct NovNov Dec)
Dec)
10|
10|
11|
11| def
def initialize(f)initialize(f)12|12| @logfile@logfile == ff
13|
13| @ip_addr@ip_addr == ''''
14|
14| end
end
15|
15|
16|
16| def
def parse_time(time)parse_time(time)
17|
17| m
m == DateFormat.match(time)
DateFormat.match(time)18|
18| Time.local(m[3].to_i,Time.local(m[3].to_i, Month.index(m[2])+1,Month.index(m[2])+1, m[1].to_i,m[1].to_i, m[4].to_i,m[4].to_i, m[5].to_i,m[5].to_i, m[6].to_i)m[6].to_i)
19|
19| end
end
20|
20|
21|
21| def
def eacheach
22|
22| @logfile.each@logfile.each do
do |line||line|
23|m
23|m == LogFormat.match(line)
LogFormat.match(line)
24|
yield
24|
yield parse_time(m[2])parse_time(m[2]) if
if m
m and
and m[1]m[1] ==== @ip_addr@ip_addr
25|
25| endend
26|
26| endend
27|end27|endアクセス日時の抽出（イテレータ）
3|class AccessLog（略）
21| def each22|
@logfile.each do |line|
23|m = LogFormat.match(line)
24|
yield parse_time(m[2]) if m and m[1] == @ip_addr
25|
end
26| end27|endEnumerableモジュール
3|class AccessLog
AccessLog
4| include Enumerable（略）
27|end（略）
69| access_log = AccessLog.new(ARGF)
70| access_log.ip_addr = ip_addr
71| report(access_log.sort)

要素ごとの繰り返しを行うクラスのためのモジュール
 eachメソッドを定義してinclude
 collect, find, sortなどが追加される(Mix-in)レポート出力38|38| def
def report(log)report(log)
39|39| visits
visits == count
count == elapsed_time
elapsed_time == 00
40|
40| first_time
first_time == previous_time
previous_time == log.first
log.first
41|
41|
42|
42| log.each
log.each do
do |current_time||current_time|
43|
if
43|
if current_time
current_time -- previous_time
previous_time >> INTERVALINTERVAL
44|
elapsed_time
44|
elapsed_time +=+= output_log(first_time,output_log(first_time, previous_time,previous_time, count)
count)
45|
first_time
45|
first_time == previous_time
previous_time == current_time
current_time
46|
visits+=146|
visits += 147|
count
47|
count == 1148|
else
48|
else
49|
count
49|
count +=+= 11
50|
previous_time
50|
previous_time == current_time
current_time
51|
end
51|
end
52|
end
52| end53|
53| ifif first_time
first_time ==== previous_time
previous_time oror count
count >> 1154|
visits54|
visits +=+= 1155|
elapsed_time
55|
elapsed_time +=+= output_log(first_time,output_log(first_time, previous_time,previous_time, count)
count)
56|end56| end
57|
57|
58|
58| puts
puts <<EOS<<EOS
59|total:
59|total: #{elapsed_time.to_i}#{elapsed_time.to_i} sec
sec
60|visits:
60|visits: #{visits}#{visits}
61|average:
61|average: #{"%.2f"#{"%.2f" %% (elapsed_time/[visits,(elapsed_time/[visits, 1].max)}
1].max)} sec
sec
62|EOS
62|EOS63|
63| endendおわりにz

本格的なテキストフィルタから使い捨てのフィルタまで
zARGF

文字列操作
z 正規表現z