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编辑
,原因: