laravel生产环境定时任务需确保crontab正确配置、app_env=production,并通过日志、邮件、totem等手段监控执行状态与失败告警。

在Laravel生产环境里,定时任务一旦静默失败,日志不写、告警不发、业务数据就慢慢堆积或过期,你直到用户投诉才察觉——这不是小概率事件,而是未配置监控的必然结果。
确认调度器是否真正在运行
先验证底层链路是否通:登录服务器,执行crontab -l,确认存在且仅有一条形如 * * * * * cd /var/www/myapp && php artisan schedule:run >> /dev/null 2>&1 的条目。缺这条,整个调度系统就是空转。
手动触发一次调度:在项目根目录下运行 php artisan schedule:run,观察终端输出。若看到 Running scheduled command: app:send-daily-report 类似行,说明Kernel中定义的任务能被识别;若只输出空白或报错“Command not found”,说明命令未注册或APP_ENV不是production。
【APP_ENV必须为production】,否则schedule:run会直接退出,不报错也不执行任何任务。
给关键任务加执行日志与失败通知
方法一:用sendOutputTo()和emailOutputOnFailure()
在app/Console/Kernel.php的schedule()方法中,为每日报表任务添加日志路径和失败邮件:
$schedule->command('reports:daily')->dailyAt('08:00')->sendOutputTo(storage_path('logs/reports-daily.log'))->emailOutputOnFailure('ops@company.com');
这一步会让每次执行的标准输出和错误输出都追加进指定日志文件;只要任务抛出未捕获异常或exit非0,Laravel会自动发邮件——前提是已配置好MAIL_MAILER等邮件驱动。
方法二:在命令类内部手动记录
打开app/Console/Commands/Reports/DailyReport.php,在handle()开头加:
\Log::channel('daily')->info('Daily report task started at ' . now());
并在try/catch末尾补上:
} catch (\Exception $e) { \Log::channel('daily')->error('Daily report failed', ['exception' => $e->getMessage()]); throw $e; }
注意:log channel需在config/logging.php中预先定义,否则日志会写进默认通道,混在laravel.log里难追踪。
检查任务是否被跳过或并发阻塞
第一步:运行 php artisan schedule:list
它会列出所有注册任务及其下次预计执行时间(Next Due)。如果某任务的“Next Due”是过去的时间,但“Status”显示“Due”,说明它本该已运行却没跑;如果状态是“Overlapping”,说明上一轮还没结束,当前轮被withoutOverlapping()拦下了。
第二步:查看缓存锁文件是否存在
执行 ls -la storage/framework/cache/data/,找形如 schedule-xxx.lock 的文件。如果有长期存在的.lock文件(比如超过30分钟),说明上次任务卡死或崩溃后没释放锁,后续所有轮次都会被跳过。此时应手动删除该文件,并检查对应命令是否存在死循环或数据库连接超时。
第三步:确认是否用了onOneServer()
多机部署时,若任务加了->onOneServer(),但Redis缓存不可用或连接失败,Laravel会静默跳过该任务,不报错也不记录。此时去任意一台机器执行redis-cli ping,确保返回PONG;再检查config/cache.php中stores.redis.connection是否指向正确的Redis实例。
用Totem实现可视化任务监控
安装Totem包:composer require studio/laravel-totem
发布迁移并运行:php artisan vendor:publish --provider="Studio\Totem\Providers\TotemServiceProvider" → php artisan migrate
访问/totem(需配置路由和权限中间件),你会看到所有已注册任务的实时状态:上次运行时间、持续时长、是否成功、最近3次输出摘要。点击任一任务,还能手动触发、禁用、修改频率——不用改代码、不重启服务。
这一步最关键:Totem会在每次schedule:run执行前后向数据库写入执行记录,因此它本身依赖调度器正常运转。如果Totem界面里所有任务的“Last Run”都是空的,问题一定出在最外层Cron或APP_ENV上,别往下调Totem配置。











