Back to Course |
Handling Exceptions and Errors in Laravel

Log Exceptions and Return Nice Messages

When dealing with exceptions - we want to display nice messages to the user, as no one wants to see things like this:


Returning Nice Messages

The simplest way to return a nice message to the user is to return a friendlier message or redirect the user to a page that displays a friendlier message:

Controller

public function __invoke()
{
$data = [['name' => 'John Doe', 'email' => 'email@email.com', 'date' => '23-04']];
 
try {
$this->import($data);
} catch (ImportHasMalformedData $e) {
return redirect()->route('dashboard')->with('message', 'Uh oh, the import has failed. Try again later or contact support');
}
 
return 'Imported';
}

Then we can display the message in the Blade View:

@if(session()->has('message'))
<div class="bg-red-100 shadow">
<div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8">
{{ session('message') }}
</div>
</div>
@endif

Which will result in the following message being displayed:

It looks much better and provides a better user experience.


Log Exceptions to External Tools

While displaying a nice message to the user is excellent, we also want to log the Exception to investigate later. Otherwise, how can we fix a bug if we don't know what caused it?

For this, we have quite a few options:

All of them are meant to log your errors/exceptions with helpful information so that you can investigate them later. Let's look at Bugsnag (as it is the one I use) and how it is used, and what it shows:

use App\Exceptions\ImportHasMalformedData;
use Bugsnag\BugsnagLaravel\Facades\Bugsnag;
use Carbon\Carbon;
use Exception;
 
class ImportController extends Controller
{
public function __invoke()
{
$data = [['name' => 'John Doe', 'email' => 'email@email.com', 'date' => '23-04']];
 
try {
$this->import($data);
} catch (ImportHasMalformedData $e) {
// Here we inform Bugsnag that it should log this Exception!
Bugsnag::notifyException($e);
 
return redirect()->route('dashboard')->with('message', 'Uh oh, the import has failed. Try again later or contact support');
}
 
return 'Imported';
}
 
public function import($data)
{
try {
foreach ($data as $row) {
$date = Carbon::parse($row['date']);
}
} catch (Exception $exception) {
// Here, you might notice that we pass the Exception as a 3rd parameter to the Exception we throw.
// This will carry over all the previous exceptions giving us a full stack trace.
throw new ImportHasMalformedData('The import has malformed data.', 500, $exception);
}
}
}

Now if we go to the Bugsnag dashboard, we will see the following:

We can see many details in the stack trace, along with the original Exception thrown.

But what if we don't pass the 3rd parameter to the Exception we throw? Let's see:

use App\Exceptions\ImportHasMalformedData;
 
// ...
 
public function import($data)
{
try {
foreach ($data as $row) {
$date = Carbon::parse($row['date']);
}
} catch (Exception $exception) {
// Here, we attempt the same Exception as before but don't pass the 3rd parameter.
throw new ImportHasMalformedData('The import has malformed data.', 500, $exception);
throw new ImportHasMalformedData('The import has malformed data.', 500);
}
}

This will result in:

So this view doesn't show us more information about real issues (in this case, Carbon problems).

So, as you can see, those 3rd party tools are life-savers in complex systems as they log a lot of details automatically, allowing you to reproduce and fix issues.