应显式传递请求id:http层生成uuid并存入请求属性,分发队列任务时作为参数传入,任务类保存并在handle中用log::withcontext注入日志上下文。

怎么给每个队列任务绑定唯一请求 ID
队列任务脱离 HTTP 生命周期,request_id 不会自动传递。直接在任务里调用 request()->id 会报错或返回 null —— 因为根本没有 request 实例。
正确做法是在分发任务时,把当前上下文的 ID 显式传进去:
- HTTP 请求中生成一个稳定 ID(比如用
Str::uuid()或uniqid()+ 时间戳) - 存到
request()->attributes或中间件里,确保能被日志 channel 拿到 - 分发任务时作为参数传入:
ProcessOrder::dispatch($order, $requestId) - 任务类构造函数接收并保存:
public function __construct(Order $order, string $requestId) { $this->requestId = $requestId; }
Laravel 日志如何自动带上 request_id
靠默认的 single 或 daily channel 是不行的,它们不感知请求上下文。必须用支持「处理器」的日志驱动,比如 stack 配合 monolog 的 Processors。
在 config/logging.php 里改写你的 channel:
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily'],
'ignore_exceptions' => false,
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/laravel.log'),
'level' => 'debug',
'days' => 14,
'tap' => [App\Logging\AddRequestIdProcessor::class],
],
],
AddRequestIdProcessor 是个简单类,实现 __invoke 方法,往 record 的 context 里塞 request_id。注意:它只能拿到当前请求生命周期内的值,所以队列任务必须自己带 ID,否则这里取不到。
队列任务里怎么让日志输出 request_id
队列 worker 启动后就脱离了请求生命周期,request() 全失效。这时候不能依赖中间件或全局 request 绑定,得靠任务自身携带上下文,并在任务执行时手动注入日志上下文。
推荐在任务的 handle() 方法开头做两件事:
- 用
Log::withContext(['request_id' => $this->requestId])临时绑定 - 或者更稳妥地,在每条日志里显式传参:
Log::info('order processed', ['request_id' => $this->requestId, 'order_id' => $this->order->id]) - 避免用
Log::channel('stack')->info(...)这种写法——它不会继承withContext,除非你重写了 channel 的初始化逻辑
如果用了 Monolog 的 Logger 实例,还可以在任务构造时把 logger 实例化好,提前 setProcessor,但多数项目没必要这么重。
为什么用 UUID 而不用 request()->ip() 或 session_id()
IP 和 session_id 在队列里不可靠:worker 可能在另一台机器跑,session 可能未启用,而且它们不具备唯一性与可追踪性。一个用户多次提交、一个请求触发多个队列任务,都需要区分。
UUID 是最轻量且稳定的方案,但要注意两点:
- 不要在模型事件或 observer 里生成新 UUID —— 容易导致同一次请求产生多个 ID
- 如果用了 Horizon,它的面板默认不显示
request_id,得自己加列或用failed_jobs表的exception字段反查日志 - Redis 队列序列化时,确保
request_id是字符串类型,别传对象或 resource,否则json_encode失败静默丢任务
真正难的不是生成 ID,而是让这个 ID 穿透整个调用链:HTTP → job dispatch → queue worker → DB query → HTTP call out。每断一环,排查成本就翻倍。











