Run history, uptime & latency
Every executed check records one immutable HealthCheckRun (status + wall-clock latency), queryable off the HealthCheck model:
$check = $team->healthChecks()->first();
$check->runs(); // HasMany, newest first
$check->latestRun(); // ?HealthCheckRun
$check->uptimePercentage(now()->subDay()); // % of non-alertable runs in the window
$check->p95LatencyMs(now()->subDay()); // 95th-percentile duration in ms$run = $check->latestRun();
$run->status; // Status::Ok
$run->duration_ms; // wall-clock latency of check()
$run->message; // ?string
$run->meta; // the result meta
$run->ran_at; // Carbon- Skipped runs are recorded too, and count as up.
- uptimePercentage() is the share of runs whose status is not alertable, rounded to two decimals. It returns 100.0 with no history.
- p95LatencyMs() is a nearest-rank 95th percentile of duration_ms. It returns 0 with no history.
- Both are computed in PHP, so they behave the same on SQLite, MySQL and PostgreSQL. Without a window they cover the full history — which is what the status report uses.
Retention
Retention is pruned by alerts:prune-runs, auto-scheduled daily when history is enabled, or from code with Health::prune():
php artisan alerts:prune-runs --days=30use RoundlyConsulting\Alerts\Facades\Health;
Health::prune(); // older than alerts.history.retention_days; returns the number deleted
Health::prune(7); // older than 7 daysWithout --days (or a days argument) it uses history.retention_days (30 by default). Set history.enabled to false to stop recording runs and scheduling the prune — uptime then reads 100 and p95 reads 0.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.