Hyperf框架封装日志类,可便捷打印日志

admin 2024-07-25 09:01:01 1771

Hyperf打印日志,原始方法比较长,可以封装一下使用。

 

我的日志文件配置如下:config/autoload/logger.php

<?php

declare(strict_types=1);

/**
 * This file is part of Hyperf.
 *
 * @link     https://www.hyperf.io
 * @document https://hyperf.wiki
 * @contact  [email protected]
 * @license  https://github.com/hyperf/hyperf/blob/master/LICENSE
 */

use App\Log;

return    [
    Log::Default->value =>    [
        'handler' =>    [
            'class' => Monolog\Handler\StreamHandler::class,
            'constructor' =>    [
                'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
                'level' => Monolog\Level::Debug,
            ],
        ],
        'processors' =>    [
               ['class' => App\Kernel\Log\AppendFlowIdProcessor::class,],
        ],
        'formatter' =>    [
            'class' => Monolog\Formatter\LineFormatter::class,
            'constructor' =>    [
                'format' => null,
                'dateFormat' => 'Y-m-d H:i:s',
                'allowInlineLineBreaks' => true,
            ],
        ],
    ],
    Log::Business->value =>    [
        'handler' =>    [
            'class' => Monolog\Handler\StreamHandler::class,
            'constructor' =>    [
                'stream' => BASE_PATH . '/runtime/logs/business.log',
                'level' => Monolog\Level::Debug,
            ],
        ],
        'processors' =>    [
               ['class' => App\Kernel\Log\AppendFlowIdProcessor::class,],
        ],
        'formatter' =>    [
            'class' => Monolog\Formatter\LineFormatter::class,
            'constructor' =>    [
                'format' => null,
                'dateFormat' => 'Y-m-d H:i:s',
                'allowInlineLineBreaks' => true,
            ],
        ],
    ],
    Log::Request->value =>    [
        'handler' =>    [
            'class' => Monolog\Handler\RotatingFileHandler::class,
            'constructor' =>    [
                'filename' => BASE_PATH . '/runtime/logs/request/request.log',
                'level' => Monolog\Level::Debug,
            ],
        ],
        'processors' =>    [
               ['class' => App\Kernel\Log\AppendFlowIdProcessor::class,],
        ],
    ],
    Log::Error->value =>    [
        'handler' =>    [
            'class' => Monolog\Handler\RotatingFileHandler::class,
            'constructor' =>    [
                'filename' => BASE_PATH . '/runtime/logs/error/error.log',
                'level' => Monolog\Level::Debug,
            ],
        ],
        'processors' =>    [
               ['class' => App\Kernel\Log\AppendFlowIdProcessor::class,],
        ],
        'formatter' =>    [
            'class' => Monolog\Formatter\LineFormatter::class,
            'constructor' =>    [
                'batchMode' => Monolog\Formatter\JsonFormatter::BATCH_MODE_NEWLINES,
                'appendNewline' => true,
            ],
        ],
    ],
];

 

上下文类:app/Kernel/Log/AppendFlowIdProcessor.php

<?php

declare(strict_types=1);

namespace App\Kernel\Log;

use Hyperf\Context\Context;
use Hyperf\Coroutine\Coroutine;
use Monolog\LogRecord;
use Monolog\Processor\ProcessorInterface;

/**
 * 打印日志时,增加上下文 flow_id 便于链路追踪
 * 同一个执行过程中 所有的 flow_id 相同
 */
class AppendFlowIdProcessor implements ProcessorInterface
{
    public const FLOW_ID = 'flow_id';

    public function __invoke(array|LogRecord $record): array|LogRecord
    {
        $record   ['extra']   ['flow_id'] = Context::getOrSet(self::FLOW_ID, uniqid());
        $record   ['extra']   ['coroutine_id'] = Coroutine::id();
        return $record;
    }
}

 

方式一:封装一个Enum类打印日志,可按需输出日志到控制台,文件路径:app/Log.php

<?php
declare(strict_types=1);

namespace App;

use App\Kernel\Log\AppendFlowIdProcessor;
use BadMethodCallException;
use Hyperf\Context\Context;
use Hyperf\Coroutine\Coroutine;
use Hyperf\Logger\LoggerFactory;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Psr\Log\LoggerInterface;

/**
 * @method void debug(string|array ...$message) 详细的调试信息。
 * @method void info(string|array ...$message) 有趣的事件。示例:用户登录、SQL日志。
 * @method void warning(string|array ...$message) 不是错误的异常事件。例如:使用被否决的API,API使用不当,不必要的事情不一定是错误的。
 * @method void error(string|array ...$message) 运行时错误,不需要立即采取措施,但通常应该记录和监控
 * @method void notice(string|array ...$message) 正常但意义重大的事件
 * @method void emergency(string|array ...$message) 系统不可用。
 * @method void alert(string|array ...$message) 必须立即采取行动。示例:整个网站关闭、数据库不可用等。这应该。触发短信提醒,唤醒您。
 * @method void critical(string|array ...$message) 情况危急。示例:应用程序组件不可用,意外异常。
 * @method void log($level, string|array ...$message) 任意级别的日志。
 */
