Back to Course |
Handling Exceptions and Errors in Laravel

Laravel Global Exception Handler

Laravel has an internal Exception Handler responsible for handling all the exceptions thrown in your application.

and


What Can You Do There

When you first open the file, you might think it has nothing!

app/Exceptions/Handler.php

class Handler extends ExceptionHandler
{
/**
* The list of the inputs that are never flashed to the session on validation exceptions.
*
* @var array<int, string>
*/
protected $dontFlash = [
'current_password',
'password',
'password_confirmation',
];
 
/**
* Register the exception handling callbacks for the application.
*/
public function register(): void
{
$this->reportable(function (Throwable $e) {
//
});
}
}

But if you look at the documentation - there's a lot of things you can do! Here's a list of some of them:

  • Handle different types of exceptions in different ways. For example, you can handle 404 errors differently than 500 errors.
  • Add more context to the logged Exception. For example, if you want to log the user's ID, you can do that.
  • Ignore exceptions not to be logged. For example, you can ignore the ModelNotFoundException Exception.
  • Give each Exception a rendered page. For example, you can make your exceptions more API friendly.

All of these are documented in the Exception Handler section of the documentation, but we will look at the most common ones.


Adding More Context to the Logged Exception

By default, Laravel logs the most critical information to the Log file, yet, you may add more context to the Exception. For example, add the user's model to the Exception. To do this, you can add the following code:

app/Exceptions/Handler.php

// ...
 
protected function context()
{
return ['user' => auth()->user()];
}
 
// ...

By doing this, you will gain access to the user's ID in the Log file, so instead of this:

[2023-05-21 09:18:23] production.ERROR: Error Processing Request {"userId":1,"exception":"[object] (Exception(code: 1): Error Processing Request at app/Http/Controllers/ImportController.php:27)
[stacktrace]
#0 app/Http/Controllers/ImportController.php(21): App\\Http\\Controllers\\ImportController->importData(Array)
#1 app/Http/Controllers/ImportController.php(13): App\\Http\\Controllers\\ImportController->import(Array)
#2 vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\ImportController->__invoke()
...

You will get this:

[2023-05-21 09:19:24] production.ERROR: Error Processing Request {"user":{"App\\Models\\User":{"id":1,"name":"Test User","email":"admin@admin.com","email_verified_at":"2023-05-19T06:10:23.000000Z","created_at":"2023-05-19T06:10:23.000000Z","updated_at":"2023-05-19T06:10:23.000000Z"}},"exception":"[object] (Exception(code: 1): Error Processing Request at app/Http/Controllers/ImportController.php:27)
[stacktrace]
#0 app/Http/Controllers/ImportController.php(21): App\\Http\\Controllers\\ImportController->importData(Array)
#1 app/Http/Controllers/ImportController.php(13): App\\Http\\Controllers\\ImportController->import(Array)
#2 vendor/laravel/framework/src/Illuminate/Routing/Controller.php(54): App\\Http\\Controllers\\ImportController->__invoke()
...

In addition to the Exception itself, the log entry contains all the information about the user that got the Exception. You can expand this context array with as much information as you want.


Working with Exception Render Pages

Another great use of the Exception Handler is to customize the error pages. We've all seen the default page, but having a custom page for your application may be a good brand experience for your users.

To do this, we can publish assets using the Artisan command:

php artisan vendor:publish --tag=laravel-errors

This command will create a new folder in resources/views/errors with all the error pages. You can customize them to your liking. Let's take a look at the 500.blade.php file:

resources/views/errors/500.blade.php

@extends('errors::minimal')
 
@section('title', __('Server Error'))
@section('code', '500')
@section('message', __('Server Error'))

It's not much, but we can do better! Let's write our custom error page using a free template

resources/views/errors/500.blade.php

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="apple-touch-icon" type="image/png"
href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png"/>
 
<meta name="apple-mobile-web-app-title" content="CodePen">
 
<link rel="shortcut icon" type="image/x-icon"
href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico"/>
 
<link rel="mask-icon" type="image/x-icon"
href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-b4b4269c16397ad2f0f7a01bcdf513a1994f4c94b8af2f191c09eb0d601762b1.svg"
color="#111"/>
<title> 500 - Internal Error</title>
<link rel="canonical" href="https://codepen.io/fcasantos/pen/LJXeKP"/>
<meta name=viewport content="width=device-width, initial-scale=1"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Merriweather:400,400i"/>
<style>
html {
box-sizing: border-box;
}
 
*,
*::before,
*::after {
box-sizing: inherit;
}
 
body * {
margin: 0;
padding: 0;
}
 
body {
font: normal 100%/1.15 "Merriweather", serif;
background-color: #7ed0f2;
color: #fff;
}
 
.wrapper {
position: relative;
max-width: 1298px;
height: auto;
margin: 2em auto 0 auto;
}
 
/* https://www.flaticon.com/authors/vectors-market */
/* https://www.flaticon.com/authors/icomoon */
.box {
max-width: 70%;
min-height: auto;
margin: 0 auto;
padding: 1em 1em;
text-align: center;
background: url("https://www.dropbox.com/s/xq0841cp3icnuqd/flame.png?raw=1") no-repeat top 10em center/128px 128px,
transparent url("https://www.dropbox.com/s/w7qqrcvhlx3pwnf/desktop-pc.png?raw=1") no-repeat top 13em center/128px 128px;
}
 
h1, p:not(:last-of-type) {
text-shadow: 0 0 6px #216f79;
}
 
h1 {
margin: 0 0 1rem 0;
font-size: 8em;
}
 
p {
margin-bottom: 0.5em;
font-size: 3em;
}
 
p:first-of-type {
margin-top: 4em;
}
 
p > a {
border-bottom: 1px dashed #216f79;
font-style: italic;
text-decoration: none;
color: #216f79;
}
 
p > a:hover {
text-shadow: 0 0 6px #216f79;
}
 
p img {
vertical-align: bottom;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
</head>
<body translate="no">
<div class="wrapper">
<div class="box">
<h1>500</h1>
<p>Sorry, it's me, not you.</p>
<p>&#58;&#40;</p>
<p><a href="/">Let me try again!</a></p>
</div>
</div>
</body>
</html>

Now, if we have an error come up - we will see a page that looks like this:

It can be redesigned to fit your application styling/needs. You can customize all the error pages by editing the files in resources/views/errors. Their names correlate to the HTTP Status code received.