Goal
いつどの程度の量あるかわからないログデータを時間軸でプロットして、単位時間内の合計や平均を出力するスクリプトを作る.
Code
request.log
datetime,elapse,request_url
2016-12-16T14:00:00Z,123,/blogs
2016-12-16T14:00:00Z,124,/login
2016-12-16T14:00:00Z,30,/blogs
2016-12-16T14:00:00Z,862,/blogs/23456
csv_datetime_mapper.rb
#!/usr/bin/env ruby
require "time"
# max_datetime_count: 無限ループや不要なループをなくすため十分大きい数を設定している
max_datetime_count = 50000
# start_datetime: 時間軸プロット開始時間
start_datetime = nil
# interval_ms: 時間軸プロット間隔ミリ秒
interval_ms = 1000
map = {}
STDIN.each_line do |line|
# 入力形式
datetime, elapse, request_url = line.split(',')
unless start_datetime
start_datetime = datetime
end
t = Time.parse(datetime)
base_ms = ((t - Time.parse(start_datetime))*1000).div(interval_ms) * interval_ms
map[base_ms] = [] unless map[base_ms]
map[base_ms] << elapse.to_i
end
max_datetime_count.times do |i|
delta_ms = interval_ms * i
break if delta_ms > map.keys.max
current_ms = (Time.parse(start_datetime).to_i * 1000) + (interval_ms * i)
total_elapse = map[delta_ms] ? map[delta_ms].reduce(:+) : 0
request_count = map[delta_ms] ? map[delta_ms].length : 0
average_elapse = (request_count > 0) ? total_elapse / request_count : 0
datetime = Time.at(current_ms / 1000)
# 出力形式
puts "#{datetime},#{total_elapse},#{request_count},#{average_elapse}"
end
cat request.log | ruby csv_datetime_mapper.rb
# datetime,total_elapse,request_count,average_elapse
# 2016-12-16T14:00:00Z,32301,209,154
# 2016-12-16T14:01:00Z,33961,213,159
# 2016-12-16T14:02:00Z,35448,237,149
# 2016-12-16T14:03:00Z,34818,237,146
# 2016-12-16T14:04:00Z,34270,231,148
# 2016-12-16T14:05:00Z,36255,257,141
# 2016-12-16T14:06:00Z,35169,245,143
# 2016-12-16T14:07:00Z,32747,232,141
# ...
Behavior
- (ログ時間 - 開始時間)エポックタイム変換ミリ秒を間隔ミリ秒(interval_ms)で割り、これを基底時間とする
- あるログ情報から基底時間を計算しこれをキーとして、ハッシュにログデータを保存する
- 開始時間から間隔ミリ秒を加算していって、ハッシュからデータを取り出し必要な情報を出力する
Merit
- 一度基底時間をキーにハッシュ作成するので、ログが時間順でソートされている必要がない
- ハッシュ内データに頼らずに出力をおこなうので、ログが存在しない時間帯があっても抜けがおこらない
Demerit
- 一度ハッシュに展開するのでメモリを消費する(interval_msを大きくすることでメモリ量をおさえるなどの対応はできる)