inicio sindicaci;ón

Getting the PHP fatal errors

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:

  1. Look for any line matching this regular expression (Fatal, Parse, Recoverable errors)
  2. Send this line to root
  3. 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: root 
To: 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

14 Kommentare to “Getting the PHP fatal errors”

  1. Andrew Magruder Says:

    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.

  2. Alexey Zakhlestin Says:

    Actually, some of fatal errors can be caught using register_shutdown_handler…

  3. Alexey Zakhlestin Says:

    oops… that was supposed to be “register_shutdown_function”

  4. Marc Gear Says:

    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.

  5. Soenke Ruempler Says:

    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.

  6. blah Says:

    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;
    }

  7. blah Says:

    Input validation stripped some necessary code out of some hopefully obvious places.

  8. Michael Dunsky Says:

    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

  9. ClubPenguinCheats Says:

    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.

  10. PerryElnora34 Says:

    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.

  11. Acai berry diet Says:

    Heya..thanks for the post and great tips..even I also think that hard work is the most important aspect of getting success.

  12. Acai berry free trial Says:

    I really liked the post and the stories are really thanks for sharing the informative post.

  13. Acai berry select Says:

    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.

  14. How to get pregnant Says:

    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

Leave a Reply

*
To prove that you're not a bot, enter this code
Anti-Spam Image