前回の日記 でログの保存方法をテキストファイルで行い、一覧表時用に集計したファイルを別途作成する事にしました。

しかし、テストしていて感じたのが「テキストファイルで集計するのは困難だ」と言う事です。
単に集計した値を追加するだけならテキストファイルでも良いのですが、「元の値を変更」する可能性があります。

特に「表示ファイル」や「ユーザーエージェント」などは、利用者も多いです。毎日加算すべき内容です。


しかし、テキストファイルで更新・編集を実現するには一筋縄ではいきません。
以下のような複雑な手順・考え方が必要になります。

○元ファイルを読み込み
○ファイル内に追加するデータ(文字列)が存在するか調べる
 └存在する場合  → 元ファイルから1行ずつ取り出して、一致するかIFで比較
                一致する場合は、値の部分を取り出す。
                そして加算する値を加えて文字列を結合し、置き換える事で変更※
 └存在しない場合 → 元ファイルを読み込んで最後の行に追加

※一致した行が以下の場合

 $line には「localhost,5」が入っているとする。意味としてはlocalhostというホスト名のユーザが5回アクセスした

$ip_line = explode(",",$line); // カンマで分割
$ip_new = $ip_line[0].",".$ip_line[1]+$num; // $numは加算する値。例として5が代入されているとする
$str = str_replace($line,$ip_new,$file); // $lineを$ip_newに置換

と言う事を行えば、テキストファイル内の指定文字列の値を変更する事が出来ます。
この例文で言うと、「localhost,10」に置き換わり、見た目上は「元の値に+5されている」と読み取れます。


と言うように、文字で現すにしても非常に説明しづらく、ややこしい処理が発生します。
それが、「年」「月」「日」「時」「ホスト名」「IPアドレス」「リンク元」「表示ファイル」…の集計というように多くなると、
とてもテキストファイルに集計結果を保存するのは困難に感じました。


と言うわけで、集計結果はデータベースで管理する事にします。
SQLiteだとログを削除する時に(特別な事をしないと)ファイルサイズが肥大化したままになるので、MySQLを使おうと思います。

いくら集計用のファイルでも、ログの整理は必要です。

例えば毎日1000人がサイトに訪れると、1年で最低365,000レコードを使用する事になります。
もし、複数サイトで利用出来るアクセス解析を作ろうとした場合、比例してそのレコード数が増えてしまいます。

ですので、「保存は1年まで」と言うように期限を決めて、期限を過ぎた場合はレコードを削除した方がよいでしょう。

一旦データベースに入れれば、後から集計用のテキストファイルを作成するのは容易なので、1年前になるファイルはテキストとして出力するのも良いかもしれません。


ちょっとわかりづらい記事になりましたが、、後ほど修正しつつ、次回は表示面について考えたいと思います。
前回の記事 でアクセスログの保存方法について検討しました。そして「テキストファイルを使ってみよう」という、一応の結論が出ました。

その結論が出るまでのプロセスは省くとして、、以下のような流れで考えれば良いのではないか?と思っています。

(1)アクセスログをテキストファイルに追加する

WEBページにアクセスがある度にユーザの情報をテキストファイルに保存します。
保存は、(日付).log にします。

なぜ日付にしているのかというと、AWstatsというアクセス解析がそうしていたから真似たというのが本音ですが、すべてのログを同じファイルに書き込むよりも、日で分けた方が読み込み時のファイルを開く時に、重くならないだろうという思惑もあります。

(2)日付が変わった時、保存したテキストファイルを圧縮する

ただログを保存していくと、テキストファイルの容量が肥大化してしまいます。私の実験では、10万行(10万PV)で14MBになりました。たった1日のファイルでこれならアクセスの多いサイトでは、すぐにサーバの容量を食いつぶす事になります。

そう思って調べたところ、AWstatsのログファイルの拡張子が「gz」になっている事に気づきました。
gzは圧縮しているからだと言う事は分かったのですが、PHPで圧縮出来るのだろうか?そう思って検索したら、どうやら出来るみたいです。

http://php.plus-server.net/ref.zlib.html

こんな便利な関数があったんですね。
このZlib関数を使う事で、10万行14MBのファイルが65KBまでなりました。

Zlib関数には圧縮したファイルを読み込んで出力する機能もあるので、特に解凍する必要は無さそうです。
ですので、日付が変わった時に昨日に値するファイルを圧縮したいと思います。

(3)集計用のファイルを作成する

データベースを使っていると、
「SELECT * FROM acclog WHERE date='2008-11-27'」
などのSQL文で、簡単に検索条件に合ったデータを抽出させる事が出来ます。