enum Log: string
{
    case Default = "default";//默认日志通道 自己打印日志时用
    case Request = "request";//接口请求日志通道,在接口里打印日志时用 会将同一个请求的日志关联起来
    case Business = "business";//业务日志通道 业务相关的可以使用它
    case Error = "error";//错误日志通道 致命错误时

    public const DEBUG = 1;
    public const INFO = 2;
    public const WARNING = 3;
    public const ERROR = 4;


    /**
     * 配置日志前缀名字
     * @param string $name
     * @return LoggerInterface
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function init(string $name = 'app'): LoggerInterface
    {
        return di(LoggerFactory::class)->get($name, $this->value);
    }

    /**
     * 执行日志写入
     * 使用方法
     * Log::Default->info("Hello~");
     * Log::Business->init('PayNotice')->info("Business~");
     * Log::Request->info("Request~");
     * Log::Error->error("Error~");
     * @param $method
     * @param $arguments
     * @return void
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    public function __call($method, $arguments)
    {
        foreach ($arguments as $argument) {
            $msg_arr [] = is_string($argument) ? $argument : json_encode($argument, JSON_UNESCAPED_UNICODE);
        }
        $messages = implode("|", $msg_arr ??  []);
        if (method_exists(LoggerInterface::class, $method)) {
            if ($this instanceof LoggerInterface) {
                $this->$method($messages);
            } else {
                $this->init('app')->$method($messages);
            }
            if (!is_prod()) {// 若非线上,同时控制台输出日志
                $func =  [
                    'debug' => self::DEBUG,
                    'info' => self::INFO,
                    'warning' => self::WARNING,
                    'error' => self::ERROR,
                    'notice' => self::INFO,
                ];
                $level = $func [$method] ?? 0;
                 ['color' => $color, 'back' => $back] =  [
                    self::DEBUG =>  ['color' => 35, 'back' => ""],
                    self::WARNING =>  ['color' => 31, 'back' => ""],
                    self::ERROR =>  ['color' => 37, 'back' => "\033 [41m"],
                ] [$level] ??  ['color' => 32, 'back' => ""];
                $title = " [\033 [0;{$color}m{$back}{$this->value}-{$method}\033 [0m]";
                $context =  ['flow_id' => Context::get(AppendFlowIdProcessor::FLOW_ID), 'coroutine_id' => Coroutine::id()];
                // [2024-07-25 10:27:25.796827]  [default-info] {"flow_id":"66a1b80dc26e0","coroutine_id":-1} Hello~
                printf(" [%s] %s %s %s\n", di(\DateTime::class)->format('Y-m-d H:i:s.u'), $title, json_encode($context), $messages);
            }
        } else {
            throw new BadMethodCallException("Method {$method} does not exist.");
        }
    }
}

上面的使用方式如下(支持多参数输入):

<?php
Log::Default->info("Hello~",1,'sadfsa',   [1,2,3]);
Log::Default->info("Hello~");

 

方式二:简单封装一个帮助函数(app/Functions.php),不过帮助函数不支持传入多参数

<?php

declare(strict_types=1);

if (!function_exists('is_prod')) {
    function is_prod(): bool
    {
        return config('app_env', 'dev') == 'prod';
    }
}

if (!function_exists('container')) {
    /**
     * 获取当前容器
     * @return ContainerInterface
     */
    function container(): ContainerInterface
    {
        return ApplicationContext::getContainer();
    }
}

if (!function_exists('di')) {
    /**
     * 依赖注入
     * @param null $id
     * @return mixed
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function di($id = null): mixed
    {
        $container = container();
        if ($id) {
            return $container->get($id);
        }
        return $container;
    }
}

if (!function_exists('logger')) {
    /**
     * 快速写log,也可以用 App/Log
     * 使用方法
     * logger()->info("Hello~"); 相当于 Log::Default->info("Hello~");
     * logger('app','business')->info("business~"); 相当于 Log::Business->info("Business~");
     * @param string $name
     * @param string $group
     * @return LoggerInterface
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    function logger(string $name = 'app', string $group = 'default'): LoggerInterface
    {
        return di(LoggerFactory::class)->get($name, $group);
    }
}
最后于 4月前 被admin编辑 ,原因:
可爱猫?Telegram电报群 https://t.me/ikeaimao

社区声明 1、本站提供的一切软件、教程和内容信息仅限用于学习和研究目的
2、本站资源为用户分享,如有侵权请邮件与我们联系处理敬请谅解!
3、本站信息来自网络,版权争议与本站无关。您必须在下载后的24小时之内,从您的电脑或手机中彻底删除上述内容
最新回复 (1)

您可以在 登录 or 注册 后,对此帖发表评论!

返回