Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Monitor Laravel Package

liip/monitor

liip/monitor provides health-check endpoints for Laravel applications, running configurable checks for database, cache, queue, disk space, and custom services. Use its JSON status output to power uptime monitoring and alerting.

View on GitHub
Deep Wiki
Context7

Getting Started

Read this first: liip/monitor was archived in 2014 and is explicitly marked Deprecated. It still installs from Packagist (composer require liip/monitor), and its only runtime requirement is a conservative php >= 5.3 constraint, so it will resolve on PHP 8 — but nothing in it has been touched in a decade. Treat it as a reference implementation of a health-check DSL, not as infrastructure for a new service. If you are evaluating it today, you are really evaluating whether the CheckInterface + CheckResult contract is worth copying.

The whole library is essentially three classes. Read them in order, top to bottom, in about ten minutes:

  1. Liip\Monitor\Check\CheckInterface — three methods: getName(), getGroup(), check(): CheckResult.
  2. Liip\Monitor\Result\CheckResult — value object holding a name, a status constant (OK, WARNING, CRITICAL — verify exact spelling in your installed version), a human message, and a details array.
  3. Liip\Monitor\Exception\CheckFailedException — the "this check blew up" escape hatch.

There is no runner, no HTTP layer, and no Symfony dependency in the core package (the routing/controller/console plumbing lives in the separate, also-abandoned liip/monitor-bundle). In Laravel you wire it yourself. The first use case is a single /health endpoint that returns 200 or 503:

// app/Health/RedisCheck.php
use Liip\Monitor\Check\CheckInterface;
use Liip\Monitor\Result\CheckResult;

class RedisCheck implements CheckInterface
{
    public function getName(): string  { return 'redis'; }
    public function getGroup(): string { return 'cache'; }

    public function check(): CheckResult
    {
        try {
            Redis::connection()->ping();
            return new CheckResult($this->getName(), CheckResult::OK, 'Redis reachable');
        } catch (\Throwable $e) {
            return new CheckResult($this->getName(), CheckResult::CRITICAL, 'Redis unreachable', [
                'exception' => get_class($e),
            ]);
        }
    }
}
Route::get('/health', function (array $checks) {
    $results = array_map(fn ($c) => $c->check(), $checks);
    $worst = in_array(CheckResult::CRITICAL, array_column($results, 'status'), true) ? 503 : 200;

    return response()->json(['status' => $worst === 200 ? 'ok' : 'fail', 'checks' => $results], $worst);
});

Implementation Patterns

Aggregate with worst-status-wins. The library gives you per-check results and nothing else, so the aggregation policy is yours. A single CRITICAL should poison the whole response; WARNING should stay 200 but be visible in the payload (and in your metrics), otherwise you train people to ignore the endpoint.

Split liveness from readiness. This is the pattern that actually pays for itself day-to-day. Register checks in two collections:

Probe Checks Failure means
/health/live php_version, disk_space, opcache restart the container
/health/ready db, redis, queue_backlog, migrations stop routing traffic

Never let liveness depend on MySQL. A DB blip should drain traffic, not restart every pod into a crash loop.

Register checks in a service provider and tag them. Keep getGroup() meaningful (db, cache, queue, external) and resolve by tag so you can run a subset:

$this->app->tag([DatabaseCheck::class, RedisCheck::class, QueueBacklogCheck::class], 'health.checks');
$this->app->tag([PhpVersionCheck::class, DiskSpaceCheck::class], 'health.liveness');

Artisan command + scheduler for alerting. The endpoint is for orchestrators; a command is for humans and Slack. Exit non-zero on CRITICAL, print WARNING to stderr, and only notify on state transitions — notify-on-every-run is how alert channels die.

Laravel-specific checks worth writing (the bundled check list is Symfony-shaped and stale): DB::select('select 1'), Redis::ping(), Queue::size('default') > threshold, Storage::disk('s3')->exists(...), Artisan::call('migrate:status') for pending migrations, cache()->has('schedule:last_run') as a scheduler heartbeat, and free bytes on the storage partition.

Cache the expensive results. Probes get hit every 5–10 seconds per replica. Wrap the whole run in Cache::remember('health', 15, ...) so an S3 or third-party HTTP check doesn't turn your health endpoint into a load source for someone else's API.

Gotchas and Tips

  • You are on your own for maintenance. No security fixes, no PHP 8.4 testing, no typed signatures. If you adopt it, pin the exact version, or better, vendor the three classes into app/Health/ and delete the dependency.
  • Verify constructor and constant names against the installed source. CheckResult's argument order and the status constants (OK/WARNING/CRITICAL vs. prefixed variants) changed across a handful of 2013-era tags. grep -n "const " vendor/liip/monitor/src/Liip/Monitor/Result/CheckResult.php before you write twenty checks against the wrong spelling.
  • A thrown exception is worse than a failed check. If check() throws and your controller doesn't catch, you return a 500 with a stack trace instead of a 503 with a message — and some orchestrators treat 500 differently from 503. Catch \Throwable inside every check, including Error.
  • Every check needs a hard timeout. PDO::ATTR_TIMEOUT / connect_timeout, Guzzle timeout + connect_timeout, Redis read_write_timeout. A health endpoint that hangs for 30s is functionally identical to a dead app, and it will burn a PHP-FPM worker per probe.
  • getDetails() leaks. DSNs, absolute paths, PHP extensions, framework versions — all useful to an attacker. Gate details behind app()->isLocal() or an IP allowlist, and return only name/status/message in production.
  • Don't let checks mutate state. A health check that opens a write transaction or warms a cache inflates your connection count under probe load. Read-only, idempotent, ideally one round-trip per dependency.
  • Use 503, not 500, and set Content-Type: application/json. Load balancers, Kubernetes, and most uptime monitors key off the status code; a JSON body served as text/html breaks half the dashboards.
  • getName() is your identity key. It becomes the array key and the metric label. Keep it stable and machine-friendly (db_primary, not Database (primary)), or your Grafana series will churn every refactor.
  • Migration path. The real replacement is not another PHP health-check library, it's the framework's own primitives plus spatie/laravel-health (check registry, Health::check(), result stores) or beyondcode/laravel-self-diagnosis for environment sanity. On the Symfony side the successor bundle is likewise abandoned — write a controller and use symfony/... probes directly. Both give you scheduled checks, history, and notifications that LiipMonitor never had.
  • Only one real extension point. CheckInterface is the entire API surface, which is precisely why porting away from this package is cheap: your check classes survive a rewrite almost verbatim if you keep them free of Liip imports except the two interfaces. Do that deliberately.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin