[ GUIDE ]

How to track Laravel notifications in production

Per-channel delivery tracking across mail, Slack, SMS, database, and broadcast — with event listeners and aggregate dashboards.

QUICK ANSWER

How do I track Laravel notifications?

Listen to NotificationSending, NotificationSent, and NotificationFailed events in a service provider to record every delivery. For aggregated dashboards showing delivery rate per notification class and per channel, install an APM. NightOwl records every notification with class, channel, recipient hash, status, and duration — grouped by class with delivery-rate trending.

Updated · 2026-04-13

The three notification events

Every notification dispatch fires three events you can listen to per channel:

app/Providers/EventServiceProvider.php

php
use Illuminate\Notifications\Events\NotificationSending;
use Illuminate\Notifications\Events\NotificationSent;
use Illuminate\Notifications\Events\NotificationFailed;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(function (NotificationSent $event) {
        logger()->info('Notification sent', [
            'class' => get_class($event->notification),
            'channel' => $event->channel,
            'notifiable_type' => get_class($event->notifiable),
            'notifiable_id' => $event->notifiable->getKey(),
        ]);
    });

    Event::listen(function (NotificationFailed $event) {
        logger()->error('Notification failed', [
            'class' => get_class($event->notification),
            'channel' => $event->channel,
            'data' => $event->data,
        ]);
    });
}

Queue every notification — then track the queue

Every notification that hits an external service (mail, Slack, SMS) should implement ShouldQueue so it doesn't block the request:

app/Notifications/OrderShipped.php

php
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class OrderShipped extends Notification implements ShouldQueue
{
    use Queueable;

    public function via($notifiable): array
    {
        return ['mail', 'database'];
    }

    public function shouldSend($notifiable, $channel): bool
    {
        // Rate-limit — don't send twice within an hour
        $key = "shipped:{$notifiable->id}:{$channel}";
        if (cache()->has($key)) return false;
        cache()->put($key, true, now()->addHour());
        return true;
    }
}

Queued notifications fail as queued jobs. Pair notification tracking with failed-job monitoring to catch silent delivery failures.

Metrics that matter

  • Delivery rate per channel — sent / (sent + failed). Drops indicate provider issues.
  • Send count per notification class — catches runaway sends during incidents.
  • Queue lag — time between dispatch and actual delivery. Rising lag = workers overwhelmed.
  • Failure clustering by recipient — same user failing across multiple notifications usually means a stale email address or broken webhook.

Gotchas by channel

  • Mail: A 200 from your SMTP doesn't mean the email was delivered to the user. Track bounces via your mail provider's webhook and mark users as undeliverable in your database.
  • Slack webhooks: Silent failures are common if a workspace admin rotated the URL. Fall back to storing the last 20 failures and page someone if all recent sends fail.
  • SMS: Carriers drop small percentages — don't alert on single failures, alert on rate drops.
  • Database: 100% reliable, but don't use it as your primary channel — users won't see it unless they log in.
  • Broadcast: Depends on your broadcaster (Pusher, Ably, Reverb). WebSocket disconnects mean delivery isn't guaranteed for offline users.

THE EASY WAY

NightOwl tracks every notification with per-channel delivery rate

The notifications dashboard groups every dispatch by notification class + channel with delivery rate, failure count, and p95 send duration. Click into a class to see every send, recipient, and status. Alerts fire on delivery-rate drops across any configured channel.

bash
composer require nightowl/agent
php artisan nightowl:install

From $5/month flat. Data in your PostgreSQL.

Frequently asked questions

How do I track Laravel notifications in production?

Laravel fires NotificationSending, NotificationSent, and NotificationFailed events on every channel. Listen to them in a service provider to record per-channel delivery. For aggregated per-notification-class delivery rates, failure trends, and cross-channel reporting, install an APM that wires the events for you. NightOwl records every notification with class, channel, recipient hash, status, and duration.

What's the difference between Laravel mail and notifications?

Mail is one transport. Notifications are a higher-level abstraction that can deliver across mail, Slack, Nexmo/Vonage, database rows, broadcast channels, or anything you implement as a driver. Tracking mail monitors email delivery; tracking notifications monitors every channel an instance of a notification class fires through. Most teams care about both but in different dashboards.

How do I know when a Slack or SMS notification fails in Laravel?

Channels that support it throw an exception or return a failure status, which Laravel dispatches as NotificationFailed with the specific channel name. Register a listener on NotificationFailed, log by channel, and alert on spike. Channels without explicit success callbacks (webhook-style Slack) only surface synchronous failures — delivery to the end user isn't guaranteed by a 200 response.

Should I queue Laravel notifications?

Yes, almost always. Notifications dispatched from the request cycle block the response. Use ShouldQueue on the notification class, which turns each delivery into a queued job. Downside: queued notifications fail in the queue, not in the request — you need failed-job monitoring (see our failed-job guide) to notice when they don't deliver.

How do I track which users received which notifications?

Use the database channel — Laravel's built-in notifications table stores notifiable_id, type, read_at, and data per delivery. Combine with NotificationSent event listeners for channels that don't write to the database. For privacy-aware recipient tracking, store a hashed user ID rather than PII in log records.

How do I rate-limit Laravel notifications?

Use shouldSend($notifiable, $channel) on the notification class to return false when the recipient has already received the same class within a cooldown window (store the last-sent timestamp in cache or DB). For global rate limits, wrap the dispatch in Laravel's RateLimiter facade. Aggressive rate limiting on errors prevents notification storms during incidents.

What's a realistic delivery rate for Laravel notifications?

Channel-dependent. Email: 95-99% depending on sender reputation and bounces. Slack webhooks: 99%+ if the workspace is healthy. SMS: 95-98% (carriers drop a small percentage). Database: 100%. Track delivery rate per channel over time — a drop from 99% to 94% on email usually means sender reputation is slipping.

Can I test Laravel notifications without actually sending?

Yes — Notification::fake() in your test class intercepts every dispatch. Assert which notifications were sent to which notifiable, and with which channel. For dry-runs in production (e.g. a staging run that shouldn't actually email users), use the log mail driver and a no-op Slack webhook URL in env-specific config.

PRICING

Flat pricing. No event caps. No per-seat fees.

14-day free trial, no credit card. Your PostgreSQL, your data.

HOBBY

$5 /month

1 app · 14 days lookback · all Laravel events

TEAM

$15 /month

Up to 3 connected apps · unlimited environments · all Laravel events

AGENCY

$69 /month

Unlimited apps · unlimited agent instances · same flat rate at any traffic

Related