Laravel终端/stdout日志莫名添加WARN关键字致JSON格式异常
Laravel自定义日志通道输出到stdout时自动添加WARN关键字的解决办法
我在使用Laravel内置日志通道和自定义日志通道时发现,将日志输出到终端/stdout时,每条日志前会自动追加WARN关键字,但输出到日志文件时显示正常。我的需求是在终端输出JSON格式日志,这个额外的关键字导致日志变成了字符串格式,无法被正确解析。
相关配置代码
日志通道配置
'methodlog' => [ 'driver' => 'custom', 'via' => App\Logging\MethodLogChannel::class, 'formatter' => App\Logging\CustomJsonFormatter::class, 'name' => 'methodlog', 'with' => [ 'path' => storage_path('logs/laravel.log'),//'php://stdout', 'level' => env('LOG_LEVEL', 'debug'), ], ],
CustomJsonFormatter.php
class CustomJsonFormatter extends JsonFormatter { private $DEFAULT_JSON_FLAGS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR ; public function format(LogRecord $record): string { $formatted = $record['context']; if ($this->includeStacktraces) { $formatted['stack_trace'] = $record['formatted']['exception']; } return $this->toJson($formatted, true) . ($this->appendNewline ? "\n" : ''); } }
MethodLogChannel.php
class MethodLogChannel { public function __invoke(array $config) { $log = new Logger($config['name']); $handler = new StreamHandler($config['with']['path'], $config['with']['level']); $handler->setFormatter(new CustomJsonFormatter()); $log->pushHandler($handler); $log->pushProcessor(new IntrospectionProcessor()); $log->pushProcessor(new WebProcessor($_SERVER)); return $log; } }
日志调用代码
Log::channel('methodlog')->info('', $log_message);
问题原因
核心问题在于自定义日志通道中,直接将字符串类型的日志级别(如'debug')传递给Monolog的StreamHandler,而StreamHandler需要的是整数类型的Monolog级别常量(如Logger::DEBUG)。当传入字符串时,Monolog无法正确解析级别,默认使用了WARNING级别,导致输出时自动添加WARN前缀。
同时,自定义Formatter中JSON flags的设置方式不符合父类设计,可能引发潜在的格式化问题。
解决方案
1. 修复日志级别转换
修改MethodLogChannel.php,使用Monolog的toMonologLevel方法将字符串级别转换为对应的常量:
<?php namespace App\Logging; use Monolog\Handler\StreamHandler; use Monolog\Logger; use Monolog\Processor\IntrospectionProcessor; use Monolog\Processor\WebProcessor; class MethodLogChannel { public function __invoke(array $config) { $log = new Logger($config['name']); // 将字符串级别转换为Monolog常量 $level = Logger::toMonologLevel($config['with']['level']); $handler = new StreamHandler($config['with']['path'], $level); $handler->setFormatter(new CustomJsonFormatter()); $log->pushHandler($handler); $log->pushProcessor(new IntrospectionProcessor()); $log->pushProcessor(new WebProcessor($_SERVER)); return $log; } }
2. 调整Formatter的JSON flags设置方式
修改CustomJsonFormatter.php,通过构造函数设置JSON flags,遵循父类的属性设计:
<?php namespace App\Logging; use Monolog\Formatter\JsonFormatter; use Monolog\LogRecord; class CustomJsonFormatter extends JsonFormatter { public function __construct(int $batchMode = self::BATCH_MODE_NEWLINES, bool $appendNewline = true) { parent::__construct($batchMode, $appendNewline); $this->jsonFlags = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_INVALID_UTF8_SUBSTITUTE | JSON_PARTIAL_OUTPUT_ON_ERROR; } public function format(LogRecord $record): string { $formatted = $record['context']; if ($this->includeStacktraces && isset($record['formatted']['exception'])) { $formatted['stack_trace'] = $record['formatted']['exception']; } return $this->toJson($formatted, true) . ($this->appendNewline ? "\n" : ''); } }
3. (可选)使用Laravel内置通道简化配置
如果不需要复杂的自定义处理器逻辑,可以直接使用Laravel内置的single通道,避免手动创建通道的潜在问题:
'methodlog' => [ 'driver' => 'single', 'path' => 'php://stdout', 'level' => env('LOG_LEVEL', 'debug'), 'formatter' => App\Logging\CustomJsonFormatter::class, ],
完成以上修改后,终端输出的日志将不再包含WARN前缀,保持纯JSON格式。
内容的提问来源于stack exchange,提问作者Priyam Sinha
相关产品推荐
相关产品推荐

