spatie/laravel-schedule-monitor
Monitor Laravel scheduled tasks by logging each run’s start, finish, failure, or skip to the database. Use an Artisan list command to see execution history and spot issues. Optional Oh Dear sync alerts you when tasks fail or don’t run on time.
Installation:
composer require spatie/laravel-schedule-monitor
php artisan vendor:publish --provider="Spatie\ScheduleMonitor\ScheduleMonitorServiceProvider" --tag="schedule-monitor-migrations"
php artisan migrate
First Sync:
php artisan schedule-monitor:sync
Verify Sync:
php artisan schedule-monitor:list
schedule-monitor:list to check if a job is marked as "late" (red background).schedule-monitor:list --failed to see jobs that failed.monitored_scheduled_task_log_items table for execution details (memory, runtime, output).Schedule Definition:
// app/Console/Kernel.php
$schedule->command('backup:run')->dailyAt('2:00')->monitorName('daily-backup');
$schedule->job(new SendReportJob())->hourly()->graceTimeInMinutes(10);
Post-Deployment Sync: Add to your deployment script:
php artisan schedule-monitor:sync --keep-old # Non-destructive sync for Oh Dear
Monitoring:
php artisan schedule-monitor:list --failed # Filter failures
php artisan schedule-monitor:list --since=2024-01-01 # Time range
monitored_scheduled_task_log_items for custom reports:
$logs = MonitoredScheduledTaskLogItem::where('task_name', 'daily-backup')
->where('status', 'failed')
->orderBy('created_at', 'desc')
->get();
Oh Dear Sync:
Configure .env with OH_DEAR_API_TOKEN and OH_DEAR_MONITOR_ID, then run:
php artisan schedule-monitor:sync
Verify with:
php artisan schedule-monitor:verify
Output Storage: Enable for commands/jobs:
$schedule->command('backup:run')->storeOutputInDb();
Retrieve output via:
$logItem->meta->output; // JSON-decoded output
Grace Time:
Override globally in config/schedule-monitor.php or per-task:
$schedule->command('long-running-job')->graceTimeInMinutes(30);
Pruning Logs:
Add to app/Console/Kernel.php:
$schedule->command('model:prune', [
'--model' => \Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem::class
])->daily();
Name Collisions:
monitorName()) deletes old logs for that task.Oh Dear Sync Issues:
OH_DEAR_QUEUE is a dedicated queue with workers.graceTimeInMinutes (e.g., 10–30 mins for long jobs).OH_DEAR_DEBUG_LOGGING=true for connection issues.Multitenancy:
PingOhDearJob to not_tenant_aware_jobs in config/multitenancy.php causes silent failures.'not_tenant_aware_jobs' => [
\Spatie\ScheduleMonitor\Jobs\PingOhDearJob::class,
],
Log Bloat:
delete_log_items_older_than_days (default: 30).Anonymous Jobs:
monitorName() use the class name of the first argument (e.g., App\Jobs\SendReportJob). Override with:
$schedule->call(fn () => $this->sendReport())->monitorName('send-report');
Failed Jobs:
php artisan schedule-monitor:list --failed --verbose
Check meta column in monitored_scheduled_task_log_items for exceptions (stored as JSON).
Oh Dear Ping Failures:
OH_DEAR_API_TOKEN and OH_DEAR_MONITOR_ID are correct.curl -X POST https://ping.ohdear.app/api/v1/ping \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"monitor_id": "YOUR_MONITOR_ID", "status": "ok"}'
Performance:
--keep-old for large schedules to avoid sync delays.PingOhDearJob:
// app/Jobs/CustomPingOhDearJob.php
use Spatie\ScheduleMonitor\Jobs\PingOhDearJob;
class CustomPingOhDearJob extends PingOhDearJob {
public function handle() {
// Custom logic (e.g., batch pings)
parent::handle();
}
}
Update config:
'ping_oh_dear_job' => App\Jobs\CustomPingOhDearJob::class,
Custom Log Fields:
Extend MonitoredScheduledTaskLogItem to add fields (e.g., user_id for user-triggered jobs):
// app/Models/CustomLogItem.php
use Spatie\ScheduleMonitor\Models\MonitoredScheduledTaskLogItem;
class CustomLogItem extends MonitoredScheduledTaskLogItem {
protected $casts = [
'meta' => 'array',
'user_id' => 'integer',
];
}
Update config:
'monitored_scheduled_log_item' => App\Models\CustomLogItem::class,
Webhook Notifications:
Listen for log events via scheduled:monitored events:
// app/Providers/EventServiceProvider.php
use Spatie\ScheduleMonitor\Events\ScheduledTaskWasMonitored;
protected $listen = [
ScheduledTaskWasMonitored::class => [
\App\Listeners\LogToWebhook::class,
],
];
Slack Alerts:
Integrate with schedule-monitor:list output using Laravel’s Artisan::call():
$output = Artisan::call('schedule-monitor:list', ['--failed' => true]);
$this->slack->send("Failed jobs: $output");
How can I help you explore Laravel packages today?