Laravel/Lumen 自定义错误日志格式

  • 有时需要完全控制已存在通道的 Monolog: 比如,你可能想要为给定通道的日志处理配置自定义的 MonologFormatterInterface
  • 实现:先在通道配置中定义一个 tap 数组tap 数组包含一个在通道创建后有机会用于自定义 Monolog 实例的类列表

格式化日志【 添加日志请求ID 】#

1. 在 config/logging.php 创建对应的处理渠道#

//自定义频道
'carverlog' => [
    //日志驱动模式
    'driver' => 'daily',
    //日志存放路径
    'path' => storage_path('logs/carver/carver.log'),
    //自定义Monolog实例
    'tap' => [App\Logging\MyLogFormatter::class],
    //日志等级
    'level' => 'info',
    //日志分片周期,多少天一个文件
    'days' => 7,
]

2. 在 app/Logging 目录下创建 MyLogFormatter 自定义 Monolog 实例的类#

<?php

namespace App\Logging;

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

提示:【官方解释】一旦在通道中有了 tap 选项配置,就要准备用于自定义 Monolog 实例的类。这种类这需要一个方法: __invoke,它接受一个 Illuminate\Log\Logger 实例作为其参数。 Illuminate\Log\Logger 实例将所有方法调用代理到基础的 Monolog 实例:#

<?php

namespace App\Logging;

class CustomizeFormatter
{
    /**
     * 自定义给定的日志实例。
     *
     * @param  \Illuminate\Log\Logger  $logger
     * @return void
     */
    public function __invoke($logger)
    {
        foreach ($logger->getHandlers() as $handler) {
            $handler->setFormatter(...);
        }
    }
}

Tip:所有的 “tap” 类都是由 服务容器 解析的,因此任何依赖它们的构造器都会自动被注入。这段算是官方文档的说明了。

3. 在 app/Logging 目录下创建,用于重写 Monolog 的 LineFormatter 格式化处理器,LineFormatter 是 Monolog 默认的格式化处理器【例如:添加自定义uuid】#

<?php

namespace App\Logging;

use Illuminate\Support\Str;
use Monolog\Formatter\LineFormatter as BaseLineFormatter;
use Monolog\Formatter\NormalizerFormatter;

class LineFormatters extends BaseLineFormatter
{
    const NEW_SIMPLE_FORMAT = "[%datetime%] [%uuid%] %channel%.%level_name%: %message% %context% %extra%\n";

    public function format(array $record)
    {
        $output = self::NEW_SIMPLE_FORMAT;
        $vars   = (new NormalizerFormatter())->format($record);
        $vars['uuid'] = 'uuid:' . Str::uuid();
        foreach ($vars['extra'] as $var => $val) {
            if (false !== strpos($output, '%extra.' . $var . '%')) {
                $output = str_replace('%extra.' . $var . '%', $this->stringify($val), $output);
                unset($vars['extra'][$var]);
            }
        }
        if (isset($vars['context']['exception']) && !empty($vars['context']['exception'])) {
            $vars['message'] = '';
            $vars['context'] = $vars['context']['exception'];
            if (isset($vars['context']['trace'])) {
                unset($vars['context']['trace']);
            }
            if (isset($vars['context']['previous'])) {
                unset($vars['context']['previous']);
            }
        }

        foreach ($vars as $var => $val) {
            if (false !== strpos($output, '%' . $var . '%')) {
                $output = str_replace('%' . $var . '%', $this->stringify($val), $output);
            }
        }
        // remove leftover %extra.xxx% and %context.xxx% if any
        if (false !== strpos($output, '%')) {
            $output = preg_replace('/%(?:extra|context)\..+?%/', '', $output);
        }
        return $output;
    }
}

 4. 处理结果如下#

[2023-03-12 16:34:10] [uuid:69bbd51e-bba2-4470-9eb4-21a409e85334] local.INFO: 提示信息 {"site_name":"carver","site_id":"1"} []

格式化日志【 格式化日志内容为JSON格式 】#

1. 在格式化日志的 LineFormatters 文件中,需要重写 Monolog 的 JsonFormatter 格式化处理器#

<?php

namespace App\Logging;

use Monolog\Formatter\JsonFormatter as BaseJsonFormatter;

class LineFormatters extends BaseJsonFormatter
{
    /**
     * @param array $record
     * @return LineFormatters|array|bool|float|int|mixed|string|string[]|null
     * @desc 格式化为Json格式
     */
    public function format(array $record)
    {
        // 这个就是最终要记录的数组,最后转成Json并记录进日志
        $newRecord = [
            'datetime' => $record['datetime']->format('Y-m-d H:i:s'),
            'message' => $record['message'],
            'extra' => $record['extra'],
        ];

        if (!empty($record['context'])) {
            $newRecord = array_merge($newRecord, $record['context']);
        }
        // 这是最终返回的记录串,可以按自己的需求改
        $json = $this->toJson($this->normalize($newRecord), true) . ($this->appendNewline ? "\n" : '');

        return $json;
    }
}

2. 处理结果如下#

{"datetime":"2023-03-12 16:43:05","message":"提示信息","extra":[],"site_name":"carver","site_id":"1"}

 

作者:Carver-大脸猫

出处:https://www.cnblogs.com/carver/articles/17209928.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

转载请注明原处

posted @   Carver-大脸猫  阅读(488)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu