Writing checks
Extend RoundlyConsulting\Alerts\Check and implement check() and notification():
use Illuminate\Notifications\Notification;
use RoundlyConsulting\Alerts\Check;
use RoundlyConsulting\Alerts\CheckResult;
class DiskUsageCheck extends Check
{
public function check(): CheckResult
{
// $this->healthCheck holds the scheduled HealthCheck record and its meta.
$serverId = $this->healthCheck?->meta['server_id'] ?? null;
return CheckResult::ok(
message: 'Disk usage within limits',
meta: ['server_id' => $serverId],
);
// Return CheckResult::failed('Disk almost full', [...]) to trigger an alert.
}
public function notification(object $notifiable): Notification
{
return new DiskUsageNotification();
}
}$this->healthCheck is the scheduled HealthCheck row the check runs for, with its meta. It is null when the check runs without a row — for example alerts:check without --notifiable — so read it null-safely.
A fuller example
Override name(), key(), description() or tags() to change how the check presents itself. Tags declared here merge with each row’s own tags. Reuse the bundled notification and pass $this->channels() to honour notifyVia():
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Http;
use RoundlyConsulting\Alerts\Check;
use RoundlyConsulting\Alerts\CheckResult;
use RoundlyConsulting\Alerts\Notifications\HealthCheckFailedNotification;
class PaymentGatewayCheck extends Check
{
public function name(): string
{
return 'Payment gateway';
}
public function tags(): array
{
return ['critical', 'billing'];
}
public function check(): CheckResult
{
$url = $this->healthCheck?->meta['url'] ?? 'https://payments.example.com/status';
// No try/catch needed — a thrown exception becomes a failed result.
$response = Http::timeout(5)->get($url);
return $response->successful()
? CheckResult::ok('Gateway reachable', ['url' => $url])
: CheckResult::failed('Gateway returned '.$response->status(), ['url' => $url]);
}
public function notification(object $notifiable): Notification
{
// Reuse the bundled notification and honour notifyVia() routing.
return new HealthCheckFailedNotification($this, channels: $this->channels());
}
}Because each row carries its own meta, one check class can watch several services for the same owner. Each row has its own counters, alerts and notification throttle:
// One check class, two monitors — each row carries its own target in meta.
Health::for($team)->monitor(PaymentGatewayCheck::class)
->everyMinute()
->meta(['url' => 'https://payments.example.com/status'])
->save();
Health::for($team)->monitor(PaymentGatewayCheck::class)
->everyMinute()
->meta(['url' => 'https://payouts.example.com/status'])
->save();Checks are instantiated without constructor arguments when you register or schedule them by class name. Keep configuration in fluent setters on a registered instance, or in the row’s meta.
Results and severity
A CheckResult carries a Status — ok, warning, failed or skipped — plus a message and a meta array:
use RoundlyConsulting\Alerts\CheckResult;
CheckResult::ok();
CheckResult::warning('Disk filling up');
CheckResult::failed('Disk full');
CheckResult::skipped('Maintenance window');| Status | Severity | Effect of a run |
|---|---|---|
ok | 0 | Resets the failure counter and counts towards recovery of an open alert. |
skipped | 1 | Records the run and nothing else — no counters, no alert, no notification. |
warning | 2 | Counts as a failure: opens an alert and notifies, just like failed. |
failed | 3 | Counts as a failure: opens an alert and notifies. |
use RoundlyConsulting\Alerts\CheckResult;
use RoundlyConsulting\Alerts\Enums\Status;
$result = CheckResult::failed('Disk full', ['free_bytes' => 1024]);
$result->status; // Status::Failed
$result->message; // 'Disk full'
$result->meta; // ['free_bytes' => 1024]
$result->isOk; // false — legacy flag, true only for Status::Ok
new CheckResult(true); // same as CheckResult::ok()
new CheckResult(false, 'Down'); // same as CheckResult::failed('Down')
new CheckResult(Status::Warning, 'Slow'); // any Status works tooYou never need a try/catch in check(): a thrown exception becomes a failed result automatically (see Timeouts & error handling).
The Status enum
Status keeps its domain helpers — isAlertable() and severity() — and adopts the Helpers trait from enums-for-laravel, so it is ready for select inputs, validation rules and API payloads:
use RoundlyConsulting\Alerts\Enums\Status;
Status::Warning->isAlertable(); // true — opens an alert and notifies
Status::Failed->severity(); // 3 (ok 0, skipped 1, warning 2, failed 3)
Status::values(); // Collection: ok, warning, failed, skipped
Status::toOptions(); // Collection: 'ok' => 'Ok', 'warning' => 'Warning', …
Status::validationRule(); // 'in:ok,warning,failed,skipped'
Status::Failed->label(); // 'Failed'
Status::tryFromLabel('Warning'); // Status::WarningAlso available: names(), labels(), options(), readable(), fromName()/tryFromName(), fromLabel(), hasValue(), is()/isIn() and the when* guards.
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.