Rendering is what the client sees. Reporting is what you see: the log, the error tracker, the alert that wakes someone.
Laravel already ignores the exceptions that are a consumer’s mistake and not yours. Validation failures, authentication failures, 404s, and throttling are never reported. You don’t write that list.
What you add are the failures that are expected in your system:
// bootstrap/app.php
$exceptions->dontReportDuplicates();
$exceptions->level(
ConnectionException::class,
LogLevel::WARNING,
);
A provider timeout is worth knowing about and not worth waking anyone for, so it is logged as a warning. When the provider is down for an hour, it is also not worth ten thousand identical entries: $exceptions->throttle() takes a rate limit and reports only that many of an exception per minute. (Laravel documentation: Error Handling › Throttling Reported Exceptions.) For an exception that is purely control flow, such as a job saying “not ready yet, try again,” implement ShouldntReport on the class and it is never reported at all:
// app/Exceptions/NotReadyYet.php
class NotReadyYet extends Exception implements ShouldntReport
{
//
}
No if in a report callback, and no noise drowning out the exceptions that need a human.
Don’t Call Your Error Tracker by Hand
An error tracker, whether Nightwatch, Sentry, or Flare, registers itself with Laravel’s exception handler when you install it. Every reported exception reaches it. You don’t need to send anything. One setting is yours, though: have it scrub the Authorization header, or every report carries a working token into somebody else’s storage.
I learned that the slow way. A report callback I wrote called the tracker directly, with a branch per exception type to set the severity, and wrapped the calls in try/catch (Throwable) so that a tracker outage could never break a request. The methods I was calling didn’t exist on that tracker’s facade. Every call threw. The catch swallowed it. For as long as that code ran, the severity logic I had written did nothing, and nothing told me, because the one thing built to catch a silent failure was the thing silently failing.
The tracker had been receiving every exception anyway, through its own hook. The callback was unnecessary from the start.
What I took from it: use level(), dontReport(), and ShouldntReport to shape reporting, and let the tracker listen. And treat an empty catch (Throwable) as a bug until proven otherwise.