1. Code
  2. PHP
  3. Laravel

Exception Handling in Laravel

Scroll to top

In this article, we're going to explore one of the most important and least discussed features of the Laravel web framework—exception handling. Laravel comes with a built-in exception handler that allows you to report and render exceptions easily and in a friendly manner.

In the first half of the article, we'll explore the default settings provided by the exception handler. In fact, we'll go through the default Handler class in the first place to understand how Laravel handles exceptions.

In the second half of the article, we'll go ahead and see how you could create a custom exception handler that allows you to catch custom exceptions.

Setting Up the Prerequisites

Before we dive into the handler class straight away, let's have a look at a couple of important configuration parameters related to exceptions.

Go ahead and open the config/app.php file. Let's have a close look at the following snippet.

1
...
2
/*

3
|--------------------------------------------------------------------------

4
| Application Debug Mode

5
|--------------------------------------------------------------------------

6
|

7
| When your application is in debug mode, detailed error messages with

8
| stack traces will be shown on every error that occurs within your

9
| application. If disabled, a simple generic error page is shown.

10
|

11
*/
12
13
'debug' => (bool) env('APP_DEBUG', false),
14
...
15
...

As the name suggests, if it's set to TRUE, it'll help you to debug errors that are generated by an application. The default value of this variable is set to a value of the APP_DEBUG environment variable in the .env file.

In the development environment, you should set it to TRUE so that you can easily trace errors and fix them. On the other hand, you want to switch it off in the production environment, and it'll display a generic error page in that case.

Logging Configuration File

In addition to displaying errors, Laravel allows you to log errors in the log file. Let's have a quick look at the options available for logging. Let's visit the config/logging.php file and have a close look at the following snippet.

1
<?php
2
3
use Monolog\Handler\NullHandler;
4
use Monolog\Handler\StreamHandler;
5
use Monolog\Handler\SyslogUdpHandler;
6
7
return [
8
9
    /*

10
    |--------------------------------------------------------------------------

11
    | Default Log Channel

12
    |--------------------------------------------------------------------------

13
    |

14
    | This option defines the default log channel that gets used when writing

15
    | messages to the logs. The name specified in this option should match

16
    | one of the channels defined in the "channels" configuration array.

17
    |

18
    */
19
20
    'default' => env('LOG_CHANNEL', 'stack'),
21
22
    /*

23
    |--------------------------------------------------------------------------

24
    | Log Channels

25
    |--------------------------------------------------------------------------

26
    |

27
    | Here you may configure the log channels for your application. Out of

28
    | the box, Laravel uses the Monolog PHP logging library. This gives

29
    | you a variety of powerful log handlers / formatters to utilize.

30
    |

31
    | Available Drivers: "single", "daily", "slack", "syslog",

32
    |                    "errorlog", "monolog",

33
    |                    "custom", "stack"

34
    |

35
    */
36
37
    'channels' => [
38
        'stack' => [
39
            'driver' => 'stack',
40
            'channels' => ['single'],
41
            'ignore_exceptions' => false,
42
        ],
43
44
        'single' => [
45
            'driver' => 'single',
46
            'path' => storage_path('logs/laravel.log'),
47
            'level' => 'debug',
48
        ],
49
50
        'daily' => [
51
            'driver' => 'daily',
52
            'path' => storage_path('logs/laravel.log'),
53
            'level' => 'debug',
54
            'days' => 14,
55
        ],
56
57
        'slack' => [
58
            'driver' => 'slack',
59
            'url' => env('LOG_SLACK_WEBHOOK_URL'),
60
            'username' => 'Laravel Log',
61
            'emoji' => ':boom:',
62
            'level' => 'critical',
63
        ],
64
65
        'papertrail' => [
66
            'driver' => 'monolog',
67
            'level' => 'debug',
68
            'handler' => SyslogUdpHandler::class,
69
            'handler_with' => [
70
                'host' => env('PAPERTRAIL_URL'),
71
                'port' => env('PAPERTRAIL_PORT'),
72
            ],
73
        ],
74
75
        'stderr' => [
76
            'driver' => 'monolog',
77
            'handler' => StreamHandler::class,
78
            'formatter' => env('LOG_STDERR_FORMATTER'),
79
            'with' => [
80
                'stream' => 'php://stderr',
81
            ],
82
        ],
83
84
        'syslog' => [
85
            'driver' => 'syslog',
86
            'level' => 'debug',
87
        ],
88
89
        'errorlog' => [
90
            'driver' => 'errorlog',
91
            'level' => 'debug',
92
        ],
93
94
        'null' => [
95
            'driver' => 'monolog',
96
            'handler' => NullHandler::class,
97
        ],
98
99
        'emergency' => [
100
            'path' => storage_path('logs/laravel.log'),
101
        ],
102
    ],
103
104
];

Laravel logging is based on channels. Each channel provides a different way of writing log information. For example, the single channel writes logs to the log file, and the slack channel is used to send logs to Slack to notify your team. As Laravel uses the Monolog PHP library for logging, you should set the above options in the context of that library. By default, Laravel uses the stack channel for logging messages.

The default log file is located at storage/logs/laravel.log, and it's sufficient in most cases. On the other hand, the level attribute in the channel configuration is set to a value that indicates the severity of errors that'll be logged.

The Handler Class

Next, let's have a look at the default Handler class that comes with the default Laravel application. Go ahead and open the app/Exceptions/Handler.php file.

