ruby を使う

最近、また ruby を使っています。自宅ではなく、派遣先でのことです。

やることはテキストファイルの加工なんで、別に、awk でも、perl でも、なんなら php でもいいでんですが、書きやすいという点ではやっぱり ruby が一番という気がしてます。

今やっているのは、httpd のログファイルの解析。
世の中には、httpd のログを解析する仕組みはいくらでもあるのですが、できるだけ元のデータ
が見たいなと思う。しかも、必要な箇所だけ表示したり、集計したりすると Excel に読めれば
ベスト、と思っているので、Excel に読めるようにCSVファイルになっていると都合がいい。

最初から httpd のログをそんな風に出力できれば問題ないのだろうが、なかなか勝手にいじる
訳にもいかない、となると、デフォルトの形式で出力されたログファイルを、Excel で読みやすいように加工しようか、ということに。

昔は、ruby でなんて考えられないくらい CPU が遅かったので、こういうのはちゃんとCで書いたものだ。しかし、最近は CPU が早いので ruby でもやれてしまう。いい時代になったな…

今回作成したのは、こんな感じのスクリプトです。

#!/usr/bin/ruby
#
def explode_word( string )
  start_p = @current_pos
  if start_p >= string.length then
    return false
  end
  key = string[start_p, 1]
  if key.eql?("[") then
    e_pos = string.index("]", start_p) +1
  elsif key.eql?("\"") then
    e_pos = string.index("\"", start_p+1) +1
  else
    e_pos = string.index(" ", start_p)
  end
  ret_s = string[start_p, (e_pos - start_p)]
  @current_pos = e_pos+1;
  return( ret_s )
end
#

filename = "access_log"
words = Array.new
logs_ip = Array.new
logs_date = Array.new
logs_hour = Array.new
logs_url = Array.new
logs_stat = Array.new
logs_agent = Array.new

if( fp = open( filename, "r") ) then
    fp.each_line do |line|
	tmp = line.chop
	@current_pos = 0
	i=0
	while( (words[i] = explode_word(tmp) ) != false ) do
	  i=i+1
	end
	date_s = words[3][1,11]
	hour_s = words[3][13,2]
	logs_date[log_count] = date_s
	logs_hour[log_count] = hour_s
# access url
	logs_url[log_count] = words[4]
# status ( ex. 200 401 404 )
	logs_stat[log_count] = words[5]
# access agent
	logs_agent[log_count] = words[8]
# 
	log_count = log_count +1

    end
    fp.close
end
#
# for status = 404
#
wfp = open("404.log", "w")
i=0
while( i < log_count ) do
  if logs_stat[i].to_i == 404 then
    wfp.write( logs_ip[i]+ "," )
    wfp.write( logs_date[i]+ "," )
    wfp.write( logs_hour[i]+ "," )
    wfp.write( logs_url[i]+ "," )
    wfp.write( logs_stat[i]+ "," )
    wfp.write( logs_agent[i]+ "\n" )
  end
  i=i+1
end

1行に出力されている各項目を切り出して、一旦配列に入れ、そこから Excel で使いたいデータにしてCSVで出力しています。当初はステータス毎にファイルに書き出そうかと思ったのですが、結局データが多すぎるので、404(ファイルが存在しない)に注目して今回は確認を行いました。それでも、結構出ています。

ブログの記事は、それぞれ担当者が書いており、テクのある人は自分でテーマを作ったりしている(作ってもらっている?)ようです。前任者はサーバーを設定すれば終わり、という管理者だったようで、抜けが結構ありました。まあ、自作したテーマのミス?というのもあり、この辺はログを見れる人でないと解らないかも、というのも。

サーバーが安定して稼動してくれればいいのですが、エラーがたくさん出ている状況は管理者としては気分がよくないし。整理できてよかったかも。

同じように error_log でも同じように作ってみました。

#!/usr/bin/ruby
#

def explode_word( string )
  start_p = @current_pos
  if start_p >= string.length then
    return false
  end
  key = string[start_p, 1]
  if key.eql?("[") then
    e_pos = string.index("]", start_p) +1
  else
    e_pos = string.length()
  end
  ret_s = string[start_p, (e_pos - start_p)]
  @current_pos = e_pos+1;
  return( ret_s )
end

filename = "blog.tuad.ac.jp-error_log.1"
#filename = "err1000.log"
words = Array.new
logs_date = Array.new
logs_stat = Array.new
logs_cli = Array.new
logs_msg = Array.new

@current_pos = 0
log_count = 0

if( fp = open( filename, "r" ) ) then
  fp.each_line do |line|
    tmp = line.chop
    @current_pos = 0
    i=0
    while( (words[i] = explode_word(tmp)) != false ) do
	i=i+1
    end

    logs_date[log_count] = words[0]
    logs_stat[log_count] = words[1]
    if words[3] == false then
      logs_cli[log_count] = "[-]"
      logs_msg[log_count] = words[2]
    else
      logs_cli[log_count] = words[2]
      logs_msg[log_count] = words[3]
    end
    log_count = log_count +1
  end
  fp.close
end

if log_count > 0 then
  wfh = open("err.log", "w")
  i=0
  while( i < log_count ) do
    wfh.write( logs_date[i].to_s + "," )
    wfh.write( logs_stat[i].to_s + "," )
    wfh.write( logs_cli[i].to_s + "," )
    wfh.write( logs_msg[i].to_s + "\n" )
    i=i+1
  end
end

こういうので調べると実は知らなかったことがあり、今時のサーバー管理が学べます。
来週、今回手を入れた箇所がちゃんと動いているのを確認する予定。
そこで、また、何か発見があるかも、とちょっとワクワクしてます。