Inline closure checks
For one-off checks, define one inline instead of writing a class:
use Illuminate\Support\Facades\Redis;
use RoundlyConsulting\Alerts\CheckResult;
use RoundlyConsulting\Alerts\Facades\Health;
Health::define('redis-up', fn () => Redis::ping() ? CheckResult::ok() : CheckResult::failed('Redis down'))
->name('Redis')
->throttle(maxAttempts: 1, decayMinutes: 15)
->notifyUsing(RedisDownNotification::class);A closure may return a CheckResult or a plain bool — true is ok, false is failed. It receives the ClosureCheck, so it can read the scheduled row’s meta like a class-based check. A notification closure receives the check and the recipient:
use RoundlyConsulting\Alerts\Checks\ClosureCheck;
// The closure receives the ClosureCheck; return a CheckResult or a plain bool.
Health::define('orders-flowing', function (ClosureCheck $check): bool {
$minutes = $check->healthCheck?->meta['window'] ?? 60;
return Order::where('created_at', '>=', now()->subMinutes($minutes))->exists();
})->name('Orders flowing');
// A notification closure receives the check and the recipient.
Health::define('queue-drained', fn () => Queue::size() < 100)
->notifyUsing(fn (ClosureCheck $check, object $notifiable) => new QueueStuckNotification($check->name()));Builder options
Health::define() returns a PendingCheck:
| Method | Effect |
|---|---|
name(string) | Display name. Without it every inline check is named “Closure Check”, so set one. |
description(string) | Description shown by the registry. |
notifyUsing(string|Closure) | Your notification: a class instantiated with the check, or a closure receiving the check and the recipient. |
tags(array) | The check’s own tags, merged into every row’s effective tags. |
failAfter(int) | Consecutive failures before an alert opens. |
recoverAfter(int) | Consecutive successes before an alert closes. |
timeout(int $seconds) | Time budget for the closure. |
notifyVia(array, ?int $level) | Channels for the bundled notification, globally or per escalation level. |
escalate(array) | Threshold ⇒ group escalation policy. |
throttle(int, int) | Renotification limit for rows that run the check (see the notes below). |
notifyUsing() with a class name instantiates it with the check as the only constructor argument. If the class or the closure does not produce a Notification, the bundled HealthCheckFailedNotification is used instead.
Scheduling and running
// An inline check has no class — run it by its key. The throttle and
// options declared on define() apply:
Health::for($team)->run('redis-up');
// Monitor it by its key — the builder starts from define()'s options,
// and any builder call overrides them:
Health::for($team)->monitor('redis-up')
->everyMinute()
->failAfter(2)
->save();An inline check has no class, so you run and monitor it by its key. Health::for($owner)->run('key') uses the throttle and options declared on define(), and Health::for($owner)->monitor('key') starts from them — any builder call overrides them.
Where the options apply
- name(), description(), tags() and notifyUsing() belong to the check itself and apply wherever it runs.
- throttle(), failAfter(), recoverAfter(), timeout(), notifyVia() and escalate() apply to the on-demand row that Health::for($owner)->run() uses when the owner has no schedule for the check. That row mirrors the definition, so a changed define() takes effect on the next run.
- A row you schedule with Health::for($owner)->monitor('key') starts from the same options and keeps what was saved — change them on that builder.
- The renotification throttle is always read from the row’s max_attempts and decay_minutes, which come from define()’s throttle() or the monitor() builder.
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.