Laravel 實現使用中介軟體記錄所有請求資訊以及日誌純 JSON 格式儲存

huanling發表於2020-04-10

歡迎閱讀本文,希望能給與你一定的幫助,Laravel新人一枚,技術不過關,如有撰寫錯誤的地方,懇請不吝賜教!

Laravel常規寫入的Log是這樣的,帶有日期和日誌型別:

[2020-04-10 09:37:23] local.INFO: record request message

本文最終實現寫入的Log記錄是純Json格式儲存:

{"datetime":"2020-04-10 09:27:36","message":"record request message","url":"URL","method":"POST"}

配置 config/logging.php

新增自定義通道 request,有時需要完全控制已存在通道的 Monolog: 比如,你可能想要為給定通道的日誌處理配置自定義的 Monolog FormatterInterface 實現。

先在通道配置中定義一個 tap 陣列。 tap 陣列包含一個在通道建立後有機會用於自定義 Monolog 例項的類列表。

return [
    'channels' => [
        'request' => [
            'driver' => 'daily',
            'path' => storage_path('logs/request.log'),
            'level' => 'info',
            'days' => 5,
            'tap' => [App\Logging\CustomizeFormatter::class],
            'value_max_length' => env('REQUEST_LOG_VALUE_MAX_LENGTH', 300),
        ],
    ]
]

建立app/Logging/CustomizeFormatter.php 檔案

<?php
namespace App\Logging;

use App\Logging\CustomizeJsonFormatter;

class CustomizeFormatter
{
    /**
     * 自定義給定的日誌例項。
     *
     * @param  \Illuminate\Log\Logger  $logger
     * @return void
     */
    public function __invoke($logger)
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(new CustomizeJsonFormatter());
        }
    }
}

建立app/Logging/CustomizeJsonFormatter.php檔案

<?php


namespace App\Logging;

use Monolog\Formatter\JsonFormatter;

class CustomizeJsonFormatter extends JsonFormatter
{
    // 重構
    public function format(array $record): string
    {
        $newRecord = [
            'datetime' => $record['datetime']->format('Y-m-d H:i:s'),
            'message' => $record['message'],
        ];

        if (!empty($record['context'])) {
            $newRecord = array_merge($newRecord, $record['context']);
        }
        $json = $this->toJson($this->normalize($newRecord), true) . ($this->appendNewline ? "\n" : '');

        return $json;
    }
}

建立中介軟體命令:php artisan make:middleware RecordRequestMessage

命令執行後,檔案會生成在app/Http/Middleware/RecordRequestMessage.php

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;

class RecordRequestMessage
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 記錄所有請求資訊
        $requestMessage = [
            'url' => $request->url(),
            'method' => $request->method(),
            'ip' => $request->ips(),
            'path' => $request->path(),
            'headers' => $request->header(),
            'query' => $request->query()
        ];

        if ($request->file()) {
            // 檔案內容不做日誌記錄,使用<file>做標識
            $requestMessage['body'] = '<file>';
        }
        else {
            // 獲取請求體Body資訊
            $bodyContent = $request->all();
            // 從.env檔案中獲取引數內容的長度
            $parameterLength = \config('logging.channels.request.value_max_length');

            if ($bodyContent && in_array($request->method(), ['POST', 'PATCH']))
            {
                foreach ($request->all() as $key => $value) {
                    if (Str::length($value) > $parameterLength) {
                        // 引數內容的長度過大的進行裁剪
                        $bodyContent[$key] = Str::limit($value, $parameterLength);
                    }
                }
            }
            $requestMessage['body'] = $bodyContent;
        }

        Log::channel('request')->info('record request message', $requestMessage);
        return $next($request);
    }
}
app/Http/Kernel.php加入全域性中介軟體
protected $middleware = [
    \App\Http\Middleware\RecordRequestMessage::class,
];
最後訪問請求站點,即可在storage/logs/request-xxxx.log中看到日誌最終效果
{"datetime":"2020-04-10 09:27:36","message":"record request message","url":"URL","method":"POST"}

後續:精簡版

只需要配置config/logging.php使用內建的Monolog方法實現即可,不需要配置tap陣列

use Monolog\Formatter\JsonFormatter;
use Monolog\Handler\StreamHandler;
return [
    'channels' => [
        'request' => [
            'driver' => 'daily',
            'path' => storage_path('logs/request.log'),
            'level' => 'info',
            'days' => 5,
            'handler' => StreamHandler::class,
            'formatter' => JsonFormatter::class,
            'value_max_length' => env('REQUEST_LOG_VALUE_MAX_LENGTH', 300),
        ],
    ]
]

日誌《Laravel 7 中文文件》上沒有介紹到handlerformatter這些配置項,對新手而然容易忽略這些便捷的實現方法,從而使用較為複雜的方式來實現最終效果,可查閱官網https://learnku.com/docs/laravel/7.x/logging#creat... 以及Monolog的文件 https://github.com/Seldaek/monolog

Laravel 實現使用中介軟體記錄所有請求資訊以及日誌純 JSON 格式儲存

參考

日誌《Laravel 7 中文文件》
中介軟體《Laravel 7 中文文件》
https://www.jianshu.com/p/b8e0ef4ef249

本作品採用《CC 協議》,轉載必須註明作者和本文連結

萬物之初,起始於幻。世界大道,終歸於零。
破虛空,握乾坤,逆天命,唯我不凡。

相關文章