Apacheアクセスログと私(日付を選べるようにする)

大体見てみたいものは作ったかな~と思うので,いよいよファイルを選べるようにする.

まず,modeが選択されてなかったら日付と処理モードを選ぶ初期画面を表示するのだ.最初,それを初期画面表示メソッドとしてinitというのを作っていたのだが,入力にエラーがあったときはエラー表示と共に,選んだモードなんかは保持した上でもう一度初期画面が出ると便利だなぁなどと考えていたら,条件やら何やらが面倒になってきた.

それと,「何月何日から何月何日までの範囲で入力してください」と表示させないとどこから見られるか分からないやと,過去ログファイルの表示に頭を悩ませる.Dir.entriesを使えるとラクなんだけど,セキュリティエラーがでるのでまた悩む.

どうにかロジックを組んで,画面を表示させていざ,過去ログ選んでみたら,おっとまたセキュリティエラー!!

そうなのである.外部からの入力を使ってのファイル操作はセーフレベル1ではできないのだ.だめだ,こればかりは他の方法って訳に行かない.

うんうん唸って,悩んだ挙句,怖かったけど,入力文字列をチェックした上で,禁断のuntaint.こうなると,上述の最古ファイル~最新ファイルを得るためのメソッドにDir.entriesを使わないのもなんだなぁと思い,結局,いろんなものを書き換えた.

前に書いた最新ログ取得メソッドlastlogは,

#最新のログファイル(圧縮なし)を取得するメソッド
def lastlog
  logfilename = Dir.entries(LOG_DIR).sort[-3]
  if /^access_log_\d{8}$/ =~ logfilename
    logfilename.untaint
    logfilename = LOG_DIR + logfilename
  end
  return logfilename
end

となった.

一番古いログファイルを得る方はしたがって,

#もっとも古いログファイルを取得する
def oldestlog
  logfilename = Dir.entries(LOG_DIR).sort[2]
  if /^access_log_\d{8}\.gz$/ =~ logfilename
    logfilename.untaint
    logfilename = LOG_DIR + logfilename
  end
  return logfilename
end

となる.

最新・最古ファイルが決まると初期画面が決められる.

#初期画面表示関数
def init(log="", date="", mode="")
  /(\d{4})(\d{2})(\d{2})/ =~ lastlog()
  last = [$1, $2, $3]
  /(\d{4})(\d{2})(\d{2})/ =~ oldestlog()
  old = [$1, $2, $3]
  print <<EOF
<form action="#{SCRIPT}" method="get">
<fieldset>
<legend>ログファイル選択</legend>
<p>#{old[0]}年#{old[1]}月#{old[2]}日~#{last[0]}年#{last[1]}月#{last[2]}日から選択</p>
EOF
if log == "last" || log == ""
  print <<EOF
<label><input type="radio" name="log" value="last" checked>最新</label>
<label><input type="radio" name="log" value="old">過去ログ</label>
<label><input type="text" name="date" size="10">(半角数字,YYYYMMDDという形式で入力すること)</label>
EOF
else
  print <<EOF
<label><input type="radio" name="log" value="last">最新</label>
<label><input type="radio" name="log" value="old" checked>過去ログ</label>
<label><input type="text" name="date" size="10" value="#{date}">(半角数字,YYYYMMDDという形式で入力すること)</label>
EOF
end
  print <<EOF
</fieldset>
<fieldset>
<legend>モード選択</legend>
EOF
  print '<label><input type="radio" name="mode" value="view"'
  print " checked" if mode == "view" || mode == ""
  print ">アクセスログ</label>\n"
  print '<label><input type="radio" name="mode" value="error"'
  print " checked" if mode == "error"
  print ">エラーログ</label>\n"
  print '<label><input type="radio" name="mode" value="trace"'
  print " checked" if mode == "trace"
  print ">サイト内遷移</label>\n"
  print '<label><input type="radio" name="mode" value="robot"'
  print " checked" if mode == "robot"
  print ">クローラー挙動</label>\n"
  print <<EOF
</fieldset>
<input type="submit" value="実行">
</form>
EOF
end

やっと部品ができたので,呼び出し側のメインプログラムを以下のように書き換える.

cgi = CGI.new
mode = cgi.params["mode"].to_s #モード取得
log = cgi.params["log"].to_s #最新ログかそうでないか
if mode == "" #もしモードがなかったら
  header_out("ログ/モード選択")
  init() #ファイル/モード選択画面
  footer_out()
else
  #モードがある場合,まず,表示するファイルをチェック
  if log == "last" #最新を選択していたら最新ファイルをセット
    file = lastlog()
  else #過去ログを選択している場合
    date = cgi.params["date"].to_s
    unless /^\d{8}$/ =~ date
      header_out("入力エラー")
      print "<p>半角数字で8桁(YYYYMMDDの形式)を入力してください.</p>\n"
      init(log, date, mode)
      footer_out()
      exit
    else
      date.untaint #8桁の数字だったら汚染を除去
    end
    file = "#{LOG_DIR}access_log_#{date}.gz"
    unless FileTest.exist?(file)
      header_out("ファイルがありません.")
      print "<p>日時の指定が範囲内か確認してください.</p>\n"
      init(date, mode)
      footer_out()
      exit
    end
    file = "|/usr/bin/gunzip -c #{file}"
  end
  case mode #モードがある場合の処理
    when "view"
      log_view(file) #生ログ表示
    when "error"
      errorlog_view(file) #エラーログ表示
    when "trace"
      trace_view(file) #サイト内遷移の様子を表示
    when "robot"
      trace_robot(file)
    else
      header_out("モードエラー")
      print "<p>有効なモードを取得できません.</p>"
      footer_out()
  end
end

いちおう,動いてるみたい.

じゃ,あとは,集計のことを考えるか.

日時: 2004年11月 4日 | PC/Web > Ruby |

コメントを投稿

(空欄でもかまいません)

(メールアドレスは管理人に通知されますが,Web上には表示されません)

Powered by Movable Type