概述
Monolog 是一個流行的 PHP 日誌記錄庫,它提供了強大的功能來幫助開發者在應用程式中進行日誌記錄。Monolog 支持將日誌訊息發送到多種目的地,包括檔、套接字、電子信件、資料庫以及其他各種 Web 服務。它實作了 PSR-3 日誌介面,這意味著它與遵循該標準的其他日誌庫相容,提供了一致的日誌記錄方法。
核心概念
通道(Channels) :每個 Monolog 日誌記錄器例項都有一個或多個通道,每個通道都有一個名稱,用於區分不同型別的日誌訊息。
處理器(Handlers) :處理器負責實際的日誌訊息處理,例如將訊息寫入檔、發送到電子信件或儲存到資料庫中。一個日誌記錄器可以有多個處理器,它們形成一個堆疊,日誌訊息會按順序透過這些處理器。
格式化器(Formatters)
:格式化器用於定義日誌訊息的輸出格式。Monolog 提供了多種內建格式化器,如
LineFormatter
將日誌格式化為單行字串,
JsonFormatter
將日誌編碼為 JSON 格式等。
處理器(Processors) :處理器可以修改日誌記錄或添加額外的數據,例如添加請求的 URI、使用者資訊等。
安裝
Monolog 的安裝通常透過 Composer 進行,使用以下命令即可安裝到計畫中:
composer require monolog/monolog
基本用法
使用 Monolog 時,你可以建立一個或多個記錄器例項,為每個例項配置不同的通道和處理器,以滿足不同場景下的日誌記錄需求。Monolog 的靈活性和可延伸性使其成為 PHP 應用程式中進行日誌記錄的理想選擇。
<?php
declare(strict_types=1);
useMonolog\Handler\FirePHPHandler;
useMonolog\Handler\StreamHandler;
useMonolog\Logger;
require_once dirname(__DIR__).'/vendor/autoload.php';
// ① 建立日誌服務例項
$logger = new Logger('tinywan');
// ② 添加日誌處理器
$logger->pushHandler(new StreamHandler('./test.log', Logger::WARNING));
$logger->pushHandler(new FirePHPHandler());
// ③ 添加日誌記錄
$logger->warning('③ 添加日誌記錄 ');
// 註意:由於StreamHandler的第二個參數是:Logger::WARNING,所以這條日誌是不記錄的
$logger->info('③ 添加日誌記錄 ');
// ④ 添加額外的數據:1、使用上下文(context)
$logger->warning('④ 添加額外的數據:1、使用上下文(context): ',['username' => 'Tinywan']);
// ④ 添加額外的數據:2、使用加工程式(Processor)
$logger->pushProcessor(function($record){
$record['extra'] = [
'username' => 'Tinywan',
'age' => 24
];
return $record;
});
$logger->warning('④ 添加額外的數據:2、使用加工程式(Processor): ');
以上執行輸出
[2022-04-09T08:43:14.860308+00:00] tinywan.WARNING: ③ 添加日誌記錄 [] []
[2022-04-09T08:43:14.861232+00:00] tinywan.WARNING: ④ 添加額外的數據:1、使用上下文(context): {"username":"Tinywan"} []
[2022-04-09T08:43:14.861272+00:00] tinywan.WARNING: ④ 添加額外的數據:2、使用加工程式(Processor): [] {"username":"Tinywan","age":24}
① 建立日誌服務例項
這個例項後將在程式碼中用到。唯一的參數是通道的名稱,它在你有多個日誌服務例項的時候很有用。
② 添加日誌處理器
上面的程式碼中註冊了兩個處理器到棧中,以便允許使用兩種不同的方式來處理日誌記錄。
添加順序是:冒泡方式,按照棧【先進後出(First-In/Last-Out)】順序添加。
其中
StreamHandler
在棧的最底部,它會把記錄都保存到硬碟上。
註意 :這個日誌服務例項自己是不是知道如何處理一條日誌記錄的。它把記錄代理給了一些處理器。
註意 FirePHPHandler 是被先呼叫的,因而它被添加到了棧頂。這允許你臨時添加一個禁止冒泡的處理器從而允許你覆蓋其他配置的日誌(處理器)。
③ 添加日誌記錄
添加簡單的文本訊息
註意
:由於StreamHandler的第二個參數是:
Logger::WARNING
,所以只會記錄warning的日誌,其他日誌不會被記錄。即:
$logger->info('③ 添加日誌記錄 ');
不會被記錄
④ 添加額外的數據
為記錄添加額外的數據。monolog 提供了兩種不同的方式來為簡單的文本訊息增加額外的資訊
1. 使用上下文(context)
第一種方式是使用上下文(
context
),這允許你在傳遞記錄的時候傳遞一個陣列格式的數據
// ④ 添加額外的數據:1、使用上下文(context)
$logger->warning('④ 添加額外的數據:1、使用上下文(context): ',['username' => 'Tinywan']);
簡單的處理器(比如
StreamHandler
)將只是把陣列轉換成字串。而復雜的處理器則可以利用上下文的優點(如 FirePHP 則將以一種優美的方式顯示陣列)。
2. 使用加工程式(
Processor
)
第二種方式是使用加工程式來為所有的記錄添加額外數據。加工程式可以是任何可以呼叫的函式。
加工程式接收日誌記錄作為參數,並且需要在修改(設定)了
extra
欄位後,再返回日誌記錄。再次記錄日誌,則新日誌會添加新的額外的日誌。讓我們來寫一個添加一些假數據的加工程式
// ④ 添加額外的數據:2、使用加工程式(Processor)
$logger->pushProcessor(function($record){
$record['extra'] = [
'username' => 'Tinywan',
'age' => 24
];
return $record;
});
$logger->warning('④ 添加額外的數據:2、使用加工程式(Processor): ');
Monolog提供了一些內建的加工程式,你可以在你的計畫中使用它們。
[warning] 小技巧:加工程式可以被註冊到一個特定的處理器上而不是直接在日誌服務例項上,從而可以只在對應的處理器上生效。
如果你單獨使用 Monolog, 並且在尋找一種簡單的方式來配置許多處理器,那可以用 theorchard/monolog-cascade。 它可以幫你使用PHP陣列、YAML或者JSON來構建復雜的日誌配置。
使用通道
通道是一種非常棒的方式來區分是套用的哪個部份的日誌被記錄下來的。這通常在大型計畫中非常有用(而且被Symfony2的MonologBundle所使用)。
假設有兩個日誌服務例項共享了一個處理器,這個處理器將日誌寫入單個日誌檔。通道則將允許你來區分是哪個日誌服務例項記錄了哪條日誌。你可以很簡單地透過通道來篩選日誌。
useMonolog\Handler\FirePHPHandler;
useMonolog\Handler\StreamHandler;
useMonolog\Logger;
require_once dirname(__DIR__).'/vendor/autoload.php';
// ① 建立日誌處理器
$stream = new StreamHandler('./test2.log', Logger::DEBUG);
$fire = new FirePHPHandler();
// ② 建立安全相關的日誌服務通道
$securityLogger = new Logger('security');
$securityLogger->pushHandler($stream);
$securityLogger->pushHandler($fire);
// 添加日誌記錄
$securityLogger->debug('CSRF:1');
$securityLogger->debug('CSRF:2');
// ③ 建立支付相關的日誌服務通道
$payLogger = new Logger('pay');
$payLogger->pushHandler($stream);
$payLogger->pushHandler($fire);
// 添加日誌記錄
$payLogger->debug('支付寶支付');
$payLogger->debug('微信支付');
// ④ 使用複制方式建立培訓相關通道
$trainLogger = $payLogger->withName('train');
$trainLogger->pushHandler($stream);
$trainLogger->pushHandler($fire);
// 添加日誌記錄
$trainLogger->debug('[複制方式] 電商培訓');
$trainLogger->debug('[複制方式] 直播培訓');
以上輸出日誌結果
[2022-04-09T09:06:46.284206+00:00] security.DEBUG: CSRF:1 [] []
[2022-04-09T09:06:46.285166+00:00] security.DEBUG: CSRF:2 [] []
[2022-04-09T09:06:46.285215+00:00] pay.DEBUG: 支付寶支付 [] []
[2022-04-09T09:06:46.285250+00:00] pay.DEBUG: 微信支付 [] []
[2022-04-09T09:06:46.285279+00:00] train.DEBUG: [複制方式] 電商培訓 [] []
[2022-04-09T09:06:46.285279+00:00] train.DEBUG: [複制方式] 電商培訓 [] []
[2022-04-09T09:06:46.285329+00:00] train.DEBUG: [複制方式] 直播培訓 [] []
[2022-04-09T09:06:46.285329+00:00] train.DEBUG: [複制方式] 直播培訓 [] []
自訂日誌格式
在monolog中,可以很簡單地來自訂日誌的格式,無論是寫入檔、套接字、信件、資料庫還是其他處理器。大多數處理器都是用
$record['formatted']
這個值來自動寫入日誌裝置。
這個值依賴格式化器的配置。你可以選擇預定義的格式化器類,也可以自己寫一個(比如一個可讀的多行文字檔案)。
// 預設的日期格式是 "Y-m-d H:i:s"
$dateFormat = "Y-m-d H:i:s";
// 預設的輸出格式是 "[%datetime%] %channel%.%level_name%: %message% %context% %extra%\n"
$output = "[%datetime%] [%channel%] [%level_name%] %message% %context% %extra% \n";
// ① 建立一個格式化器
$formatter = new LineFormatter($output, $dateFormat);
// ② 建立日誌處理器
$stream = new StreamHandler('./test3.log', Logger::DEBUG);
$stream->setFormatter($formatter);
// ③ 建立安全相關的日誌服務通道
$trainLogger = new Logger('train');
$trainLogger->pushHandler($stream);
// 添加日誌記錄
$trainLogger->debug('[複制方式] 電商培訓');
$trainLogger->debug('[複制方式] 直播培訓');
日誌輸出
[2022-04-0909:17:10] [train] [DEBUG] [複制方式] 電商培訓 [] []
[2022-04-0909:17:10] [train] [DEBUG] [複制方式] 直播培訓 [] []
可以在多個處理器之間復用同一個格式化器,並且在多個日誌服務例項間共享這些處理器。
JSON 格式寫入記錄
// ① 建立一個Json格式化器
$jsonFormatter = new JsonFormatter();
// ② 建立日誌處理器
$stream = new StreamHandler('./test4.log', Logger::DEBUG);
$stream->setFormatter($jsonFormatter);
// ③ 建立安全相關的日誌服務通道
$trainLogger = new Logger('train');
$trainLogger->pushHandler($stream);
// 添加日誌記錄
$trainLogger->debug('[複制方式] 電商培訓',[
'username' => 'Tinywan',
'age' => 24
]);
輸出日誌
{
"message": "[複制方式] 電商培訓",
"context": {
"username": "Tinywan",
"age": 24
},
"level": 100,
"level_name": "DEBUG",
"channel": "train",
"datetime": "2022-04-09T10:02:52.568338+00:00",
"extra": {}
}
日誌級別
Monolog 支持一下RFC 5424中的日誌級別:
偵錯
DEBUG (100)
: 詳細的偵錯資訊。
資訊
INFO (200)
: 有意義的事件,比如使用者登入、SQL日誌。
提示
NOTICE (250)
: 正常但是值得註意的事件。
警告
WARNING (300)
: 異常事件,但是並不是錯誤。比如使用了廢棄了的API,錯誤地使用了一個API,以及其他不希望發生但是並非必要的錯誤。
錯誤
ERROR (400)
: 執行時的錯誤,不需要立即註意到,但是需要被專門記錄並監控到。
嚴重
CRITICAL (500)
: 邊界條件/危篤場景。比如套用元件不可用了,未預料到的異常。
警報
ALERT (550)
: 必須立即采取行動。比如整個網站都掛了,資料庫不可用了等。這種情況應該發送簡訊警報,並把你叫醒。
緊急
EMERGENCY (600)
: 緊急請求:系統不可用了。
使用清單
常用 Handler
Monolog內建很多很實用的handler,它們幾乎囊括了各種的使用場景,這裏介紹一些使用的
StreamHandler:把記錄寫進PHP流,主要用於日誌檔。
SyslogHandler:把記錄寫進syslog。
ErrorLogHandler:把記錄寫進PHP錯誤日誌。
NativeMailerHandler:使用PHP的mail()函式發送日誌記錄。
SocketHandler:透過socket寫日誌。
AmqpHandler:把記錄寫進相容amqp協定的服務。
BrowserConsoleHandler:把日誌記錄寫到瀏覽器的控制台。由於是使用瀏覽器的console物件,需要看瀏覽器是否支持。
RedisHandler:把記錄寫進Redis。
MongoDBHandler:把記錄寫進Mongo。
ElasticSearchHandler:把記錄寫到ElasticSearch服務。
BufferHandler:允許我們把日誌記錄緩存起來一次性進行處理。
常用 Processor
前面說過,Processor可以為日誌記錄添加額外的資訊,Monolog也提供了一些很實用的processor
IntrospectionProcessor:增加當前指令碼的檔名和類名等資訊
WebProcessor:增加當前請求的URI、請求方法和存取IP等資訊
MemoryUsageProcessor:增加當前記憶體使用情況資訊
MemoryPeakUsageProcessor:增加記憶體使用高峰時的資訊
常用 Formatter
同樣的,這裏介紹幾個內建的Formatter:
LineFormatter:把日誌記錄格式化成一行字串。
HtmlFormatter:把日誌記錄格式化成HTML表格,主要用於信件。
JsonFormatter:把日誌記錄編碼成JSON格式。
LogstashFormatter:把日誌記錄格式化成logstash的事件JSON格式。
ElasticaFormatter:把日誌記錄格式化成ElasticSearch使用的數據格式。
核心概念
每一個日誌服務例項 (Logger) 都有一個通道(名稱),並有一個處理器 (Handler)棧. 無論何時你添加一條 記錄 到對應的日誌服務例項,這個處理器棧將被遍歷一遍:每個處理器都將依次決定是否要處理這條記錄,而如果要處理,則遍歷結束(譯註:類似DOM事件冒泡)。
這樣子可以建立非常靈活的日誌配置。比如一個 StreamHandler 可以把所有日誌都寫入磁盤,而上面加個MailHandler 可以把錯誤日誌作為信件發送出去。處理器還有一個 bubble 參數為 false 則意味著 MailHandler 將不會把自己已處理過的記錄繼續冒泡給 StreamHandler.
你可以建立許多日誌服務例項(Logger),每一個則定義一個通道(比如資料庫、請求、路由...)。而每一個日誌服務例項都可以組合各種各樣的處理器,可以共享處理器也可以不共享。這個通道將會在日誌中反映出來,從而允許你可以很容易地檢視或者篩選記錄。
每一個處理還會有一個格式化器(Formatter)。如果你沒有配置一個,則一個有意義的預設的格式化器將被建立。格式化器用來規範化並格式化輸入的記錄,以便處理器能輸出一些有用的資訊。
不支持自訂的嚴重性級別。只支持使用RFC 5424中定義的八個級別(偵錯/Debug、資訊/Info、提示/Notice、警告/Warning、錯誤/Error、嚴重/Critical、警報/Alert、緊急/Emergency)來作為基本的篩選目的。不過,如果為了排序或者其他需要靈活性的使用場景,你可以添加加工程式(Processor)從而可以在(處理器)處理前添加額外的資訊(標簽、使用者IP...)。