時間軸でログをマッピングするRubyスクリプトサンプル

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)で割り、これを基底時間とする
  • あるログ情報から基底時間を計算しこれをキーとして、ハッシュにログデータを保存する
  • 開始時間から間隔ミリ秒を加算していって、ハッシュからデータを取り出し必要な情報を出力する

スクリーンショット 2017-01-11 14.05.03.png

Merit

  • 一度基底時間をキーにハッシュ作成するので、ログが時間順でソートされている必要がない
  • ハッシュ内データに頼らずに出力をおこなうので、ログが存在しない時間帯があっても抜けがおこらない

Demerit

  • 一度ハッシュに展開するのでメモリを消費する(interval_msを大きくすることでメモリ量をおさえるなどの対応はできる)