Laravel — How to make incident logs

So logging useful data is hard, logging useful data in a manner that makes it accessible is ever harder. Often we only log the exceptions thrown because that’s the route of the problem when in fact we need to see the entire history of the logs during a request, not just the stack trace of what made the application crash.

Imagine this scenario, your customer is using your site happily and then they attempt something to only receive a big 500 server error page. There’s no context on the page, nothing to help them fix the issue. If they’re not very technical themselves they have to find a way of describing what they were doing as they contact your company’s support for answers.

What would be better instead is if you can provide your customer with an error code at the point of failure. Then your support team can be provided with that code for a developer to match with the logs data from the request which produced the error. Giving you an instant point to start investigating with.

When developing in symfony we can create a “fingers crossed” Monolog handler and we could do the same in Laravel but the setup I’m going to show you something else that works an is a little bit easier to set up. In theory this should work with all Laravel 5.0+ projects.

Setting up a message logged event listener

To begin with we need to collect all the log messages as they’re made. Laravel’s logging system already handles the dispatch of these so we only need to make a listener for them. This can be achieved with:

php artisan make:listener LoggingListener -e Illuminate\\Log\\Events\\MessageLogged

Then edit the listener as shown in the example. We’ll simply add a collection where we can push those MessageLogged events to use later on should a fatal exception occur.

We also need to modify the EventServiceProvider namely the listen property to make sure that MessageLogged events get delivered to our new LoggingListener listener.

Also we need to make our listener a singleton otherwise we won’t be able to access the events collected as when we fetch the listener from the container we’ll receive a listener with an empty collection. For this only one line needs to be added which I’ve added to the AppServiceProvider.

Modifying the exception handler

Now we have our collection of MessageLogged events we need to modify our application exception handler to report the exception with the logs and output the error code for the user to see. This class is found in app/Exceptions/Handler.php which by default should have two methods in there, report and render.

To quickly summarise these two function, report happens first, the idea of it is to be able to send the exception to some kind of external service in the event of a failure. You can read more about this in the official docs. The render function is simply how you present an error page to the user upon a critical error. We won’t change the render function but we will have to alter the process by overriding the renderHttpException method in the parent class.

You’ll see in the report function I’ve added just a simple mechanism for storing the report to the local disk in a json file. This is purely for demonstration purposes. You could do any number of things with the events to make easier to analyse. Also for generating a unique ID I simply choose to use str_random which is provided by Laravel but in all honest you can use anything you like, just make sure it’s going to be unique enough that you don’t end up with multiple users with the same code.

As for adding the renderHttpException method, the code in the example is near identical to what’s in the parent class except for instead of just passing the variables errors and exception to the view we now also provide a errorCode variable.

Adding an error page to display the code

Of course the last part is that we need to have an error view which will display the error code we’re generating.

Any errors thrown should now end up looking something like the screenshot below.

Don’t forget at this point if you’re not set APP_DEBUG=true to APP_DEBUG=false in your .env file you won’t receive the production error message!

A quick test

Here’s a simple little route you can add to routes/web.php test that your error reports are being generated.

This tests will create two MessageLogged events, the first for the info line and a second upon Laravel catching the exception which will ultimately cause the error page to be shown. If using the example code this will create a corresponding json file in the storage/app/incident/ folder in your project.

So now you have a reliable process for viewing the logs of a single error should you need to. This really has only been an example of what can be done. I’ve no doubt that you can create an amazingly in depth system for tracking incidents with this mechanism.

You can find the fully working demo code on github.

I’m Peter Fox, a software developer in the UK who works with Laravel among other things. If you want to know more about me you can at and feel free to follow me @SlyFireFox on twitter for more Laravel tips and tutorials.