[PHP] inotify関数を使ってログを監視するスクリプトを作ろう | A Day In The Boy's Life

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

前回の「inotifyを使ってファイルやディレクトリに起きたイベントを簡単に監視する 」にてinotifyの機能を使ってみましたが、PHPのプログラムからも利用できるようなので、試しにログを監視するスクリプトを作ってみました。


inotifyの機能を使って独自のPHPスクリプトを書けば、何かのファイルを監視したり、ディレクトリにおきた変化をトリガーにして何か処理をするというようなスクリプトも簡単に書くことができます。



PECLのinotifyパッケージをインストールする


PHPからinotifyの機能を利用するにはPECLの拡張モジュールとして組み込む必要があります。

PECLで提供されている拡張モジュールのインストール方法 は色々用意されているようですが、今回はphpizeコマンドを使ってモジュールをコンパイルし、PHPに組み込むようにしました。


1. PECLのinotifyパッケージをダウンロードする


まずは、PECLの公式サイトからinotifyパッケージ をダウンロードします。

ダウンロード後はサーバーに転送し、下記のようにモジュールをコンパイルします。

# tar -xvzf inotify-0.1.3.tgz

# cd inotify-0.1.3

# phpize

# ./configure

# make

# make install

make install実行時の最後に拡張モジュールのインストール先が示されますので、その場所をメモしておきます。


Installing shared extensions: /usr/local/lib/php/extensions/no-debug-non-zts-20060613/

上記ディレクトリ内に、inotify.soという動的ライブラリファイルが作られています。


2. inotify拡張モジュールをPHPに組み込む


先ほど作られたinotifyの動的ライブラリファイル(inotify.so)をPHPの設定ファイル(php.ini)にセットして読み込まれるようにします。


extension_dir = "/usr/local/lib/php/extensions"

拡張モジュール用のディレクトリは、環境に合わせて適当に編集してください。


このままでは、extensionsディレクトリ以下にはinotify.soファイルは存在しないので、シンボリックリンクを作成しておきます。


# cd /usr/local/lib/php/extensions/

# ln -s ./no-debug-non-zts-20060613/inotify.so inotify.so

最後に、Apacheを再起動しておきましょう。



3. inotify拡張モジュールが有効になっているかテストしてみる


PECLのinotifyパッケージ内にはサンプルプログラム(tail.php)付属しています。

これを実行してうまく動作するか試してみましょう。


# tail.php /home/hoge/test.txt

引数に監視するファイルのパスを指定します。

上記を実行中に、test.txtファイルに文字を書き込んでみます。


# echo "xyz" >> /home/hoge/test.txt

すると・・・


# php tail.php /home/sakaiya/test.txt
Event received !
xyz

ターミナル上にイベントをキャッチしたメッセージが表示されました。

どうやらうまく動作してくれているようです。



ログファイルに何か書き込まれたらアラートメールを発砲する監視スクリプト


よくあるログ監視用のスクリプトを書いてみたいと思います。

監視ツールを導入するとなるとかなりの費用がかかってしまうため、このような簡易スクリプトで代替できる場合もあると思います。


先ほどのサンプルスクリプトをベースに作ってみました。


<?php

// 監視対象のログファイル
define("LOG_FILE_PATH", "/var/log/error.log");

// 通知先
define("MAIL_TO", itboy@hogehoge.com">itboy@hogehoge.com);

// ログファイルが存在するか、オープンできるかチェック
if (!file_exists(LOG_FILE_PATH) || ($fp = fopen(LOG_FILE_PATH, "r")) === FALSE) {
    echo "ログファイルの読み込みに失敗しました";
    exit;
}

// ファイルポインタを最後に持っていく(ログファイルに出力された内容を取得するため)
fseek($fp, 0, SEEK_END);

// inotifyのインスタンスを初期化
$fd = inotify_init
();

// inotifyの監視対象ファイルをセット
// ログファイルに内容が書き込まれた(変更があった)イベントを取得する
if (($watch_descriptor = inotify_add_watch ($fd, LOG_FILE_PATH, IN_MODIFY)) === FALSE) { echo "ログファイルの読み込みに失敗しました"; exit; } // イベントが発生するまでループ while (($events = inotify_read ($fd)) !== false) { // ログファイルに何か書き込まれたときはアラートのメールを発砲 foreach($events as $event) { if (!($event['mask'] & IN_MODIFY)) continue; // ログファイルに書き込まれた内容を取得 $log = stream_get_contents($fp); // メールを送信 mb_send_mail(MAIL_TO, "WARNING", $log); break; } } inotify_rm_watch ($fd, $watch_descriptor); fclose($fp); ?>

inotifyでは、イベントの種類をキャッチすることはできますが、何が書き込まれたのかなんかはわからないため、別でファイルを開いておいてその内容を取り込むようになっています。


テストをしたければ、上記スクリプトの実行中に


echo "DBが停止しました" >> /var/log/error.log

見たいな事をしてみれば、その内容がメールで送信されるはずです。


なお、実運用に耐えうるスクリプトにはなっていないのであしからず。
例えば、ログファイルに続けざまに書き込みが行われたら大量のメールが送信されることになります。
障害時には、そのように大量のメッセージを出力するということがよくあったりします。


あと、ログファイルにlogrotateなどをセットしておくと、そのローテーション実行時に場合によってはログファイルを見失うことになります。

(昔のログファイルを削除して、新規に作り直すような設定をしておいた場合)


注意)

現在のPECLのinotifyパッケージは、1つのイベントしか監視できないようです。

監視するイベントは、inotify_add_watch 関数で追加していきますが、複数追加しても最後にセットした監視イベントしか拾わなくなります。