在 ThinkPHP(以 5.x 為例)中引入並使用 Monolog(一款功能強大的 PHP 日誌庫),可以實現更靈活的日誌處理(如多渠道輸出、按級別拆分、格式化等)。以下是具體步驟:
一、安裝 Monolog
通過 Composer 安裝 Monolog 依賴:
composer require monolog/monolog
二、封裝 Monolog 工具類
為了在 ThinkPHP 中方便調用,建議封裝一個 Monolog 工具類,統一管理日誌實例。
<?php
namespace fast;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Handler\RotatingFileHandler;
use Monolog\Formatter\LineFormatter;
class MonologUtil
{
/**
* 日誌實例數組
* @var array
*/
private static $loggers = [];
/**
* 獲取日誌實例
* @param string $channel 日誌通道名稱
* @param string $logPath 日誌存儲路徑
* @param bool $isStream 是否開啓流日誌
* @return Logger
*/
public static function getLogger($channel = 'app', $logPath = '', $isStream = false)
{
$pathPrefix = RUNTIME_PATH . 'monologs' . DIRECTORY_SEPARATOR . date('Ym') . DIRECTORY_SEPARATOR;
//流日誌的時候所有都寫到一個文件中
$streamPathPrefix = RUNTIME_PATH . 'monologs' . DIRECTORY_SEPARATOR;
// 日誌存儲路徑處理 默認存儲到runtime/monologs目錄下
if (empty($logPath)) {
// 流日誌默認存儲到runtime/monologs目錄下
$logPath = $isStream ? $streamPathPrefix : $pathPrefix;
} else {
$logPath = $pathPrefix . trim($logPath, '/') . DIRECTORY_SEPARATOR;
}
$key = $channel . $logPath;
if ($isStream) {
$key .= 'stream';
}
// 單例模式,避免重複創建
if (isset(self::$loggers[$key])) {
return self::$loggers[$key];
}
// 初始化日誌實例
$logger = new Logger($channel);
// 確保目錄存在
if (!is_dir($logPath)) {
mkdir($logPath, 0755, true);
}
// 日誌文件路徑
$logFile = $logPath . $channel . '.log';
if ($isStream) {
$handler = new StreamHandler($logFile, Logger::DEBUG);
} else {
// 按天分割日誌,保留30天
$handler = new RotatingFileHandler($logFile, 30, Logger::DEBUG);
}
// 日誌格式設置
$formatter = new LineFormatter(
"[%datetime%] [%channel%.%level_name%] %message% %context% %extra%\n",
'Y-m-d H:i:s',
true,
true
);
$handler->setFormatter($formatter);
// 添加處理器
$logger->pushHandler($handler);
// 保存實例
self::$loggers[$key] = $logger;
return $logger;
}
/**
* 記錄DEBUG級別日誌
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
*/
public static function debug($message, $context = [], $channel = 'app', $logPath = '')
{
self::getLogger($channel, $logPath)->debug($message, $context);
}
/**
* 記錄INFO級別日誌
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
*/
public static function info($message, $context = [], $channel = 'app', $logPath = '')
{
self::getLogger($channel, $logPath)->info($message, $context);
}
/**
* 記錄WARNING級別日誌
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
*/
public static function warning($message, $context = [], $channel = 'app', $logPath = '')
{
self::getLogger($channel, $logPath)->warning($message, $context);
}
/**
* 記錄ERROR級別日誌
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
*/
public static function error($message, $context = [], $channel = 'app', $logPath = '')
{
self::getLogger($channel, $logPath)->error($message, $context);
}
/**
* 記錄CRITICAL級別日誌
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
*/
public static function critical($message, $context = [], $channel = 'app', $logPath = '')
{
self::getLogger($channel, $logPath)->critical($message, $context);
}
/**
* 流式寫入日誌到單文件中(這樣系統的所有錯誤信息都可以記錄到這個文件中)
* @param string $message 日誌信息
* @param array $context 上下文數據
* @param string $channel 日誌通道
* @param string $logPath 日誌路徑
* @return void
*/
public static function stream($message, $context = [], $channel = 'stream', $logPath = '')
{
self::getLogger($channel, $logPath, true)->error($message, $context);
}
}
三、在 ThinkPHP 中使用 Monolog
在控制器、模型或其他業務代碼中直接調用封裝好的工具類:
默認配置的日誌文件是在runtime/monologs目錄下
// 記錄 DEBUG 級別日誌
LogUtil::debug('調試信息', ['user_id' => 1, 'action' => 'view']);
// 記錄 DEBUG 級別日誌 自定義channel文件為debug
LogUtil::debug('調試信息指定channel文件', ['user_id' => 1, 'action' => 'view'],'debug');
// 記錄 DEBUG 級別日誌 自定義channel文件以及所在文件夾
//寫法1:
LogUtil::debug('調試信息', ['user_id' => 1, 'action' => 'view'],'debug_folder/debug');
//寫法2:
LogUtil::debug('調試信息', ['user_id' => 1, 'action' => 'view'],'debug','debug_folder');
// 記錄 INFO 級別日誌(如接口訪問)
LogUtil::info('用户訪問接口', [
'ip' => $this->request->ip(),
'url' => $this->request->url(),
'method' => $this->request->method()
]);
// 記錄 ERROR 級別日誌(如業務錯誤)
try {
// 模擬錯誤
throw new \Exception('訂單創建失敗');
} catch (\Exception $e) {
LogUtil::error('業務異常', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}
運行之後zairuntime/monologs中就會創建相應的日誌記錄文件(如下圖所示):
默認日誌文件app.log中的內容如下:
自定義channel文件中的內容如下:
自定義目錄以及channel文件中的內容如下:
四、關鍵配置説明
-
日誌處理器:
- RotatingFileHandler:按天拆分日誌文件(自動添加日期後綴),並限制保留天數。
- 可根據需求添加其他處理器(如 RedisHandler 寫入 Redis、MailHandler 發送郵件告警等)。
- 日誌級別:
Monolog 支持 DEBUG、INFO、WARNING、ERROR、CRITICAL 等級別(從低到高),通過處理器的級別參數控制記錄範圍。 - 日誌格式:
自定義 LineFormatter 可調整日誌內容,例如添加請求 ID、用户信息等上下文數據。
追加:
上面的日誌記錄方式日誌每天一個文件比較分散,但是在實際項目中想把所有加日誌的錯誤信息都寫到一個文件中, 這樣查看錯誤信息的時候就只看這個文件就行了,因此增加了stream方法.
MonologUtil::stream('測試流式寫入', ['user_id' => 1, 'action' => 'test_stream']);
執行完成後效果如下(直接在monologs目錄下就有個stream.log日誌文件,方便查看):