ThinkPHP6 日志增加请求唯一标识需通过中间件生成 trace_id 并绑定到 Request,再在自定义日志驱动 save() 中自动提取注入日志字段,确保所有通道(含 SQL)统一使用该驱动,最终实现全链路 trace_id 透传。

给 ThinkPHP6 日志增加请求唯一标识(如 log_id 或 trace_id),核心是两点:一是在请求生命周期起始处生成并绑定该 ID,二是确保所有日志写入时能自动携带它。不依赖控制器手动传参,也不靠每次调用 Log::record() 时硬编码,而是通过中间件 + 日志处理器联动实现全局透传。
在全局中间件中生成并注入请求 ID
新建中间件(如 app/middleware/RequestTraceId.php),在请求进入时生成唯一 ID,并存入 Request 对象或静态上下文,供后续日志使用:
- 推荐用
uniqid('', true)或更可靠的bin2hex(random_bytes(8))生成短唯一字符串;也可参考百度方案用微秒级时间戳拼接(($sec * 100000 + $usec / 10) & 0x7FFFFFFF) - 调用
$request->withAttr('trace_id', $id)将 ID 绑定到当前 Request 实例,后续可通过Request::attr('trace_id')获取 - 若需跨协程或异步场景兼容,可配合
think\facade\Env或自定义静态容器(如AppContext::set('trace_id', $id))
自定义日志驱动中自动注入 trace_id
在你已有的自定义日志类(如 app/driver/log/Tp6Log.php)的 save() 方法中,从 Request 或上下文提取 trace_id,并插入日志消息前缀或结构化字段:
- 在
save(array $log): bool开头加一行:$traceId = Request::hasAttr('trace_id') ? Request::attr('trace_id') : 'N/A'; - 修改日志格式,例如将原
format配置'[%s][%s] %s'扩展为'[%s][%s][%s] %s',并在写入时把$traceId作为第一个占位符 - 若使用 JSON 日志,直接在每条日志数组中追加
'trace_id' => $traceId字段,便于 ELK 或 Loki 解析
确保所有日志通道都生效
仅改驱动还不够,要确认日志配置中启用的是你的自定义驱动,并且关键日志(如 SQL、error、user)都走同一通道:
立即学习“PHP免费学习笔记(深入)”;
- 检查
config/log.php的channels.file.type指向你的类(如\app\driver\log\Tp6Log::class) - 避免部分日志被
apart_level分流到默认File驱动而丢失 trace_id,建议将所有业务相关级别(user、pay、order等)统一归入自定义通道 - SQL 日志需额外注意:它由 ORM 触发,但依然走日志系统,只要
log.channel和channels配置正确,trace_id 就会自动带上
验证和调试技巧
上线前快速验证是否生效:
- 在中间件里临时加一句
Log::info('trace_id generated: {id}', ['id' => $traceId]);,查看日志文件是否出现带 ID 的行 - 发起一次请求,用
curl -v http://your.app/test,再查对应时间点的日志,确认多条日志(access、sql、user)的 trace_id 完全一致 - 如果用 Monolog 驱动(如 SocketHandler),需在
save()中显式把$traceId注入$context数组,而非只改 format 字符串



















