今週はサーバ構築が多くて疲れました。インフラ池田(@mikeda)です。
本番作業時や障害対応時、HTTP応答がどういう状況なのかリアルタイムで確認したいですよね。
でも
- muninは5分おきとかなので遅すぎる
- アクセスログをtail -fしてても速すぎて「なんか流れてるなー」としかわからない
というわけでなんかvmstatっぽく見れないかな、と思って作ってみました。
アクセスログをパイプで流し込むと、指定した秒数ごとにvmstatっぽく表示してくれます。
左からこういう情報が並んでます。
- 時間
- request/sec
- 平均応答速度
- アクセス数
- レスポンスコードごとのアクセス数
細かい調整は間にgrepとか挟む感じで。
ソースコードはこんな感じ。とりあえず版です。
#!/usr/local/bin/ruby
DEFAULT_INTERVAL = 3
class AccessCount
def initialize(interval)
@interval = interval
end
def start
@count = 0
@count_status = empty_count_status
@response_time = 0
print_header
[
Thread.new(&method(:watch)),
Thread.new(&method(:count))
].each{|t| t.join}
end
def print_header
puts " from - to req/sec avg(ms) count 2xx/3xx/4xx/#{red('5xx')}/other"
end
def flush_counter(from, to)
count, @count = @count, 0
response_time, @response_time = @response_time, 0
count_status, @count_status = @count_status, empty_count_status
rps = ( count / (to - from) ).round(2)
response_time_mean = count == 0 ? 0 : response_time / count
time_str = from.strftime('%H:%M:%S') + ' - ' + to.strftime('%H:%M:%S')
count_status['5xx'] = red(count_status['5xx'])
status_str = count_status.values.join('/')
if response_time_mean > 200
response_time_mean = red(response_time_mean)
elsif response_time_mean > 100
response_time_mean = yellow(response_time_mean)
end
puts [ time_str, rps, response_time_mean , count, status_str].join("\t")
end
def empty_count_status
{
'2xx' => 0,
'3xx' => 0,
'4xx' => 0,
'5xx' => 0,
'other' => 0
}
end
def watch
while true
now = Time.now
@last_checked ||= Time.now
sleep 0.1
if now - @last_checked >= @interval
flush_counter(@last_checked, now)
@last_checked = now
end
end
end
def count
ARGF.each_line do |line|
record = parse_line(line)
next unless record
@count += 1
@response_time += record['response_time'].to_i / 1000
if /(\d)\d\d/ =~ record['status']
@count_status["#{$1}xx"] += 1
else
@count_status["other"] += 1
end
end
end
def parse_line(line)
record = {}
# LTSV用
line.split("\t").each do |kv|
k, v = kv.split(":", 2)
record[k] = v
end
### combined用はたぶんこんな感じ
### LogFormatの末尾に%Dを追加してる想定
#regexp = /^(?<host>[^ ]*) [^ ]* [^ ]* \[(?<time>[^\]]*)\] "(?<path>(?:\\[\\\"]|.)*)" (?<status>[^ ]*) (?<size>[^ ]*) "(?<referer>(?:\\[\\\"]|.)*)" "(?<agent>(?:\\[\\\"]|.)*)"( (?<response_time>\d+))?$/
#return unless regexp.match(line)
#m = Regexp.last_match
#m.regexp.names.each{|name| record[name] = m[name]}
record
end
def red(str); "\e[31m#{str}\e[0m" end
def yellow(str); "\e[33m#{str}\e[0m" end
end
interval = ( ARGV.shift || DEFAULT_INTERVAL ).to_i
AccessCount.new(interval).start
まとめ
これはけっこう便利そうな気がする。
でもなんか似たようなのが有りそうな気がするので、もっとオシャレなツールがあれば教えて下さい!(あんま調べてないw)
