Laravel monitoring
How to get alerted when orders stop in your Laravel app
Your error tracker won't tell you when customers stop ordering. A simple scheduled check that will, and where it runs out.
Most Laravel monitoring answers one question: is something broken? Exceptions go to your error tracker, slow queries show up in your APM, failed jobs land in failed_jobs, and an uptime check pings your homepage every minute.
All of that is worth having. None of it notices the most expensive failure an online business can have: the app is up, nothing is throwing, and nobody is ordering.
It happens more often than you'd think. A deploy puts the checkout button behind a feature flag nobody switched on. A consent banner update covers the "Place order" button on iPhones. A new validation rule rejects a postcode format a third of your customers use, and they see a polite error message instead of an exception. Every one of those requests returns a 200 or a 422. Your dashboards stay green. The only symptom is an absence: orders that should have happened and didn't.
So let's measure the absence.
Start simple: alert when nothing has happened for an hour
The simplest useful check is a scheduled command that counts recent orders and shouts if there are none.
<?php namespace App\Console\Commands; use App\Models\Order; use Illuminate\Console\Command; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Http; class CheckOrdersFlowing extends Command { protected $signature = 'orders:check-flowing'; protected $description = 'Alert when no orders have been created in the last hour'; public function handle(): void { $recent = Order::where('created_at', '>=', now()->subHour())->count(); if ($recent > 0) { return; } // Cache::add only succeeds when the key is missing, so this alerts // once and then stays quiet for three hours instead of every run. if (Cache::add('alerts:orders-silent', true, now()->addHours(3))) { Http::post(config('services.slack.alerts_webhook'), [ 'text' => 'No orders have been created in the last hour.', ]); } } }
Add an alerts_webhook entry under slack in config/services.php pointing at a Slack incoming webhook, then schedule it in routes/console.php:
use Illuminate\Support\Facades\Schedule; Schedule::command('orders:check-flowing')->everyFifteenMinutes();
That's twenty minutes of work and it will catch a total outage of your checkout on day one.
Then the questions start.
But what about...
...3am? Most shops take no orders at 3am, so "no orders in the last hour" is true every night. You'll mute the alert within a week, and a muted alert is worse than none, because you still believe you're covered.
...a slow hour rather than a dead one? The checkout that breaks only on iPhones doesn't take orders to zero. It halves them. "No orders in the last hour" never becomes true, and this check never fires.
...Sunday afternoon versus Tuesday lunchtime? Whether an hour is worryingly quiet depends entirely on when it is. One number can't be right for both.
...Black Friday and Boxing Day? Some days look nothing like a normal week, in both directions, and neither is a fault.
...last month's outage? The moment you try to learn "normal" from your own history, the bad days are in there too. Learn from them carelessly and the alert slowly comes to accept the problem it's meant to catch.
...a shop that takes ten orders a day? An hour without an order is completely ordinary there. It needs a different question from a shop taking ten a minute.
...cron itself stopping? This check runs on the scheduler. If cron dies on your server, the check dies with it, silently, at exactly the moment you'd want it.
...signups, bookings, renewals and outgoing emails? Orders are the obvious one. Every other moment your business depends on needs its own version, its own thresholds and its own tuning.
...the deploy that went out twenty minutes ago? When it fires, your first question will be "did we just ship something?", and nothing here can answer it.
None of these is hard on its own. Together they add up to a small product that someone has to own. The anomaly detection article goes through more of them.
What Marmot does instead
Marmot is a Laravel package plus a hosted service, built for exactly this problem.
- Nothing to write per moment. Install the package and Marmot starts watching the activity your app already produces: new orders, signups, bookings, scheduled tasks, queued jobs, emails. "Orders created" is watched from the first order onwards.
- It knows what normal looks like for your business, hour by hour and day by day, so a quiet Sunday night isn't an alert and a quiet Tuesday lunchtime is.
- Quiet moments are covered too, not just the busy ones.
- Useful from day one, without weeks of watching first.
- It watches the scheduler as well, so a dead cron doesn't take your alerts down with it.
- Deploy context. Alerts tell you when something shipped shortly before.
It sends names and counts only, never your data. Alerts arrive in Slack in plain English: "Orders created has been silent for 3h 12m, expected roughly every 14 min."
For the moments that matter most, you can also name them yourself:
use Marmot\Laravel\Marmot; Marmot::event('Order placed');
Marmot is in private beta with a handful of production apps.