Laravel 8 Error Logging using Sentry

Laravel 8 Error Logging using Sentry

In a production environment, it would be great to get an instant alert if any error occurred in your application. If you are part of a big team, then it would be ideal that the alert will be passed on to the right person to resolve the issue.

Imagin if some error occurred in your application on the production environment and then you have to go through the large file of error logs to find what actually happened at a given time for a particular user (happened to me so many times). It would be ideal to browse through the error history of your application right within your browser.

That’s where the Sentry comes into play. Sentry is a cloud-based error tracking tool that comes with free and paid subscriptions. You can browse through the full list of features on their website at Sentry.io. One of the coolest features of Sentry is the fact that it’s super easy to integrate into your application. So let’s get started.

Installation

Firstly, you’ll need to register an account on the Sentry website (the free plan will be enough to get you started) and create a project. The process of creating a project is very easy, even there is an option for Laravel projects which will tailor the documentation according to your project. Once you have done that, come back here!

I will be using the latest release of Laravel which is ^8.0. To create a new Laravel application, I will use the composer create-project command.

composer create-project laravel/laravel laravel-sentry

Sentry can be integrated into Laravel application using the native package sentry-laravel. We will now install this package using the composer.

composer require sentry/sentry-laravel

Once the package is installed, we will need to generate the config file for this package using the below command.

php artisan vendor:publish --provider="Sentry\Laravel\ServiceProvider"

When you create a new account on the Sentry website, you will be given a DSN string which is like an API key, if you haven’t copied it yet, you can grab it from the settings area. Next, we will add that key to our .env file.

SENTRY_LARAVEL_DSN=https://[email protected]/0000000
Where xxxxx and 0000000 will be equivlent to your keys from Sentry.

Sentry documentation for Laravel will advise you to hook into Laravel error capturing via an Exception handler.

I always set up the Sentry using the log channel so will recommend you follow this approach as it will be more reliable.

To use the log channel, you will need to add a new channel to your config/logging.php configuration file.

'channels' => [
    // ...
    'sentry' => [
        'driver' => 'sentry',
        'level'  => null,
        'bubble' => true,
    ],
],

You can see two extra configuration values in the above code snippet, level and bubble.

The level, allows you to control the minimum error level that will be sent to the sentry. Laravel uses Monolog for handling errors, so you can take a look at the available levels in their documentation.

The bubble allows/prevents errors from being reported to other channels after the error being handled by Sentry.

Configuring Channel

To configure your application to use the Sentry as a log channel, simply change the LOG_CHANNEL value to below.

LOG_CHANNEL=sentry

From now on all errors will be logged to Sentry.

In my application, I like to use the stack option which will enable our application to log errors to multiple channels. I usually use the daily and sentry as my stack because I firstly like to log errors to a local file and then send it to Sentry. This enables me to keep track of errors in a log file and it will be never missed.

So for my above approach, update the .env file to below.

LOG_CHANNEL=stack

and in the config/logging.php file update the stack channel as below.

    'channels' => [
        'stack' => [
            'driver'   => 'stack',
            'channels' => ['daily', 'sentry']
        ],
        'daily' => [
            'driver' => 'daily',
            'path'   => storage_path('logs/laravel.log'),
            'level'  => 'debug',
            'days'   => 28,
        ],
        'sentry' => [
            'driver' => 'sentry',
            'level'  => 'debug',
            'bubble' => true,
        ],
    ...
],

Testing Error Logging

By now we have integrated the Sentry into our application, now we will test if everything is set up properly. For this, the Sentry package provides a nice command for testing the integration.

In your console run the below command.

php artisan sentry:test

If everything is set up properly, you will see an output like below.

[Sentry] DSN discovered!
[Sentry] Generating test Event
[Sentry] Sending test Event
[Sentry] Event sent with ID: 31e6f44928284d67b891bf4b5415fbbb

You will also see an issue will be added to your Sentry dashboard on the Sentry website for this test.

Sentry Error Log Testing

We can also test it by throwing an exception, For example, add the below route in your routes/web.php file and visit the route in the browser.

Route::get('/sentry-test', function () {
    throw new \Exception("Sentry Test Exception");
});

You will receive an email alert if you have set up that in the Sentry account area and will also see it on the Sentry dashboard like below.

Sentry Erro Log Entry through Route

Adding User Context to Error Logs

If you have spent some time on Sentry or are already using it, you will have noticed that Sentry provides a lot of information about your application.

It would be nice to know which error occurred for which user so we can reach the users to inform them that error has been resolved.

To add which user was active at the time of the error, we will need to create a middleware to pass current user information to Sentry.

Run the below command to generate a middleware class.

php artisan make:middleware SentryUser

This will generate a middleware class at app/Http/Middleware.

Open the newly created class and replace it with the below content.

namespace App\Http\Middleware;

    use Auth;
    use Closure;
    use Sentry\State\Scope;
    
    class SentryUser
    {
        /**
         * Handle an incoming request.
         *
         * @param \Illuminate\Http\Request $request
         * @param \Closure                 $next
         *
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            if(Auth::check() && app()->bound('sentry'))
            {
                \Sentry\configureScope(function (Scope $scope): void {
                    $scope->setUser([
                        'id'    => Auth::user()->id,
                        'email' => Auth::user()->email,
                        'name'  => Auth::user()->name,
                    ]);
                });
            }
    
            return $next($request);
        }
    }

The pretty easy stuff is happening in this middleware class, firstly we check if there is a user authenticated and Sentry is available from the Laravel container. Then, we simply pass the details of the current logged in user to Sentry. The array can be altered according to your need to pass any additional information to Sentry.

Now to use this middleware, we will need to register this middleware in your Laravel application, open the app/Http/Kernel.php file and add it to the $middlewareGroups array like below.

protected $middlewareGroups = [
    'web' => [
        // ...
        \App\Http\Middleware\SentryUser::class
    ]
];

Now when an error is logged with Sentry and a user is authenticated, you should see the user information displayed with the error details in your Sentry dashboard.

Final Words

Hopefully, this post will help you to integrate the Sentry into your application for better error logging and log alerts. Sentry offers so many features such as release tracking, detailed control over notifications, removing sensitive data captured by Sentry (known as data scrubbing), etc. – check out the documentation for more details.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

When sharing a code snippet please wrap you code with pre tag and add a class code-block to it like below.
<pre class="code-block">you code here</pre>

*
*

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Your little help will keep this site alive and help us to produce quality content for you.