Learn how to handle global exceptions in Laravel, customize 404/500 error pages, and implement robust logging to keep your Task Manager app stable and professional.
Previously in this course, we explored using database transactions in Laravel to ensure data atomicity. In this lesson, we shift our focus from database integrity to application stability by learning how to handle global exceptions, customize error views, and log failures effectively.
In a perfect world, your code would never fail. In the real world, database connections drop, third-party APIs time out, and users attempt to access resources that don't exist. When these "exceptional" events occur, PHP throws an exception.
If unhandled, your application crashes, potentially exposing sensitive stack traces to the user. Laravel’s core provides a centralized bootstrap/app.php file (in Laravel 11) where you can define how your application responds to these failures. By catching and logging these exceptions, you transform a jarring crash into a graceful user experience.
Laravel ships with default error pages, but they rarely match your application’s brand. To customize them, you need to create specific Blade files in your resources/views/errors directory.
mkdir -p resources/views/errors.404.blade.php for "Not Found" errors.500.blade.php for "Server Error" issues.If you create resources/views/errors/404.blade.php, Laravel will automatically render this view whenever an HttpException with a 404 status code occurs. This is much cleaner than building custom error responses clients can actually use for your API manually in every controller.
While global handlers manage unhandled exceptions, you should use try/catch blocks locally when you anticipate a specific point of failure.
Let’s update our TasksController to handle a scenario where an external service (like a hypothetical task-syncing API) might fail:
PHPpublic function sync(Task $task) { try { #6A9955">// Assume this calls an external service $this->taskService->syncToRemote($task); } catch (\Exception $e) { #6A9955">// Log the error for the developer Log::error("Task sync failed: " . $e->getMessage(), ['task_id' => $task->id]); #6A9955">// Redirect with a friendly message for the user return back()->with('error', 'We couldn\'t sync this task right now, but it is saved locally.'); } }
By wrapping the call in a try block and logging the error in the catch block, we prevent the entire application from failing while keeping our logs populated with actionable data.
Laravel uses the Monolog library, which is incredibly powerful. You aren't limited to Log::error(). You can log at different levels: info, warning, emergency, and debug.
In your bootstrap/app.php, you can customize how your application handles exceptions globally. If you want to perform a specific action every time an error occurs, use the withExceptions method:
PHP#6A9955">// bootstrap/app.php ->withExceptions(function (Exceptions $exceptions) { $exceptions->report(function (InvalidTaskException $e) { #6A9955">// Send a notification to Slack or an error tracking service }); })
This approach is similar to how you might implement professional error handling and logging for WordPress plugins, ensuring that you have visibility into issues before your users report them.
resources/views/errors/404.blade.php file.layouts.app) to ensure the page matches your app's style./tasks/999999) to verify that your custom template renders.APP_DEBUG=true in production: This shows the full stack trace to the user, which is a massive security risk. Always set APP_DEBUG=false in production.\Exception too broadly: Try to catch specific exceptions (like ModelNotFoundException or QueryException) rather than the base \Exception class, so you don't accidentally hide bugs you should be fixing.error. Use info for standard logs and error or critical for actual failures.Effective error management is the difference between a amateur project and a production-grade application. By customizing your 404/500 pages, using try/catch for localized failures, and leveraging Laravel's built-in logging, you ensure that your Task Manager remains resilient and user-friendly.
Up next: We will prepare your application for the real world by configuring production-specific settings and optimizing your server environment.
Stop manually testing your forms. Learn how to use Laravel's testing suite to automate validation checks, simulate authenticated users, and ensure data integrity.
Read moreLearn how to use View Composers in Laravel to share data across your Blade templates without repeating code in your controllers. Keep your app DRY.
Handling Global Exceptions