failed() 方法仅在任务抛出异常时触发,若被 try/catch 吞掉或静默返回则不执行;Redis 队列需显式配置 'failer' => 'database' 才写入 failed_jobs 表;应监听 JobFailed 事件统一处理告警,并记录完整异常、payload 和 attempts。

队列任务失败时 failed() 方法没被调用?
多数人以为只要在任务类里写个 failed() 方法,异常就能自动记录——但 Laravel 默认只对「已抛出的异常」触发该方法,如果任务里用了 try/catch 吞掉异常、或调用失败但没 throw,failed() 就不会执行。
实操建议:
- 确保任务中所有可能失败的操作最终都
throw异常(比如数据库操作失败、HTTP 请求返回 5xx 后主动throw new Exception) - 避免在任务中静默处理错误,例如
if (!$res) return;这类逻辑会让失败“消失” - 检查队列驱动是否启用失败表:运行
php artisan queue:failed-table+php artisan migrate,否则即使触发failed()也存不到数据库
想记录完整异常堆栈,不能只打 $exception->getMessage()
getMessage() 只返回错误提示,丢失行号、文件路径、上下文和嵌套异常。线上排查时基本没用。
实操建议:
- 在
failed()方法里用$exception->getTraceAsString()或(string) $exception获取全量信息 - 如果用日志通道(如
Log::error()),直接传入整个$exception对象:Laravel 的日志底层会自动展开它 - 注意:不要手动拼接字符串记录,比如
Log::error('Job failed: ' . $e->getMessage()),这会丢掉关键 trace
使用 redis 驱动时,失败任务不进 failed_jobs 表?
Redis 队列默认不写入 failed_jobs 表,除非显式配置了 failer。很多人以为只要数据库有这张表,Redis 队列失败也会自动落库——其实不会。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
实操建议:
- 在
config/queue.php的redis配置下加'failer' => 'database' - 确认
'connection'指向的是正确的数据库连接名(不是default就容易错配) - 若用 Horizon,它自带失败存储逻辑,此时可关掉 Laravel 原生 failer,改用
horizon:supervisor的日志聚合能力
自定义失败处理逻辑(比如发钉钉告警)该挂在哪里?
别在每个任务的 failed() 里重复写通知代码——维护成本高,且漏掉新任务就收不到告警。
实操建议:
- 监听
Illuminate\Queue\Events\JobFailed事件,在EventServiceProvider中注册闭包或监听器 - 在监听器里统一处理:记录日志、提取关键字段(
$event->job->getName()、$event->exception->getMessage())、调用通知服务 - 注意:事件监听器本身不能失败,否则会静默丢弃;建议把通知逻辑包在
try/catch里,并记录自身异常
真正难的不是记日志,而是让日志里能还原出「谁、在什么上下文、调了什么、返回了什么」。堆栈只是起点,$job->payload 和 $job->attempts() 这些字段经常被忽略,但它们才是区分偶发失败和稳定崩溃的关键。

















