One big issue of the PHP error handling is that there’s no built-in way to catch fatal errors with an user-defined error handler. So I thought a little bit about it and maybe you have better approaches or solutions …
The short goal is to send the error via e-mail to the developer(s). As we are security-aware, we’re logging errors and do not display them to the world. (Hint: that should be your default on every production machine!)
With the error_log directive we have 3 possibilties: syslog, sapi and a common logfile. Sapi in my case is the mod_php Apache module that logs into the error_log by default so we can classify sapi and a normal logfile to be the same in this case.
Now I see two possibilities - file watching and syslogger.
First I looked into the syslogger if there is any possibility to send e-mails. This would be nice …
The original bsd syslogger used by the most Linux distributions can’t. And there’s a reason, yeah ;) The syslogger logs many events, even critical system ones (emergency etc). If the system is unstable and something really goes wrong the mailing subsystem shouldn’t be invoked. Yes that’s plausible to me. [1]
There are some people that built named pipes with the syslogger [2]. But that got too weird to me ;) Another issue is that the error_log-syslog behavior (facility, priority) of PHP seems to be not really documented. So I didn’t look deeper into syslog… did u make other / better experiences with syslog? I’d appreciate feedback because I didn’t got deeper into the syslog approach …
So lets get back. The more pragmatic approach is an external log file analyzier. The common suspects are:
Logwatch and Logcheck are not real-time, but we want real-time.
So there are Logsurfer and Swatch left. Because Swatch is available as Debian package (and I like standardized systems) I’ve chosen it. Swatch has a fine and clean but flexible configuration approach and is really easy to setup. The heart is this config-file:
watchfor /(PHP.*error:.*?)$/i mail addresses=root threshold=on threshold track_by=$1,type=limit,count=1,seconds=10
This means in spoken words:
- Look for any line matching this regular expression (Fatal, Parse, Recoverable errors)
- Send this line to root
- Limit repeating lines to one per ten second (and use the $1 substitution of the matched line, this is the error message itself without the date)
Number 3 is the most interesting point because one can tune it and you don’t get spammed if the Fatal Error occures many times.
Now we start the daemon:
# swatch --daemon -c /root/php_fatal.conf --tail-file=/var/log/apache2/error.log
To test it raise a fatal error and wait for the mail:
From: rootTo: root@server01.intern.northclick.de Subject: Message from Swatch [Tue May 15 13:24:12 2007] [error] [client 192.168.2.16] PHP Parse error: syntax error, unexpected T_CONSTANT_ENCAPSED_STRING, expecting ‘)’ in /home/soenke/tests/fatal.php on line 1
You want to put the swatch daemon into your rc.local or some other startup file so it’s started when the machine boots.
This technique seems the most clean to me.
What do you use? What other approaches do you have?
References:
[1] comp.os.linux.networking - How to send a syslog message by E-mail?
[2] http://www.softpanorama.org/Logs/Syslog/pipes_in_syslog.shtml


May 15th, 2007 at 01:59
We’re going down the error_get_last() path. See: http://us2.php.net/manual/en/function.error-get-last.php
It’ll pull the same data that would be in the log file on the next page render with a nice numerical value to compare against (instead of regex-ing strings.)
The trick is that there’s no “error_clear_last” feature, and this doesn’t get cleared out until another error occurrs. Therefore you need to generate a throw-away trigger_error that will be ignored on all following page renders (because it’s USER_NOTICE not a FATAL_ERROR.) Also, you need to accept the fact that you may get multiple notifications for a single fatal error depending on race conditions.
Both of which are cheap prices to pay to find out about PHP’s dreaded White Page of Death (WPoD) using a single error handling solution.
May 15th, 2007 at 07:57
Actually, some of fatal errors can be caught using register_shutdown_handler…
May 15th, 2007 at 07:57
oops… that was supposed to be “register_shutdown_function”
May 15th, 2007 at 15:08
We use the standard php error logging to log fatal php errors to a file, and run a cronjob every three minutes to send the contentsto the development team, with checks to make sure it isn’t mailing anything too big, then truncate the log. Its not as tidy or as useful I might like, but its really quick to set up.
The only other real alternative for it that I’ve found is the Zend platform. ZP is pretty whizzy when it comes to monitoring your application though, as you can use it to recieve error notifications with backtraces, request URIs, and a whole bunch of stuff that you wouldn’t normally know about which is dead handy when you want to actually fix the problem. Monitoring slow running scripts is handy too.
May 18th, 2007 at 12:37
Hey Guys,
thanks for all your input on this. There are (interesting and some scary :D ) approaches. But that shows me that I’m not the only one that tries to find solutions for fatal errors.
October 22nd, 2007 at 20:20
ini_set(’display_errors’, ‘On’);
ini_set(’error_prepend_string’, );
ini_set(’error_append_string’, );
ob_start(’fatal_error_handler’);
function fatal_error_handler($bufferContents)
{
$output = $bufferContents;
$matches = array();
$errors = “”;
if ( preg_match(’|.*|s’, $output, &$matches) )
{
foreach ($matches as $match)
{
$errors .= strip_tags($match) . “\n\n—\n\n”;
}
//Do your error logging/emailing/AIMing here
//and overwrite $output so errors are not displayed.
}
return $output;
}
October 22nd, 2007 at 20:21
Input validation stripped some necessary code out of some hopefully obvious places.
January 30th, 2008 at 11:24
Using swatch had one problem left: after the apache error.log was taken away by logrotate, swatch didn’t work any longer.That’s because swatch starts the ‘tail’ with the options ‘-n0 -f’. The ‘-f’ is the same as ‘–follow=descriptor’, but the file descriptor points to the wrong file after the logrotation (file is renamed and later compacted, and not used for logging data).
We solved it by starting swatch with this option: –tail-args=’–follow=name -n 0′
Using ‘–follow=name’ makes tail watching the log file by its name rather than the file descriptor. So it works even after the logrotation has created a new file. The swatch process itself logs the change too:
/usr/bin/tail: `/var/log/apache2/error.log’ has been replaced; following end of new file
April 6th, 2010 at 02:30
ZP is pretty whizzy when it comes to monitoring your application though, as you can use it to recieve error notifications with backtraces, request URIs, and a whole bunch of stuff that you wouldn’t normally know about which is dead handy when you want to actually fix the problem.
July 6th, 2010 at 18:49
If you want to buy a house, you will have to get the business loans. Moreover, my brother all the time takes a bank loan, which seems to be really useful.
July 27th, 2010 at 14:37
Heya..thanks for the post and great tips..even I also think that hard work is the most important aspect of getting success.
July 27th, 2010 at 14:38
I really liked the post and the stories are really thanks for sharing the informative post.
July 27th, 2010 at 14:40
This is one of the best posts that I’ve ever seen; you may include some more ideas in the same theme. I’m still waiting for some interesting thoughts from your side in your next post.
July 27th, 2010 at 14:41
I have read a number of posts of yours, but this is the one that I like the most. So expecting some more ideas from your side. Thanks