1
<?php
2
3
namespace App\Exceptions;
4
5
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
6
use Throwable;
7
8
class Handler extends ExceptionHandler
9
{
10
    /**

11
     * A list of the exception types that are not reported.

12
     *

13
     * @var array

14
     */
15
    protected $dontReport = [
16
        //

17
    ];
18
19
    /**

20
     * A list of the inputs that are never flashed for validation exceptions.

21
     *

22
     * @var array

23
     */
24
    protected $dontFlash = [
25
        'password',
26
        'password_confirmation',
27
    ];
28
29
    /**

30
     * Report or log an exception.

31
     *

32
     * @param  \Throwable  $exception

33
     * @return void

34
     *

35
     * @throws \Throwable

36
     */
37
    public function report(Throwable $exception)
38
    {
39
        parent::report($exception);
40
    }
41
42
    /**

43
     * Render an exception into an HTTP response.

44
     *

45
     * @param  \Illuminate\Http\Request  $request

46
     * @param  \Throwable  $exception

47
     * @return \Symfony\Component\HttpFoundation\Response

48
     *

49
     * @throws \Throwable

50
     */
51
    public function render($request, Throwable $exception)
52
    {
53
        return parent::render($request, $exception);
54
    }
55
}

There are two important functions that the handler class is responsible for—reporting and rendering all errors.

Let's have a close look at the report method.

1
/**

2
 * Report or log an exception.

3
 *

4
 * @param  \Throwable  $exception

5
 * @return void

6
 *

7
 * @throws \Throwable

8
 */
9
public function report(Throwable $exception)
10
{
11
    parent::report($exception);
12
}

The report method is used to log errors to the log file. At the same time, it's also important to note the dontReport property, which lists all types of exceptions that shouldn't be logged.

Next, let's bring in the render method.

1
/**

2
 * Render an exception into an HTTP response.

3
 *

4
 * @param  \Illuminate\Http\Request  $request

5
 * @param  \Throwable  $exception

6
 * @return \Symfony\Component\HttpFoundation\Response

7
 *

8
 * @throws \Throwable

9
 */
10
public function render($request, Throwable $exception)
11
{
12
    return parent::render($request, $exception);
13
}

If the report method is used to log or report errors, the render method is used to render errors on a screen. In fact, this method handles what will be displayed to users when the exception occurs.

The render method also allows you to customize a response for different types of exceptions, as we'll see in the next section.

Custom Exception Class

In this section, we'll create a custom exception class that handles exceptions of the CustomException type. The idea behind creating custom exception classes is to easily manage custom exceptions and render custom responses at the same time.

Go ahead and create a file app/Exceptions/CustomException.php with the following contents.

1
<?php
2
 
3
namespace App\Exceptions;
4
 
5
use Exception;
6
 
7
class CustomException extends Exception
8
{
9
    /**

10
     * Report the exception.

11
     *

12
     * @return void

13
     */
14
    public function report()
15
    {
16
    }
17
 
18
    /**

19
     * Render the exception into an HTTP response.

20
     *

21
     * @param  \Illuminate\Http\Request

22
     * @return \Illuminate\Http\Response

23
     */
24
    public function render($request)
25
    {
26
        return response()->view(
27
                'errors.custom',
28
                array(
29
                    'exception' => $this
30
                )
31
        );
32
    }
33
}

The important thing to note here is that the CustomException class must extend the core Exception class. For demonstration purposes, we'll only discuss the render method, but of course you could also customize the report method.

As you can see, we're redirecting users to the errors.custom error page in our case. In that way, you can implement custom error pages for specific types of exceptions.

Of course, we need to create an associated view file at resources/views/errors/custom.blade.php.

1
Exception details: <b>{{ $exception->getMessage() }}</b>

That's a pretty simple view file that displays an error message, but of course you could design it the way you want it to be.

We also need to make changes in the render method of the app/Exceptions/Handler.php file so that our custom exception class can be invoked. Let's replace the render method with the following contents in the app/Exceptions/Handler.php file.

1
/**

2
 * Render an exception into an HTTP response.

3
 *

4
 * @param  \Illuminate\Http\Request  $request

5
 * @param  \Throwable  $exception

6
 * @return \Symfony\Component\HttpFoundation\Response

7
 *

8
 * @throws \Throwable

9
 */
10
public function render($request, Throwable $exception)
11
{
12
    if ($exception instanceof \App\Exceptions\CustomException)  {
13
        return $exception->render($request);
14
    }
15
16
    return parent::render($request, $exception);
17
}

As you can see, we are checking the type of an exception in the render method in the first place. If the type of an exception is \App\Exceptions\CustomException, we call the render method of that class.

How to Test Our CustomException Class

So everything is in place now. Next, let's go ahead and create a controller file at app/Http/Controllers/ExceptionController.php so we can test our custom exception class.

1
<?php
2
namespace App\Http\Controllers;
3
 
4
use App\Http\Controllers\Controller;
5
 
6
class ExceptionController extends Controller
7
{
8
    public function index()
9
    {
10
        // something went wrong and you want to throw CustomException

11
        throw new \App\Exceptions\CustomException('Something Went Wrong.');
12
    }
13
}

Of course, you need to add an associated route in the routes/web.php, as shown in the following snippet.

1
// Exception routes

2
Route::get('exception/index', 'ExceptionController@index');

And with that in place, you can visit the https://your-laravel-site.com/exception/index URL to see if it works as expected. It should display the errors.custom view as per our configuration.

So that's how to handle custom exceptions in Laravel.

Conclusion

Today, we went through the exception handling feature in Laravel. At the beginning of the article, we explored the basic configuration provided by Laravel in order to render and report exceptions. Further, we had a brief look at the default exception handler class.

In the second half of the article, we prepared a custom exception handler class that demonstrated how you could handle custom exceptions in your application.

Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.