Loading Jaywing website
2 January 2013 / Opinion

Automated error logging with Monolog

Dan Martin / Head of Systems Development

This is a brief post about my initial experiences with using Monolog to log and report on errors in a Symfony2 application.

There are plenty of more in-depth articles out there which I strongly suggest you read if you are interested in using Monolog, though they all seem to omit one crucial detail which cost me a lot of time in getting it working - which I will get to shortly.

Monolog is an extremely flexible logging framework which comes bundled with Symfony2. It uses a hierarchy of handlers to determine how to log messages from the system and comes bundled with a variety of useful handlers.

I’ve made use of four of them: the Streamed handler, the Buffered handler, the Swiftmail handler and the ChromePHP handler.

The Streamed handler is the most straightforward. This is used to log everything to a file and is part of the default Symfony config:

streamed: type: stream path: “%kernel.logs_dir%/%kernel.environment%.log” level: debug

This will log all messages to a file in the logs directory e.g. app/logs/dev.log and provides a record of all activity. It’s very useful but we can’t expect to monitor it all the time, so we need something to alert us if anything really bad happens.

Monolog provides something called a ‘fingers crossed’ handler, which can be configured to log anything above a certain level (e.g. critical):

mail: type: fingers_crossed action_level: critical handler: buffered

The handler is called mail because that’s ultimately what it’s going to do, but this handler doesn’t actually send any mail. Instead it invokes the Buffered handler, which is going to swallow up all the log messages until a critical event occurs.

The Buffered handler is configured as follows:

buffered: type: buffer handler: swift T

his, then, simply holds onto the messages until it’s told to release them (by a critical event) and then passes them on to the Swiftmail handler which will send the mail:

swift: type: swift_mailer from_email: <from address> to_email: <to address> subject: “An Error Occurred!”

This, of course, requires the Swiftmail service to be configured (see the Symfony2 documentation.) As we use gmail for our email system we use the gmail configuration for Swiftmail. Important- This is the bit that took me ages to figure out! The sample Swiftmail configuration includes the line:

spool: { type: memory }

The spool option causes emails to be stored whilst the HTTP request is processing, then sent afterwards, so that the user’s experience is not affected by any delay in sending mail. When using Swiftmail with Monolog, this memory spooling appears not to work.

The event that triggers sending seems to fire before the error is logged, so is not sent during this request and since it’s only a memory spool it’s then discarded without being sent. There is an option to spool to disk, but since I’m only sending emails on an error condition I decided just to comment the line out, meaning emails are sent immediately.

There might be a slight delay for the user but since something is broken anyway, I decided it didn’t matter.

This configuration causes the system to send me email notifications of any fatal errors including the log messages leading up to the error. It will allow me to see any problems as they happen without needing users to report them, and it will give me the detail I need to investigate the problem.

One thing I’d like to improve is the legibility of the email. At the moment it’s just a dump of all the logs around the error and it’s a bit of a job to identify the critical error which caused the problem.

It shouldn’t be too much of a stretch to create a Monolog Formatter to generate HTML emails with some colour coding to identify the line with the error.

The final type of handler I’ve used is the ChromePHP handler. ChromePHP is an extension for Google Chrome which allows PHP to fire messages into Chrome’s debug console.

This handler allows me to send all the logs into the console so if there’s a problem I can view PHP logs in the same way I would view javascript errors. Obviously this is only enabled in the development environment because we don’t want the error logs publicly available in production.

I’ve only really scratched the surface of using Monolog and it looks extremely powerful. I want to try to improve the monitoring of all our tools over the coming months, so I will no doubt be investigating it further to see what else I can make it do.