Hyperf日志不显示请求ID,根本原因是ContextHandler(实为自定义Processor)未在logger配置中正确注册到formatter的processor数组,导致Context::get('request_id')无法注入日志context;需确保中间件已设值、Processor在同协程链路中调用、避免异步子协程丢失上下文。

Hyperf日志不显示请求ID,是因为ContextHandler没生效或没配对
默认的 Monolog\Handler\StreamHandler 不会自动读取协程上下文,ContextHandler 是 Monolog 官方提供的 Processor(非 Handler),必须显式注册到 logger 实例或 formatter 所在的 channel 配置中,否则 Context::get('request_id') 的值永远不会进入日志 message 或 context 字段。
- 确认你用的是
Monolog\Processor\IntrospectionProcessor或自定义 Processor,而不是误以为某个 Handler 能“自动注入”上下文 -
ContextHandler本身不存在于 Monolog 或 Hyperf 核心中——常见误区是把自定义 Processor 命名为 ContextHandler,但实际需继承Monolog\Processor\ProcessorInterface - Hyperf 推荐做法是在
config/autoload/logger.php的formatter配置里挂载 Processor,而非在 Handler 构造时传入 - Processor 必须返回带
request_id的数组,且该键名要与你在业务中Context::set('request_id', ...)设置的一致
在 logger.php 中正确注册上下文处理器
别只改 handler,重点在 formatter 的 processor 配置项。Hyperf 允许为每个 channel 单独指定 processor 数组,它会在日志写入前被依次调用。
- 先确保已在中间件或全局生命周期中设置了 request_id:
Context::set('request_id', $request->getHeaderLine('X-Request-ID') ?: uniqid('req_')) - 在
config/autoload/logger.php的目标 channel(如'file')下,添加'processor' => [App\Logger\RequestIdProcessor::class] -
RequestIdProcessor类需实现__invoke(array $record): array,并在$record['context']['request_id'] = Context::get('request_id') ?: 'unknown' - 注意:Processor 运行在当前协程内,无需手动
Context::copy(),只要调用发生在同一协程链路中即可
为什么用了 Context::set 却 still 没日志?检查这三处
最常踩的坑不是代码写错,而是上下文根本没传到日志写入那一刻——尤其在异步、defer、go 启动的子协程里。
-
Context::set()只作用于当前协程,go(function () { Context::get('request_id') })一定为null,除非你显式go(function () use ($context) { Context::set($context); }) - 数据库查询、Redis 调用等异步操作若触发了新协程(比如
Co::sleep()或底层驱动协程切换),原始request_id就断了 - 日志写入发生在
@AfterReturning切面里?那方法抛异常时该切面压根不执行——必须用@Around统一包裹,并在try/catch后再组装日志字段
Formatter 里直接读 Context 更稳,但要注意时序
比起靠 Processor 注入,有些场景更适合在自定义 Formatter 的 format() 方法里动态取值,因为 Formatter 总是在日志真正序列化前最后一步执行,上下文仍可读。
- 继承
Monolog\Formatter\LineFormatter,重写format(),开头加一行:$record['message'] = '[' . (Context::get('request_id') ?: 'no-id') . '] ' . $record['message']; - 不要在构造函数里缓存
Context::get()结果,那只是初始化时刻的快照,后续协程里全失效 - 如果用了
JsonFormatter,记得把request_id写进$record['context'],否则 JSON 输出里看不到 - 注意
LineFormatter第五个参数$allowInlineLineBreaks设为true,否则换行符会被转义,日志平台解析困难


















