Monolog は、ログをファイル、ソケット、受信箱、データベース、およびさまざまな Web サービス (クラウド) に送信します。モノログは 1 つ以上のストレージ メディアに同時に保存できます (スタック バブリング処理が続きます)。
インストール
$ composer require monolog/monolog
<?phpuse Monolog\Logger;use Monolog\Handler\StreamHandler;// create a log channel$log = new Logger('name');$log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));// add records to the log$log->warning('Foo');$log->error('Bar');
各 Logger インスタンスには
channel (つまり、一意の名前) と Aがあります。 1 つ以上のハンドラーで構成されるスタック。 Logger にレコードを追加すると、レコードはハンドラー スタックを通過します。各ハンドラーはレコードを完全に処理するかどうかを決定し、完全に処理する場合はプロセスを終了します (バブリングを停止します)。ここでのフルとは、考えているか考えていないかを指します。考えたくない場合は、続けることができます。 これにより、ログを柔軟に設定できるようになります。たとえば、スタック
の一番下にあるStreamHandlerは、すべてのレコードをハードディスクに保存します。その上には、エラーメッセージが記録されたときに電子メールを送信するMailHandlerがあります。すべてのハンドラーには $bubble 属性があり、これは、レコードの処理時にハンドラーが処理をブロックするかどうかを定義するために使用されます (ブロックされた場合、レコードが到着後に処理されることを意味するため、バブルアップして処理しないでください)従順です)。この例では、MailHandler の $bubble を false に設定します。これは、レコードが MailHandler によって処理され、StreamHandler にバブルアップされないことを意味します。追加: ここでスタックとバブリングについて言及されていますが、バブリングはボトムアップのプロセスであることが理解されているため、一見すると少し混乱するように思えます。下が StreamHandler、上が MailHandler、その結果、MailHandler が処理して StreamHandler へのバブリングを停止しました。666 というバブルが上から下に向かって泡立っているような印象を受けますが、これはバブリングと言えるでしょうか。
英雄的な瞬間:スタック、時々混乱することがありますが、スタックは先入れ先出し (
First- In/ First- Out) であることをもう一度思い出してください。来てください] 水パイプ; スタックは先入れ、後出しです (F最初- In/ Last- Out)、カップの中にN層の色があるアイスクリームを考えてください、そして下は黄色、...、上はピンクなので、最初にピンクのもの (MailHandler) を食べ、次に黄色のもの (StreamHandler) を食べます。実際、このバブルは正しいです。正確には、このバブルです。逆さのカップ、この理解は鮮明です。 続行...
多くのロガーを作成でき、各ロガーはチャネル (例: db、リクエスト、ルーターなど) を定義し、各チャネルは複数のハンドラーと組み合わせることができ、ハンドラーは次のように記述できます。普遍的かどうか。チャンネルはログの日付と時刻と同じです。 ログには、おそらく次のような文字列が記録されます。 チャンネル名は内容を記録します。特定の形式は設定によって異なります。識別またはフィルタリングに使用できます。各ハンドラーには、ログのフォーマットに使用されるフォーマッタがあります。詳細は不要です。
monolog では、カスタマイズされたログ レベルは利用できません。RFC 5424 レベルは、デバッグ、情報、通知、警告、エラー、クリティカル、アラート、緊急の 8 つだけです。ただし、分類などの特別なニーズが本当にある場合は、もちろんログ メッセージが処理される前に、プロセッサをロガーに追加できます。 私は、この人生で「プロセッサ」を追加することはないと考えています。
Here is a basic setup to log to a file and to firephp on the DEBUG level:
<?phpuse Monolog\Logger;use Monolog\Handler\StreamHandler;use Monolog\Handler\FirePHPHandler;// Create the logger$logger = new Logger('my_logger');// Now add some handlers$logger->pushHandler(new StreamHandler(__DIR__.'/my_app.log', Logger::DEBUG));$logger->pushHandler(new FirePHPHandler());// You can now use your logger$logger->addInfo('My logger is now ready');
我们来分析一下这个配置。 The first step is to create the logger instance which will be used in your code. The argument is a channel name, which is useful when you use several loggers (see below for more details about it). 第一步,创建 Logger实例,参数即通道名字。
The logger itself does not know how to handle a record. It delegates it to some handlers. The code above registers two handlers in the stack to allow handling records in two different ways. Logger本身不知道如何处理记录,它将处理委托给 Handler[s],上面的代码注册了两个 Handlers,这样就可以用两种方法来处理记录。
Note that the FirePHPHandler is called first as it is added on top of the stack. This allows you to temporarily add a logger with bubbling disabled if you want to override other configured loggers. 提示: FirePHPHandler最先被调用,因为它被添加在栈的顶部。这就允许你临时添加一个阻塞的 Logger,如果你想覆盖其他 Logger[s]的话。
Monolog 提供两种方法来添加额外的信息到简单的文本信息(along the simple textual message)。
第一种,即当前日志上下文,允许传递一个数组作为第二个参数,这个数组的数据是额外的信息:
<?php$logger->addInfo('Adding a new user', array('username' => 'Seldaek'));
简单的Handler(SteamHandler)会简单的将数组格式化为字符串,功能丰富点的Handler(FirePHP)可以搞得更好看。
Processors 可以是任何可调用的方法(回调)。它们接受 $record作为参数,然后返回它( $record),返回之前,即是我们添加 额外信息的操作,在这里,这个操作是改变 $record的 extrakey的值。像这样:
<?php$logger->pushProcessor(function ($record) { $record['extra']['dummy'] = 'Hello world!'; return $record;});
Monolog 提供了一些内置的 processors。看 dedicated chapter收回我说的话,我可能很快就会用到 Processors的。
通道是识别record记录的是程序哪部分的好方法(当然,关键词匹配啊),这在大型应用中很有用,如 MonologBundle in Symfony2。
想象一下,两个 Logger共用一个 Handler,通过这个 Handler将记录写入一个文件。这时使用通道能够让我们识别出是哪一个 Logger处理的。我们可简单的在这个文件中过滤这个或者那个通道。
<?phpuse Monolog\Logger;use Monolog\Handler\StreamHandler;use Monolog\Handler\FirePHPHandler;// Create some handlers$stream = new StreamHandler(__DIR__ . '/my_app.log', Logger::DEBUG);$firephp = new FirePHPHandler();// Create the main logger of the app$logger = new Logger('my_logger');$logger->pushHandler($stream);$logger->pushHandler($firephp);// Create a logger for the security-related stuff with a different channel$securityLogger = new Logger('security');$securityLogger->pushHandler($stream);$securityLogger->pushHandler($firephp);// Or clone the first one to only change the channel$securityLogger = $logger->withName('security');
在 Monolog 中个性化日志是很easy的。大部分 Handler 使用 $record['formatted']的值。这个值依赖于 formatter 的设置。我们可以选择预定义的 formatter 类或者编写自己的。
配置一个预定义的 formatter 类,只需要将其设置成 Handler 的字段(属性)即可:
// the default format is "Y-m-d H:i:s"$dateFormat = "Y n j, g:i a";// the default output format is [%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"$output = "%datetime% > %level_name% > %message% %context% %extra%\n";$formatter = new LineFormatter($output, $dateFormat);// Create a handler$stream = new StreamHandler(__DIR__ . 'my_app.log', Logger:DEBUG);$stream->setFormatter($formatter);// bind it to a logger object$securityLogger = new Logger('security');$securityLogger->pushHandler($stream);
formatter 是可以在N个 Handler 之间复用的,并且可在N个 Logger 之间共享 Handler。
MandrillHandler: Swift_Message インスタンスを使用して、Mandrill API 経由でメールを送信します。
FleepHookHandler: Webhook を使用して、レコードを Fleep 会話に記録します。
WhatFailureGroupHandler: このハンドラーは、GroupHandler を拡張し、各子ハンドラーによって発生した例外を無視します。これにより、リモート TCP 接続が切断された可能性があるが、アプリケーション全体がクラッシュすることは望ましくなく、他のハンドラーへのログを継続したい場合の問題を無視できます。
BufferHandler: このハンドラーは、close() が呼び出されるまで、受信したすべてのログ レコードをバッファリングします。close() が呼び出された時点で、すべてのログ メッセージを一度にラップするハンドラーの handleBatch() を呼び出します。これは、たとえば、ログ レコードごとに 1 つのメールを送信する代わりに、すべてのレコードを含む電子メールを一度に送信する場合に非常に便利です。
GroupHandler: このハンドラーは他のハンドラーをグループ化します。受信したすべてのレコードは、構成されているすべてのハンドラーに送信されます。
FilterHandler: このハンドラーは、指定されたレベルのレコードのみをラップされたハンドラーに渡します。
SamplingHandler: 別のハンドラーをラップし、一部のみを保存したい場合にレコードをサンプリングできるようにします。
NullHandler: 処理できるレコードはすべて破棄されます。これを使用して、既存のハンドラー スタックの上に置き、一時的に無効にすることができます。
PsrHandler: ログ レコードを既存の PSR-3 ロガーに転送するために使用できます。
TestHandler: テストに使用され、送信されたすべてのものを記録し、情報を読み出すアクセサーを備えています。
HandlerWrapper: 独自のラッパーを簡単に作成するために継承できるシンプルなハンドラー ラッパー。
✪ として常用
PsrLogMessageProcessor: PSR-3 ルールに従ってログ レコードのメッセージを処理し、{foo} を $context[‘foo’] の値に置き換えます。
IntrospectionProcessor: ログ呼び出しの元の行/ファイル/クラス/メソッドを追加します。
WebProcessor: 現在のリクエスト URI、リクエスト メソッド、およびクライアント IP をログ レコードに追加します。
MemoryUsageProcessor: 現在のメモリ使用量をログ レコードに追加します。
MemoryPeakUsageProcessor: ピーク メモリ使用量をログ レコードに追加します。
ProcessIdProcessor: プロセス ID をログ レコードに追加します。
UidProcessor: 一意の識別子をログ レコードに追加します。
GitProcessor: 現在の git ブランチを追加し、ログ レコードにコミットします。
TagProcessor: 事前定義されたタグの配列をログ レコードに追加します。
レジストリ: MonologRegistry クラスを使用すると、どこからでも静的にアクセスできるグローバル ロガーを構成できます。これは実際にはベスト プラクティスではありませんが、一部の古いコードベースや使いやすさに役立ちます。 MonologRegistry は、完全に構成されたロガーを許可し、完全に静的にアクセスすることができますが、これは最適な方法ではありませんが、特定の古いコード パッケージでいくつかの助けを提供することも、単に単独で使用することもできます。
ErrorHandler: The Monolog\ErrorHandlerclass allows you to easily register a Logger instance as an exception handler, error handler or fatal error handler. Monolog\ErrorHandler允许我们注册一个 Logger实例作为一个异常处理句柄,错误处理句柄或者致命错误处理句柄。
ErrorLevelActivationStrategy: Activates a FingersCrossedHandler when a certain log level is reached. 当达到某个日志等级的时候激活 FingersCrossedHandler。
ChannelLevelActivationStrategy: Activates a FingersCrossedHandler when a certain log level is reached, depending on which channel received the log record. 当达到某个日志等级的时候激活 FingersCrossedHandler,取决于哪个通道收到日志信息。
Monolog 是完全可以扩展的。可以轻松让logger适用于我们的需求。
虽然 Monolog 提供了很多内置的 Handler,但是我们依然可能没有找到我们想要的那个,这时我们就要来编写并使用自己的了。仅需 implement Monolog\Handler\HandlerInterface。
来写个 PDOHandler,用来把日志存到数据库,我们继承 Monolog 提供的抽象类,以坚守 Don’t Rpeat Yourself原则。
<?php use Monolog\Logger; use Monolog\Handler\AbstractProcessingHandler; class PDOHandler extends AbstractProcessingHandler { private $initialized = false; private $pdo; private $statement; public function __construct(PDO $pdo, $level = Logger::DEBUG, $bubble = false) { $this->pdo = $pdo; parent::__construct($level, $bubble); } protected function write(array $record) { if (!$this->initialized) { $this->initialize(); } $this->statement->execute(array( 'channel' => $record['channel'], 'level' => $record['level'], 'message' => $record['formatted'], 'time' => $record['datetime']->format('U'), )); } private function initialize() { $this->pdo->exec( 'CREATE TABLE IF NOT EXISTS monolog ' .'(channel VARCHAR(255), level INTEGER, message LONGTEXT, time INTEGER UNSIGNED)' ); $this->statement = $this->pdo->prepare( 'INSERT INTO monolog (channel, level, message, time) VALUES (:channel, :level, :message, :time)' ); $this->initialized = true; } }
现在就可以在Logger中使用这个Handler了:
$logger->pushHandler(new PDOHandler(new PDO('sqlite:logs.sqlite')));// You can now use your logger$logger->addInfo('My logger is now ready');
Monolog\Handler\AbstractProcessingHandler提供了Handler需要的大部分逻辑,包括processors的使用以及record的格式化(which is why we use $record['formatted']instead of $record['message'])。