NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Alerts for Laravel

The Health facade

RoundlyConsulting\Alerts\Facades\Health is the recommended entry point, and its Health alias is auto-discovered. Its surface has four parts: the check registry, a handle scoped to one owner, the maintenance-window sub-accessor and the verbs that span every owner:

use RoundlyConsulting\Alerts\Checks\DatabaseCheck;
use RoundlyConsulting\Alerts\Facades\Health;

// The check registry
Health::check(DatabaseCheck::make('mysql'));
Health::checks([DiskUsageCheck::class, MemoryUsageCheck::class]);
Health::define('orders-flowing', fn () => Order::where('created_at', '>=', now()->subHour())->exists());
Health::all();                               // Collection<string, Check>
Health::find('disk_usage_check');            // ?Check

// Everything scoped to one owner
Health::for($team)->monitor(DatabaseCheck::class)->everyFiveMinutes()->save();
Health::for($team)->schedule($data);         // from a ScheduleHealthCheckData
Health::for($team)->reconfigure($row, $changes); // change a schedule in place
Health::for($team)->run(DatabaseCheck::class);
Health::for($team)->run('orders-flowing');   // by key, e.g. an inline check
Health::for($team)->report(['critical']);
Health::for($team)->status();
Health::for($team)->monitors();
Health::for($team)->unmonitor(DatabaseCheck::class);

// Maintenance windows
Health::silences()->mute('db', until: now()->addHour(), for: $team, reason: 'upgrade');
Health::silences()->unmute('db', for: $team);
Health::silences()->isMuted('db');
Health::silences()->active($team);
Health::silences()->snapshot();              // every silence in force, one query
Health::silences()->prune();                 // what alerts:prune-silences calls

// Across every owner
Health::report();
Health::report(since: now()->subDay(), scheduledOnly: true);
Health::status();
Health::runDue();                            // what alerts:perform-health-checks calls
Health::prune(14);                           // what alerts:prune-runs calls
Health::routes();                            // the opt-in JSON endpoint

Flat methods

MethodReturnsWhat it does
check(string|object $check)HealthManagerRegister one check class or instance. Anything that does not extend Check throws InvalidHealthCheck.
checks(array $checks)HealthManagerRegister several checks at once.
define(string $key, Closure $callback)PendingCheckDefine an inline closure check (see Inline closure checks).
all()Collection<string, Check>Every registered check, keyed by its key.
find(string $key)?CheckOne registered check, or null.
for(Model $owner)NotifiableHealthThe handle scoped to one owner (next table).
report(?array $tags = null, ?CarbonInterface $since = null, bool $scheduledOnly = false)HealthReportReport across every monitored check — scheduled monitors and the on-demand rows run() creates — optionally filtered by tags. since: limits uptime and p95 to the runs since then; scheduledOnly: true leaves the on-demand rows out. Built in a fixed number of queries.
status(?array $tags = null)StatusThe overall() roll-up of that report.
silences()SilencesThe maintenance-window sub-accessor (table below).
runDue()intQueue the configured job for every scheduled check due since the previous call (or now, on a first call); returns how many were queued.
prune(?int $days = null)intDelete run history older than $days (default history.retention_days); returns how many runs were deleted. Below 1 day throws InvalidRetention.
routes(?string $uri = null)RouteRegister the opt-in JSON status endpoint.

Health::for($owner)

Every health operation scoped to one owner model. The handle is a security boundary: a scheduled row that belongs to another owner is refused with InvalidHealthCheck.

MethodReturnsWhat it does
run(string|Check|HealthCheck $check)CheckResultRun now with the full alert, notify and history side effects. A registered key, class or instance runs through the owner’s scheduled row, else an on-demand row that is never scheduled; a row must belong to the owner, else InvalidHealthCheck.
report(?array $tags = null, ?CarbonInterface $since = null, bool $scheduledOnly = false)HealthReportThis owner’s report, with the same tag, window and scheduled-only options.
status(?array $tags = null)StatusThis owner’s roll-up.
monitor(string $check)PendingScheduledCheckFluent schedule for a Check class or registered key (an inline check’s key starts from its define() options); save() validates the cron and returns the HealthCheck row.
schedule(ScheduleHealthCheckData $data)HealthCheckSchedule from a DTO. For a check that declares uniqueBy(), an upsert on the schedule’s identity (see Scheduling identity & reconfigure).
reconfigure(HealthCheck $row, ReconfigureHealthCheckData $changes)HealthCheckChange one of this owner’s schedules in place: null settings stay, meta merges key by key. Another owner’s row throws InvalidHealthCheck; moving onto another live schedule’s identity throws ScheduleConflict.
unmonitor(string|Check|HealthCheck $check)intSoft-delete this owner’s rows for a check — scheduled and on-demand — or one own row, closing any alert still open on them; another owner’s row throws InvalidHealthCheck.
monitors()Collection<int, HealthCheck>This owner’s schedules, oldest first — on-demand rows are not listed.

The UsesHealthChecks trait’s monitorCheck(), monitor() and createHealthCheck() are shortcuts over Health::for($this), so they behave exactly like the facade calls — and Health::fake() records them too (see Owner models).

Health::silences()

Maintenance windows. A key is a check key, a tag or * for everything; for: scopes a silence to one owner (see Maintenance windows & muting):

MethodReturnsWhat it does
mute(string $key, ?CarbonInterface $until = null, ?Model $for = null, ?string $reason = null)AlertSilenceMute a check key, a tag or *, optionally until a moment and/or for one owner. Muting the same key and scope again updates that silence’s reason and end — extending or shortening it — and returns it.
unmute(string $key, ?Model $for = null)intLift the global silences on the key, or only those scoped to $for; returns how many were lifted.
isMuted(string $key, ?Model $for = null)boolWhether a silence on exactly this key is in force now. Always false when silence is off in the config.
active(?Model $for = null)Collection<int, AlertSilence>Silences in force now: all of them, or the global ones plus those scoped to $for.
snapshot(?CarbonInterface $at = null)SilenceSnapshotEvery silence in force at $at (default now), loaded in one query; ask it covers($row) or isEmpty(). Empty while silence is off.
covers(HealthCheck $row)boolWhether a silence in force now mutes the row, by the run pipeline’s own rule (one query).
prune(?int $days = null)intDelete silences that ended more than $days ago (default silence-retention); returns how many were deleted. Below 1 day throws InvalidRetention.

Builders

  • Health::for($owner)->monitor($check) returns a PendingScheduledCheck — frequency presets, throttle, thresholds, tags, routing, escalation and meta, then save() (see Scheduling checks). For an inline check’s key it starts from the options declared on define().
  • Health::define($key, $closure) returns a PendingCheck for an inline check’s name, notification and options (see Inline closure checks).

In tests, Health::fake() swaps the whole surface for a recording fake (see Testing). If you prefer an explicit dependency or single-purpose classes, see DI and actions.

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 crypto

By 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.