【皆で学ぼうシリーズ(1)】
「syslog を元に、メール配送の遅延がどのように分布しているかを調べるス
クリプト」をちょいちょいっと ruby で書いてみました。大規模メイリングリ
ストの配送速度を計るためのものです。時間帯別に、10分間隔ごとの積算頻度
を出力します。例えば
11: 22.0 39.5 58.8 76.4 92.1 97.8 100.0 ##av.mins:26.0026 ##messages:48
は、11時台において、10分以内に配送された割合が 22.0%、20分以内に配送さ
れた割合が 39.5% ということを意味します。
ソースを添付しますので、「ここはこんなふうに書いた方が ruby らしい」と
いった指摘を頂けますと勉強になりますです。
やっぱり他人のプログラムを眺めながら勉強するのが楽ですよね。私もそうし
ました。「これから ruby を」という方の参考にもなれば幸いです。
高木 浩光@名古屋工業大学
# 「もっと入門者向けの ruby-list に」キャンペーン実施中
----8<--------8<--------8<--------8<--------8<--------8<--------8<----
#! /usr/local/bin/ruby
class Syslog
def date; @date end
def time; @time end
def host; @host end
def process_name; @process_name end
def pid; @pid end
def message; @message end
MONTH = {
"Jan"=>1, "Feb"=>2, "Mar"=>3, "Apl"=>4, "Mar"=>5, "Jun"=>6,
"Jul"=>7, "Aug"=>8, "Sep"=>9, "Oct"=>10, "Nov"=>11, "Dec"=>12
}
def initialize(line)
line.chop
month, day, @time, @host, process, @message = \
line.scan(/^(\S+) +(\d+) ([:\d]+) (\S+) ([^:]+): (.*)$/)
return if not process
@process_name, @pid = process.scan(/^([^\[]+)\[(\d+)\]/)
@date = MONTH[month].to_s + "-" + day
end
def Syslog.create(line)
tmp = Syslog.new(line)
case tmp.process_name
when "sendmail"
return SyslogSendmail.create(line)
else
return tmp
end
end
end
class SyslogSendmail < Syslog
def id; @id end
def info; @info end
def extra_message; @extra_message end
def initialize(line)
super(line)
return if process_name != "sendmail"
if @message =~ /^\w\w\w\w\w\w\w\w:/
@id, @info = @message.scan(/^(\w\w\w\w\w\w\w\w): (.*)$/)
if @info =~ /^\w+=/
else
@extra_message = @info
end
else
@extra_message = @message
end
end
def SyslogSendmail.create(line)
tmp = SyslogSendmail.new(line)
case tmp.info
when /^to=/
return SyslogSendmailTo.create(line)
when /^from=/
return SyslogSendmailFrom.create(line)
else
return tmp
end
end
end
class SyslogSendmailTo < SyslogSendmail
def to; @to end
def delay; @delay end
def xdelay; @xdelay end
def relay; @relay end
def stat; @stat end
def initialize(line)
super(line)
return if @extra_message
return unless @info =~ /^to=/
@info.split(/,/).each {|arg|
field, param = arg.scan(/^ *([^=]+)=(.*)$/)
case field
when "to"
@to = param
when "delay"
@delay = timestr_to_secs(param)
when "xdelay"
@xdelay = timestr_to_secs(param)
when "stat"
@stat = param
end
}
end
def SyslogSendmailTo.create(line)
return new(line)
end
def timestr_to_secs(timestr)
if timestr =~ /\+/
days, time = timestr.split(/\+/)
tmp = days.to_i * 86400
else
time = timestr
tmp = 0
end
hours, mins, secs = time.split(/:/)
tmp += hours.to_i * 3600 + mins.to_i * 60 + secs.to_i
return tmp
end
private :timestr_to_secs
end
class SyslogSendmailFrom < SyslogSendmail
def from; @from end
def size; @size end
def msgid; @msgid end
def relay; @relay end
def initialize(line)
super(line)
return if @extra_message
return unless @info =~ /^from=/
@info.split(/,/).each {|arg|
field, param = arg.scan(/^ *([^=]+)=(.*)$/)
case field
when "from"
@from = param
when "size"
@size = param
when "msgid"
@msgid = param
when "relay"
@relay = param
else
end
}
end
def SyslogSendmailFrom.create(line)
return new(line)
end
end
def test(input)
while line = input.gets()
print Syslog.create(line).to_s + "\n"
end
end
##########################
class StatByMessage
def date; @date end
def time; @time end
def id; @id end
def delay_list; @delay_list end
def initialize(date, time, id)
@date, @time, @id = date, time, id
@delay_list = []
end
def add_delay(delay)
@delay_list << delay
end
def size; @delay_list.length end
def average
sum = 0.0;
@delay_list.each {|d|
sum += d
}
return sum / size / 60
end
end
class StatByHour
def hour; @hour end
def delay_list; @delay_list end
def initialize(hour)
@hour = hour
@delay_list = []
@num_of_msgs = 0
end
def add_delay_list(delay_list)
@delay_list.concat(delay_list)
@num_of_msgs += 1
end
def size; @delay_list.length end
def num_of_msgs; @num_of_msgs end
def average
sum = 0.0;
@delay_list.each {|d|
sum += d
}
return sum / size / 60
end
def freq
counts = [0]
bound_width = 10 * 60
upper_bound = bound_width
@delay_list.sort{|a, b| a - b}.each {|d|
while upper_bound <= d and upper_bound <= 3600 * 6
upper_bound += bound_width
counts << 0
end
counts[counts.length() -1] += 1
}
percents = []
total = 0
counts.each {|times|
total += times
percents << total * 1000 / size / 10.0
}
return percents
end
end
def stat_syslog_sendmail_to_delay(input)
id_table = Hash.new()
while line = input.gets()
r = SyslogSendmailTo.create(line)
next if r == nil
next unless r.stat =~ /^Sent/
if id_table[r.id]
id_table[r.id].add_delay(r.delay)
else
id_table[r.id] = StatByMessage.new(r.date, r.time, r.id)
STDERR.print r.id + ": " + r.time + "\n"
end
end
hour_table = Hash.new()
id_table.each_value {|s|
next if s.size < 10
hour, min, sec = s.time.scan(/^(..):(..):(..)$/)
if hour_table[hour]
hour_table[hour].add_delay_list(s.delay_list)
else
hour_table[hour] = StatByHour.new(hour)
end
}
hour_table.keys.sort.each {|key|
s = hour_table[key]
print key + ": "
print s.freq.join(" ")
print " ##av.mins:" + s.average.to_s
print " ##messages:" + s.num_of_msgs + "\n"
}
end
stat_syslog_sendmail_to_delay(STDIN)
---->8-------->8-------->8-------->8-------->8-------->8-------->8----