[ GUIDE ]

How to Track Exceptions in Laravel Production

From the built-in Log facade to fingerprinted tracking with context and alerting.

QUICK ANSWER

How do I track exceptions in a Laravel app?

Send exceptions to a tracker that fingerprints them by class, normalized message, and stack frames — then groups repeated occurrences into a single issue with counts, first/last-seen timestamps, and trend graphs. Laravel's Log facade is a baseline. Production apps need a dedicated tracker like NightOwl (BYOD), Nightwatch Cloud, Flare, Sentry, or Bugsnag with alert channels routed to Slack or Discord.

Updated · 2026-04-13

Baseline — the Log facade

By default, Laravel's exception handler writes unhandled exceptions to the configured log channel. Look in storage/logs/laravel.log or your configured driver (daily, papertrail, syslog, custom).

config/logging.phpphp
'stack' => [
    'driver' => 'stack',
    'channels' => ['daily', 'errorlog'],
    'ignore_exceptions' => false,
],

'daily' => [
    'driver' => 'daily',
    'path' => storage_path('logs/laravel.log'),
    'level' => 'error',
    'days' => 14,
],

This catches everything. Missing: grouping, trending, and anything that looks like a UI. Good enough for a side project, not for production.

Fan out with the Exceptions handler

In Laravel 11+, register reporters in bootstrap/app.php:

bootstrap/app.phpphp
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Http\Exceptions\HttpResponseException;

->withExceptions(function (Exceptions $exceptions) {
    // Add deploy version to every captured exception
    $exceptions->context(fn () => [
        'release' => config('app.release_sha'),
        'node' => gethostname(),
    ]);

    // Skip exceptions you don't care about
    $exceptions->dontReport([
        HttpResponseException::class,
    ]);

    // Route custom exceptions to your tracker
    $exceptions->report(function (CustomBusinessException $e) {
        app('tracker')->capture($e, [
            'order_id' => $e->orderId,
            'severity' => 'business',
        ]);
    });
})

Enrich exceptions with user context

An exception stack without the user who hit it is half-useful. Attach user context via middleware so every captured exception in that request includes who, not just what.

php
namespace App\Http\Middleware;

use Closure;
use Illuminate\Support\Facades\Auth;

class AttachUserContext
{
    public function handle($request, Closure $next)
    {
        if (Auth::check()) {
            // For NightOwl / Nightwatch — sets the user on every exception + request
            app('nightwatch')->setUser([
                'id' => Auth::id(),
                'email' => Auth::user()->email,
                'plan' => Auth::user()->subscription_plan ?? null,
            ]);
        }

        return $next($request);
    }
}

Fingerprint and group

A production Laravel app throws thousands of exceptions per day. Most are duplicates. Fingerprinting rolls them into issues.

A good fingerprint uses:

  • Exception class (QueryException)
  • The top app-level stack frame (skip vendor frames)
  • A normalized message (strip dynamic values like IDs, emails, timestamps)

Every production-grade tracker (NightOwl, Nightwatch Cloud, Sentry, Bugsnag, Flare, Rollbar) does this automatically. If you're using raw logs, you're manually grep-ing — don't.

Silence the noise at the source

Common Laravel exceptions worth suppressing before they hit your tracker (to avoid billing costs and UI clutter):

php
use Illuminate\Auth\AuthenticationException;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Illuminate\Session\TokenMismatchException; // CSRF — usually noise

$exceptions->dontReport([
    NotFoundHttpException::class,          // 404s
    MethodNotAllowedHttpException::class,  // 405s
    ValidationException::class,            // expected input errors
    AuthenticationException::class,        // expected auth failures
    TokenMismatchException::class,         // stale tabs submitting CSRF
]);

Alert on signal, not noise

Page on grouped signal, not per-occurrence. The alert matrix that works for most teams:

  • First seen — any new fingerprint appearing in the last hour
  • Spike — existing fingerprint's rate jumps 10x over baseline
  • Threshold — more than N of fingerprint X in 15 minutes, where X is in a critical path
  • Regression — fingerprint that was resolved reappears after a deploy

Send all alerts to a Slack or Discord channel. Email is fine as a low-urgency fallback; never page on email.

THE EASY WAY

NightOwl fingerprints every exception with full context

NightOwl's exception watcher captures every unhandled exception with full stack, request context, user, deploy SHA, and the exact SQL / job / schedule that triggered it. Exceptions are fingerprinted and grouped into issues — with status (open, resolved, ignored), priority, assignee, comments, and activity timeline.

Alert channels ship to Slack, Discord, email, or any webhook. First-seen, spike, and threshold alerts built in.

bash
composer require nightowl/agent
php artisan nightowl:install

Data lives in your PostgreSQL. Flat from $5/month. 14-day free trial.

Frequently asked questions

How do I track exceptions in a Laravel app?

Three options from cheapest to best: (1) Laravel's built-in log channel captures stack traces to disk or an external log aggregator, (2) the Exceptions::report() handler lets you fan out to multiple destinations, (3) a dedicated exception tracking service (Flare, Sentry, Bugsnag) or full APM (NightOwl, Nightwatch Cloud, Inspector) that groups exceptions by fingerprint with trends and alerting.

What's exception fingerprinting?

Fingerprinting normalizes an exception into a stable identifier based on class, message template (without dynamic values), and the most relevant stack frames. Ten thousand occurrences of QueryException: Deadlock on orders table share one fingerprint and roll up into a single issue with a count and first/last-seen timestamps, instead of ten thousand separate notifications.

Should I use Laravel's Log facade or a dedicated exception tracker?

Logs are a baseline, not a monitoring system. Writing exceptions to a log file solves nothing if nobody reads the file. Use logs plus a tracker that groups, trends, alerts, and exposes a UI. For production-grade Laravel apps, an exception tracker is table stakes.

How do I suppress noisy exceptions in Laravel?

In app/Exceptions/Handler.php (Laravel 10) or bootstrap/app.php (Laravel 11+), use $exceptions->dontReport([NotFoundHttpException::class, ValidationException::class]) or $exceptions->dontReport(fn($e) => $e->getCode() === 404). Suppress at the source — filtering noise in the tracker costs more (per-event billing) and clutters the UI.

How do I add user context to Laravel exceptions?

Use Exceptions::context() to attach global context (app version, deploy SHA) or report() with extra data for per-request context. For user context, most trackers offer a setUser() call — scope it to a middleware so every exception captured during that request includes the auth user.

What exceptions should I alert on?

Not individual exceptions — grouped ones. Configure per-class thresholds: spike detection (suddenly 100x more of a fingerprint), first-seen (new fingerprint in the last hour), and priority (exceptions in critical paths like payments). Throttle alerts so a high-volume exception doesn't page you 10,000 times.

Can I self-host exception tracking?

Yes — Sentry is self-hostable but heavy (~20 containers, 16GB+ RAM). Laravel Telescope is free and self-hosted but not production-grade. NightOwl uses a BYOD model: your PostgreSQL stores the exception data, NightOwl hosts the dashboard and does the grouping/alerting.

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