動態

詳情 返回 返回

PHP項目中引入並monolog進行日誌管理 - 動態 詳情

在 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中就會創建相應的日誌記錄文件(如下圖所示):
image.png

默認日誌文件app.log中的內容如下:
image.png

自定義channel文件中的內容如下:
image.png

自定義目錄以及channel文件中的內容如下:
image.png

四、關鍵配置説明

  1. 日誌處理器:

    • RotatingFileHandler:按天拆分日誌文件(自動添加日期後綴),並限制保留天數。
    • 可根據需求添加其他處理器(如 RedisHandler 寫入 Redis、MailHandler 發送郵件告警等)。
  2. 日誌級別:
    Monolog 支持 DEBUG、INFO、WARNING、ERROR、CRITICAL 等級別(從低到高),通過處理器的級別參數控制記錄範圍。
  3. 日誌格式:
    自定義 LineFormatter 可調整日誌內容,例如添加請求 ID、用户信息等上下文數據。

追加:

上面的日誌記錄方式日誌每天一個文件比較分散,但是在實際項目中想把所有加日誌的錯誤信息都寫到一個文件中, 這樣查看錯誤信息的時候就只看這個文件就行了,因此增加了stream方法.
MonologUtil::stream('測試流式寫入', ['user_id' => 1, 'action' => 'test_stream']);

執行完成後效果如下(直接在monologs目錄下就有個stream.log日誌文件,方便查看):
image.png

user avatar xixindeshoutao 頭像 banxiazhimo 頭像 dbkangaroo 頭像 vanve 頭像
點贊 4 用戶, 點贊了這篇動態!
點贊

Add a new 評論

Some HTML is okay.