情報科学概論
2000.7.4


  1. 本日の作業内容

  2. Apacheのログについて

    WWWサーバとはネットワーク上のホストからHTTPによるページの要求があった場合にそれに応えて、要求されたページデータを送信するサーバである。サーバプログラムとしては現在Apacheと呼ばれるフリーウェアがもっとも多く利用されている。今回はその記録(ログ)を解析し、どのページがどういう頻度で読まれているか統計的に処理するスクリプトを作成する。

    まず、Apacheが残すログであるが、RedHat系Linuxの場合、/var/log/httpd/access_logとして残されることになっている。このログはデフォルトでは日曜日の朝から取り始め、一週間経つと圧縮されてアーカイブとして別の名前で保存され、新規に新しい記録がaccess.logの名前で作られる。書式は以下のようになっている。

    xxx.90.209.98 - - [18/Jun/2000:04:02:33 +0900] "GET /~hoge/hore1.html HTTP/1.1" 200 12449
    xxx.90.209.98 - - [18/Jun/2000:04:02:35 +0900] "GET /~hoge/line4.gif HTTP/1.1" 200 1078
    xxx.90.209.98 - - [18/Jun/2000:04:03:46 +0900] "GET / HTTP/1.1" 200 1993
    xxx.90.209.98 - - [18/Jun/2000:04:03:48 +0900] "GET /logo.GIF HTTP/1.1" 200 2662
    xxx.90.209.98 - - [18/Jun/2000:04:04:05 +0900] "GET /~huga HTTP/1.1" 301 257
    xxx.90.209.98 - - [18/Jun/2000:04:04:06 +0900] "GET /~huga/ HTTP/1.1" 200 4953
    xxx.90.209.98 - - [18/Jun/2000:04:04:08 +0900] "GET /~huga/kabe6.gif HTTP/1.1" 200 816
    xxx.90.209.98 - - [18/Jun/2000:04:04:08 +0900] "GET /~huga/hero2.gif HTTP/1.1" 200 11434
    

    記録されているのは左からアクセスしてきた先のIPアドレス、日付、アクセスしたページデータ、となっており、右側の数字はHTTPに関するコード番号と転送したファイル容量である。このように、いつどこからどのページを見に来たかがわかるようになっている。このログを利用して例えば自分のページ全体に関してアクセス数の統計を取るような処理ができる。アクセスカウンターではカウンターをつけたページに関してはアクセス件数がわかるが自分のページ全体の記録はわからない。また、スクリプトを作れば特定の時間やアクセス元ごとの情報なども整理できる。

    上記のログを解析するに当たって注意するのは、ログの内容にはページに埋め込まれた画像を読みに来たログも記録されていることである。以下のスクリプトではそのような画像に対する要求を取り除く処理が含まれている。


  3. 今日の例題

    本日の例題は以下のようなスクリプトとなっている。

    #!/usr/bin/ruby
    
    j = 0
    k = 0
    add = 0
    
    ary_ip_address = [0]
    ary_hit_number = [0]
    
    while line = gets()
    
      if /(jpg|jpeg|JPG|JPEG|gif|GIF)\s+/ !~ line
    
        ary_log = line.split(/\s+/)
        ip_address = ary_log[0]
    
        if k == 0
          ary_ip_address[0] = ip_address
        end
    
        catch(:tag) do
          for i in 0..j
            if ip_address == ary_ip_address[i]
              add = 0
              ary_hit_number[i] += 1
              throw :tag
            else
              add = 1
            end
          end
        end
    
        if add == 1
          ary_ip_address << ip_address
          ary_hit_number << 1
        end
    
        k += 1
        j += add
    
      end
    
    end
    
    total = 0
    
    for i in 0..j
      total += ary_hit_number[i]
    end
    
    print "LOG result -- Total access was ", total, "\n"
    print "detail\n"
    
    for i in 0..j
      print ary_hit_number[i], " : ", ary_ip_address[i], "\n"
    end
    

    これを別途用意してあるログに対して行うことで解析結果を得る。例として用意したログは/virtual/home/tmp/access_logとして用意してあるのでそれをコピーして使うこと。コピーの方法は、

    $ cp /virtual/home/tmp/access_log .

    である。上のコマンドはコピーコマンドの第一引数としてファイル名を、第二引数として保存場所をとっている。保存場所は「.」、すなわち、カレントディレクトリ(現在作業をしている場所)である。

    次に、スクリプトの内容について説明する。

    1. 変数の準備

      後ほど使う予定の変数を3つ定義しておく。また、配列を2つ定義しておく。変数は複数のループに亘って使用するので、冒頭で定義する必要がある。また、配列は名前を見るとわかるが、ary_ip_addressがアクセスしてきたIPアドレスを格納する場所で、ary_hit_numberがアクセス回数を格納する場所となる。


    2. メインループ

      毎度おきまりであるが、whileとgets()を利用して、access_logの中身を1行ずつ読み込んで処理をするのが基本ループとなる。


    3. 行の選択

      上で述べたように画像データに対するログは不要であるのでそれを省くために、wwwで利用可能な画像形式であるJPEGとGIFに関して、それらを拡張子としたファイルに関する記述を省くようにする。


    4. ログの配列化

      lineとして読み込んだログの中身をスペースごとに区切って配列 (ary_log) の要素とする。このときに必要なのは配列の1番目の要素であるIPアドレスだけであるが、とりあえず配列としてすべてのデータを保存する。


    5. IPアドレスのカウント

      一番最初の行のアドレスをまずary_ip_address[0]に入れる。2度目のループ処理からは配列に格納されているIPアドレスの中に今読み込んだIPアドレスがあるかどうか探し、ある時には変数addを0、ヒット数を1増やして、ループから抜ける。( catchとthrowの機能を利用している。)また、今までに読み込んだIPアドレスに一致するものがなければ、add=1として次に進む。その後のif...endで今のIPアドレスを配列要素に追加するとともにそこからのアクセス数を1にしている。


    6. 統計処理

      次のforループで総アクセス数を計算する。また、各IPアドレスごとにそのアクセス数を表示する。このとき、アクセス数、IPアドレスの順番としている。理由は後で述べる。



    以上が、スクリプトの概略である。ところで、実行してみると確かにアクセス数とIPアドレスが表示されるが、順不同であり、例えばアクセスの多い順に表示して見やすくするというような処理は行われていない。Rubyの中でその処理を行うことも可能であるが、ここではUNIX的にコマンドを複数使用して処理を行う作業の練習をしてみることにする。


    アクセス数の多い順に表示させる方法

    UNIXには覚えておくと便利なコマンドがたくさんあるが、ここではsortを紹介する。sortは数字の順番や文字コードの順番にファイルの中身を並べ替えるコマンドである。詳しくは

    $ man sort

    として確認しておくこと。アクセス数は任意の桁の数字であるので、単純にsortしたのでは、2よりも10の方が「小さい数」として判断されてしまう。そこで、オプションとして-n(数字の文字列を数値として評価)をつける。また、数字の大きい順に並べるので-r(逆順)オプションも必要である。このようにsortコマンドを使用して並べ直すために、ログの解析結果をアクセス数が先に来るようにスクリプトを組んだ。

    結果として、以下のような操作によりアクセス数の多い順にIPアドレスを表示できる。

    $ ./log.rb access_log | sort -nr | less

    ここで、スクリプトのファイル名がlog.rb、ログがaccess_logとしている。上のコマンドは中間ファイルを作成すれば、パイプを使わないでも実行できる。最後のlessは結果が一度に表示できないので少しずつ表示させるために付けている。同様の操作は

    $ ./log.rb access_log > log.txt
    $ sort -nr log.txt > order.txt
    $ less order.txt

    としても可能であるが、中間ファイルをいちいち作らなくてすむのでパイプを利用した最初の方がスマートではある。


  4. 課題

    本日の課題はこのページに。


  5. 解答例

    次のページに6.6の課題に関する解答例を示す。


ページの先頭に戻る

目次ページに戻る