プログラムを作成しているときに、必要な情報(ログイン情報やエラー情報)をログに出力することが多いと思います。Monologは強力なロギングライブラリで、ただファイルに出力するだけでなく、データベースに保存したり、メール送信したり、FireBugに出力したりと様々なことができます。
今回は、MonologをComposerでインストールして、ロギングするサンプルプログラムを作成してみます。
今回の目標
- MonologをComposerでインストールする
- Monologを使ってロギングしてみる
サーバ環境
Composerでインストール
composer.jsonを記述します。
// composer.json
{
"require": {
"monolog/monolog": "@stable"
}
}
composer installを実行すればインストールは完了です。
$ composer install
Monologでロギング
基本的なロギング
以下のサンプルプログラムを見ていきます。
src/monolog_sample_1.php
<?php
namespace BlogSample\MonologSample1;
require_once __DIR__ . '/../vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
$logging_path = __DIR__ . '/../logs/foo_test_1.log';
$logger = new Logger('foo_test');
// 基本的なロギング
$logger->pushHandler(new StreamHandler($logging_path, Logger::INFO));
// デバッグレベルなのでロギングされない
// $logger->debug()と同じ
$logger->addDebug('debug_bar');
// 以下、すべてロギングされる
// 下にいくほどログレベルが高くなる
// $logger->info()と同じ
$logger->addInfo('info_bar');
// $logger->notice()と同じ
$logger->addNotice('notice_bar');
// $logger->warning(), $logger->warn()と同じ
$logger->addWarning('warning_bar');
// $logger->error(), $logger->err()と同じ
$logger->addError('error_bar');
// $logger->critical(), $logger->crit()と同じ
$logger->addCritical('critical_bar');
// $logger->alert()と同じ
$logger->addAlert('alert_bar');
// $logger->emergency(), $logger->emerg()と同じ
$logger->addEmergency('emergency_bar');
exit;
ポイントをいくつか見ていきます。
- 15行目でログをどのように扱うかのハンドラをスタックで格納します。ドキュメントから分かるように、多くのハンドラをサポートしています。
よく使うと思われるのは、ログファイルにロギングするStreamHandler
, 日付でログファイルをローテートしてくれるRotatingFileHandler
, SyslogにロギングするSyslogHandler
, 前回紹介したSwiftMailerでメール送信できるSwiftMailerHandler
,MongoDBHandler
などのデータベース(NoSQL)にログを格納するハンドラ、FireBugやChromePHPにログを出力することでブラウザからデバッグ作業が行えるFirePHPHandler
,ChromePHPHandler
や、ログをバッファに格納しておいて指定したログレベルに到達したらバッファに格納しておいたログを出力する特殊なFingersCrossedHandler
です。pushHandler()
メソッドによって、1つのハンドラだけではなく複数のハンドラを同時に扱うこともできます。 - 同じく15行目で、扱うログレベルを設定します(ログレベルについては後述)。そして、19行目以降にロギングしていますが、設定したログレベル(今回はINFO)より低いログレベルの場合は取扱いしません。今回の場合はINFOよりログレベルが低いDEBUGのログがロギングされません。
環境によってこのログレベルを変えていく必要があると思います。例えば、開発環境では細かい挙動まで知りたい場合は、DEBUGまですべての情報をログに出力する必要があると思いますが、本番環境では必要最低限のログ出力だけで良いので、CRITICALやALERTやそれ以上を指定するはずです。 - ログレベルについては、ドキュメントや、RFC 5424にもありますが、こちらでもまとめておきます。下にいくほどログレベルが高くなり、緊急度も高くなります。
ログレベル | 説明 |
---|---|
DEBUG | 詳細なデバッグ情報 |
INFO | 情報。重要なイベント。 例として、SQLのログ、ユーザーのログイン情報など |
NOTICE | 注意。正常だが重要なイベント |
WARNING | 警告。実行時には問題ないが正常ともいえない何らかの予期しない問題。例として、廃棄予定(deprecated)のAPIの使用など |
ERROR | エラー。即時アクションは必要ないが、監視されるべきであるランタイムエラー |
CRITICAL | クリティカルなエラー。例として、アプリケーションのコンポーネントが使用できない、予期しない例外など |
ALERT | アラート。SMS・メール通知などアクションをすぐ起こす必要がある。例として、ウェブサイトがダウン、データベースに接続できないなど |
EMERGENCY | 緊急。システムが使用不能である |
サンプルプログラムを実行すると、以下のようにログファイルに出力されます。
$ php ./src/monolog_sample_1.php
$ cat logs/foo_test_1.log
[2014-05-11 11:12:13] foo_test.INFO: info_bar [] []
[2014-05-11 11:12:13] foo_test.NOTICE: notice_bar [] []
[2014-05-11 11:12:13] foo_test.WARNING: warning_bar [] []
[2014-05-11 11:12:13] foo_test.ERROR: error_bar [] []
[2014-05-11 11:12:13] foo_test.CRITICAL: critical_bar [] []
[2014-05-11 11:12:13] foo_test.ALERT: alert_bar [] []
[2014-05-11 11:12:13] foo_test.EMERGENCY: emergency_bar [] []
メールでロギング情報を送信
SwiftMailerHandler
を使ってロギングした情報をメールで送信してみます。
SwiftMailerについては、[PHP]SwiftMailerを使ってメール送信する をご参照ください。
以下サンプルプログラムです *1。
src/monolog_sample_2.php
<?php
namespace BlogSample\MonologSample2;
require_once __DIR__ . '/../vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SwiftMailerHandler;
$logging_path = __DIR__ . '/../logs/foo_test_2.log';
$mailer = \Swift_Mailer::newInstance(
\Swift_SmtpTransport::newInstance('localhost', 25)
);
$message = \Swift_Message::newInstance()
->setSubject('Monolog Sample')
->setTo('bar@bar.com')
->setFrom(['foo@foo.com' => 'Mr.FooBar']);
$logger = new Logger('foo_test');
// 基本的なロギング
$logger->pushHandler(new StreamHandler($logging_path, Logger::INFO));
// ログをメール送信する
$logger->pushHandler(new SwiftMailerHandler($mailer, $message, Logger::ALERT));
// ログファイルに出力される
$logger->addInfo('info_bar');
// ログファイルに出力される
$logger->addNotice('notice_bar');
// ログファイルに出力される
$logger->addWarning('warning_bar');
// ログファイルに出力される
$logger->addError('error_bar');
// ログファイルに出力される
$logger->addCritical('critical_bar');
// ログファイルに出力され、メール送信される
$logger->addAlert('alert_bar');
// ログファイルに出力され、メール送信される
$logger->addEmergency('emergency_bar');
exit;
- 25行目、28行目で2つのハンドラを追加しています。
StreamHandler
(ログファイルに出力)のログレベルはINFO,SwiftMailerHandler
(SwiftMailerでメール送信)のログレベルはALERTで設定しています。 - 31行目以降はログ出力していますが、メール送信されるのは、ログレベルがALERTとEMERGENCYの場合のみです。
サンプルプログラムを実行すると、以下のようにログファイルに出力されます。
$ php ./src/monolog_sample_2.php
$ cat logs/foo_test_2.log
[2014-05-11 11:30:11] foo_test.INFO: info_bar [] []
[2014-05-11 11:30:11] foo_test.NOTICE: notice_bar [] []
[2014-05-11 11:30:11] foo_test.WARNING: warning_bar [] []
[2014-05-11 11:30:11] foo_test.ERROR: error_bar [] []
[2014-05-11 11:30:11] foo_test.CRITICAL: critical_bar [] []
[2014-05-11 11:30:11] foo_test.ALERT: alert_bar [] []
[2014-05-11 11:30:11] foo_test.EMERGENCY: emergency_bar [] []
メールの以下のようなものが2通届きます。
From: Mr.FooBar <foo@foo.com>
To: bar@bar.com
Subject: Monolog Sample
Body:
[2014-05-11 11:30:11] foo_test.ALERT: alert_bar [] []
From: Mr.FooBar <foo@foo.com>
To: bar@bar.com
Subject: Monolog Sample
Body:
[2014-05-11 11:30:11] foo_test.EMERGENCY: emergency_bar [] []
FingersCrossedHandlerを使う
例えば、本番環境で何かしらのエラーの原因を究明するためにログを見るとします。
このとき、ロギング対象のログレベルがALERTなど高い場合、INFOやNOTICE, WARNINGなどもっと知りたい情報がログに残りません。かといって、ログレベルをINFOまで下げてしまうとエラーでないのに通常の本番環境では不要な情報ばかりがログに出力されてしまい、本当に必要な情報が埋もれてしまいます。
こういう場合、FingersCrossedHandler
を使うことで、指定したログレベル以下のログはバッファに格納しておき、あるログレベルの場合に、バッファ内のログを出力することができます。
以下、サンプルプログラムです。
src/monolog_sample_3.php
<?php
namespace BlogSample\MonologSample3;
require_once __DIR__ . '/../vendor/autoload.php';
use Monolog\Logger;
use Monolog\Handler\FingersCrossedHandler;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Handler\FingersCrossed\ErrorLevelActivationStrategy;
$logging_path = __DIR__ . '/../logs/foo_test_3.log';
$logger = new Logger('foo_test');
// FingersCrossedHandlerを使う
$logger->pushHandler(
new FingersCrossedHandler(
new RotatingFileHandler($logging_path, 2, Logger::INFO),
new ErrorLevelActivationStrategy(Logger::ALERT)
)
);
// デバッグレベルなのでロギングされない
$logger->addDebug('debug_bar');
// この時点ではログファイルに出力されない
$logger->addInfo('info_bar');
// この時点ではログファイルに出力されない
$logger->addNotice('notice_bar');
// この時点ではログファイルに出力されない
$logger->addWarning('warning_bar');
// この時点ではログファイルに出力されない
$logger->addError('error_bar');
// この時点ではログファイルに出力されない
$logger->addCritical('critical_bar');
// この時点で上記のログが出力される
$logger->addAlert('alert_bar');
exit;
17行目付近に注目すると、FingersCrossedHandler
クラスは2つの引数をとっていることが分かります。
1つ目は、ログを扱うハンドラで、今回はログをローテートできる RotatingFileHandler
を使ってみました。 StreamHandler
と異なるのは、ログファイル名に日付が付くのと、保存するファイル数(今回は2)を指定することができます。
2つ目は、ログに出力するログレベルを指定しています。今回の場合、ALERT以上のログレベルの場合に、INFO以上のログレベルの情報をログに出力するようにしています。
ややこしいので、以下の表を見てください。
ログレベル | ログに出力するか | 備考 |
---|---|---|
DEBUG | × | RotatingFileHandler でINFOにしているため |
INFO | ○ | ALERT以上のログレベルの場合にログに出力される |
NOTICE | ○ | ALERT以上のログレベルの場合にログに出力される |
WARNING | ○ | ALERT以上のログレベルの場合にログに出力される |
ERROR | ○ | ALERT以上のログレベルの場合にログに出力される |
CRITICAL | ○ | ALERT以上のログレベルの場合にログに出力される |
ALERT | ○ | ErrorLevelActivationStrategy で指定しているため |
EMERGENCY | ○ | ErrorLevelActivationStrategy で指定しているため |
サンプルプログラムを実行すると、以下のようにログファイルに出力されます。
$ php ./src/monolog_sample_3.php
$ cat logs/foo_test_3-2014-05-11.log
[2014-05-11 12:29:05] foo_test.INFO: info_bar [] []
[2014-05-11 12:29:05] foo_test.NOTICE: notice_bar [] []
[2014-05-11 12:29:05] foo_test.WARNING: warning_bar [] []
[2014-05-11 12:29:05] foo_test.ERROR: error_bar [] []
[2014-05-11 12:29:05] foo_test.CRITICAL: critical_bar [] []
[2014-05-11 12:29:05] foo_test.ALERT: alert_bar [] []
もちろん、このログ情報をメール等で送信することができます。
最後に
Monologでロギング処理を実装してみました。元々はSymfony2でMonologを使おうと思っていたのですが、設定ファイルを見てもよく分からなかったので自分で調べて実装してみようということで、このようなサンプルプログラムを作成してみました。
個人的にやはり FingersCrossedHandler
がかなり便利そうでオススメです。まだまだ多くのことができそうなので色々調べて分かったらまた記事にしたいと思います。
また、今回のサンプルプログラムもGitHubにありますのでgit clone
してcomposer install
して試してみてください。
https://github.com/ohsawa0515/blog_sample_source_code
参考URL
- Seldaek/monolog(GitHub)
- ロギングで Monolog を使用する方法
- Symfony2でMonologを使う方法です。