Laravel任务调度需手动集成告警,核心是在执行链路中嵌入异常捕获与通知机制,覆盖失败、超时、重试耗尽等场景,推荐统一走队列并结合failed()方法、日志通道告警及限频聚合策略。

Laravel 任务调度本身不带告警能力,必须手动补全异常捕获 + 通知触发逻辑。关键不是“配个开关”,而是把告警嵌进执行链路里,覆盖失败、超时、重试耗尽等真实场景。
在调度任务中加 try/catch 并记录告警
Laravel 的 schedule:run 每分钟拉起一次,它会逐个检查是否该执行某个任务。但任务体(比如闭包、Artisan 命令的 handle())一旦抛出未捕获异常,就只写日志,不会发通知。
你需要在任务内部主动兜底:
- 对闭包任务,直接用
try/catch包裹全部逻辑 - 对自定义 Artisan 命令,在
handle()最外层捕获Throwable(不只是Exception,要 catchParseError、FatalError等)
$schedule->call(function () {
try {
// 你的业务逻辑,比如调 API、写 DB、发邮件
sendWeeklyReport();
} catch (\Throwable $e) {
\Log::error('Weekly report failed', [
'message' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine(),
'trace' => $e->getTraceAsString(),
]);
// 触发通知(邮件、钉钉、Sentry)
\Notification::route('mail', 'ops@example.com')
->notify(new ScheduledTaskFailed($e));
}
})->weekly()->sundays()->at('03:00');注意:敏感参数(如 $e->getTraceAsString() 中可能含 token、密码)需脱敏后再记录或通知。
用 failed() 方法处理命令类任务失败
如果你用的是继承 Command 的自定义命令,推荐在命令类里实现 failed() 方法——它会在命令执行失败且未被 catch 时自动调用(前提是该命令被队列异步执行;如果是 schedule:run 直接同步运行,则仍需上面的 try/catch)。
不过更稳妥的做法是:所有调度任务统一走队列。例如:
$schedule->command('report:weekly')->onOneServer()->withoutOverlapping()
->weekly()->sundays()->at('03:00')
->onQueue('reports'); // 强制进队列这样命令实际由 queue:work 执行,就能利用 Laravel 队列的失败处理机制,包括自动调用 failed() 方法:
class ReportWeekly extends Command implements ShouldQueue
{
public function handle()
{
// 不用自己 try/catch,交由队列系统兜底
generateAndSendReport();
}
public function failed(\Throwable $exception)
{
\Sentry\captureException($exception);
\Notification::route('dingtalk', config('services.dingtalk.webhook'))
->notify(new QueueJobFailed($exception));
}
}结合日志通道做失败自动告警
你也可以不改任务代码,而是让日志“自己报警”。比如在 config/logging.php 中配置一个专用通道,当检测到 schedule 相关错误时自动触发通知:
- 在
.env中开启调试日志:LOG_LEVEL=debug - 自定义一个
schedule_alert日志通道,用tap处理器监听含ScheduleRunCommand或Failed to execute scheduled command的日志行 - 匹配成功后调用
Mail::to(...)->send(...)或推送钉钉 Webhook
这种方式适合已有大量调度任务、不便逐个改造的项目。
避免告警刷屏:加限频和聚合
瞬时网络抖动可能导致多个任务连续失败,如果每错一次都发一条钉钉,运维群会爆炸。
建议:
- 使用 Redis 记录最近 5 分钟内同任务的失败次数,超 3 次再告警
- 合并告警内容:把同一时间段内失败的 5 个任务汇总成一条消息,附上失败摘要和链接到 Horizon 或日志平台
- 设置静默期:某任务告警后,1 小时内同类失败不再重复通知
比如用 Redis::incr("alert:task:backup:fail:20260917") + expire 控制窗口。
基本上就这些。不复杂但容易忽略的是:别只依赖重试,告警得在第一次真正失败时就响起来。


















