Laravel 实现使用中间件记录所有请求信息以及日志纯 JSON 格式保存

欢迎阅读本文,希望能给与你一定的帮助,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 协议》,转载必须注明作者和本文链接
万物之初,起始于幻。世界大道,终归于零。 破虚空,握乾坤,逆天命,唯我不凡。
huanling
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。