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
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
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,
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=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.