しかし、テキストファイルの場合は全て読み込んで1行毎に検査していくしかありません。

ただでさえ何万行何MBとなるファイルを読み込むのに負荷がかかるのに、解析結果のページにアクセスする度に読み込んでいては負荷がかかりすぎる。表示に時間もかかるし良くない。

っと思ったのが、テキストファイルで保存しない一番の理由でした。

しかし、「毎回アクセスするのではなく、予め結果が集計されたデータにアクセスすれば良い」という事実に気づきました。

つまり、

20081127175200 127.0.0.1 index.php
20081128175210 127.0.0.1 test.php

と記録されているログがあるなら、これをまとめて集計して

[月の集計]
11 2

[日の集計]
27 1
28 1

と言うように別ファイルでまとめれば良いと言う事です。

これなら解析ページにアクセスしても負荷はかからず、一瞬で集計後の内容が表示されるでしょう。
検索する場合でも集計結果のファイルに対して一致する文字列を読み込めばいいわけですから、そんなに時間がかかりません。

ただ、複雑な検索は難しいでしょうね。
例えば「2008年10月~2008年11月、index.phpにアクセスし、Firefoxでアクセスした人」
とかの条件は難しいように感じます。ここまでやりたいのなら、データベースを使うべきでしょうね。


と言うわけで、テキストファイルの保存でも一長一短ありますが、主に閲覧を目的とするならこの流れで良いと思います。Amebaも閲覧だけですしね。
まず、各ページにアクセスされたアクセスログの情報を保存する方法を考えてみたいと思います。

私が保存方法として思いついたのは以下の3つです。

■MySQLを使う

普段からMySQLを使ってWEBシステムを構築していたので、そのままMySQLを利用する方法を思いつきました。
MySQLだとSQL文を使ってデータの集計や検索がしやすいので、アクセスログの操作にはもってこいだと思いました。

しかし、問題となったのは「同時接続数」と「負荷」。アクセスする度にINSERT文を実行すると負荷がかかります。
アクセス解析用途のみでMySQLを使うなら別ですが、そうでない場合は、毎回SQL文を発行するというのは気が引けます。

同時接続数もサーバによって制限があるので、出来るだけリソースが不足しないように接続を制限しなければいけません。
http://dev.mysql.com/doc/refman/5.1/ja/mysql-config-wizard-connections.html


と言うわけで、MySQLを使うWEBシステムに、新たにMySQLを使ったアクセス解析は不向きではないか?と思いました。

■SQLiteを使う

MySQLが使えないのなら別のデータベース…となるとSQLiteが手軽で良い!と思い検討していました。

MySQLと同じようにSQL文が使えますし(ただし、使える文法には制限があります)、保存用データベースもテキストファイルの要領で、追加・複製出来るので、使い勝手が良いです。「ユーザごとのアクセス解析」を作る上でも、非常に便利だと思いました。

しかし、SQLiteは保存したログを削除してもファイルサイズが変わらないといった問題がある事を知りました。
アクセス解析なので、頻繁にログを削除する事はないかも知れませんが、「削除してもファイルサイズが減らない」という事実がどうも受け入れられません。

※ちなみにvacuumという機能を使えばファイルの最適化が出来るそうです。

■テキストファイルに保存する

自宅サーバの管理をしていて思ったのですが、ApacheやPostfixのログはテキスト形式です。サーバソフトでログを吐き出すものは、だいたいテキストファイルを使っているように感じます。

最初、「なぜ集計や検索がしづらいテキストファイルを使うのだろう?」っと疑問に思いました。

自分で実験してみても、テキストファイルの読み込み・一覧表示はデータベースを使った場合よりも、体感的に明らかに「遅い」と感じます。

しかし、ログを扱うソフトではテキストファイルを使っている。と言う事は「テキストファイルを使ってもログ解析出来る」と言えます。
(まぁ、「サーバにデータベースがインストールされてなかったら使えないから」と言う理由が大半でしょうが)


そこで、「PHP アクセス解析」でググったり、サーバ型のアクセス解析、『analog』や『AWstats』を調べてみました。

そうすると、なんとなくですが「こういう方法で保存する事により、テキストファイルを使ったアクセス解析でも、スムーズにログ解析出来る」という事が分かりました。

ちょっと言葉足らずですが、そのロジックを次の記事にてまとめたいと思います。
PHPでアクセス解析を作る上で、自身が悩んだこと・実験したことをまとめたいと思います。

※尚、あくまで自分が勉強した事に対するまとめですので、掲載している事がすべて正しいと言う事はありません。あしからず。
※実際に詳細ソースを掲載するわけではありません。あくまで考え方をまとめとして書いています。