Custom Formatter of the Logger

PHP:7.2

Laravel:5.8

Laravel 本身提供了很好的 Log 機制,大部份的時候都可以透過原本 Framework 內建的方法解決大部份的 log 需求。

但也因為 Framework 都包裝好了,因此如果需要自訂 Log Format 就必須另外自行處理。

以下就以『將 log 轉成 JSON 形式儲存』當範例。

Create Formatter

Laravel 的 Log 底層是透過 Monolog 去寫入 log 的,因此我們需要從 Monolog\Formatter 下手,才能處理我們要的 Formatter。

app/Components/JsonFormatter.php

namespace App\Components;

// 繼承 Monolog 的 JsonFormatter 來達到我們的需求
use Monolog\Formatter\JsonFormatter as BaseJsonFormatter;

class JsonFormatter extends BaseJsonFormatter
{
    public function format(array $record)
    {
        // 加入我們需要的資料
        $newRecord = [
            'time' => $record['datetime']->format('Y-m-d H:i:s'),
            // message 就是我們原本的 log
            // 注意:這邊的 log 已經被轉換成字串了,並不會是 array,因此建議直接以 JSON 傳遞資料
            'result' => json_decode($record['message'], true),
        ];

        // Contextual Information
        if (!empty($record['context'])) {
            $newRecord = array_merge($newRecord, $record['context']);
        }

        // 轉換成 JSON 並換行
        $json = $this->toJson($newRecord) . ($this->appendNewline ? "\n" : '');

        return $json;
    }
}

Create Logger

接著我們需要定義一個 Logger,並載入剛剛的 Formatter,再讓 Framework 直接使用此 Logger

app/Services/LogService.php

namespace App\Services;

use App\Components\JsonFormatter;

class LogService
{
    /**
     * Customize the given logger instance.
     *
     * @param  \Illuminate\Log\Logger  $logger
     * @return void
     */
    public function __invoke($logger)
    {
        foreach ($logger->getHandlers() as $handler) {
            // 設定 JsonFormatter
            $handler->setFormatter(new JsonFormatter());
        }
    }
}

Logging Config

在 Laravel 中,可以透過 config/logging.php 這支設定檔裡控制不同的 log channels,因此我們可以自行改寫或定義自己的 channel。

config/logging.php

[
    'channels' => [
        // 新建一個 access channel
        'access' => [
            // daily driver 可以讓 log file 以 date 方式呈現
            'driver' => 'daily',
            'path' => storage_path('logs/laravel.log'),
            // 透過 tap 參數載入自行定義的 Logger
            'tap' => [App\Services\LogService::class],
            'level' => 'debug',
        ]
    ],
]

Usage

接著透過原本 Laravel 提供寫入 log 的方式就可以完成啦!

Log::channel('access')->info(json_encode('This is TEST'));
Categories: Laravel