Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHP: log stack trace for FATAL errors in production

I want to log every fatal errors, even timeout and others E_ERRORS ones. I use set_error_handler and shutdown function, but with the last one I cannot have the stacktrace. Is there any way to have it?

I want to log fatal errors on a production server to help resolving bugs which occurs only in production. I know, xdebug on dev servers must be enough, but it is not. Maybe we can use xdebug with the minimum of options activated, or a stripped version of it to add stack trace to error log?

This code print error information if one error occur, even a timeout.

<?php
function shutdown()
{
    $a=error_get_last();
    if($a!==null) print_r($a);  
}
register_shutdown_function('shutdown');
like image 716
Cédric Girard Avatar asked Jan 24 '12 17:01

Cédric Girard


2 Answers

You won't get a stack trace on the fatal error because "FATAL" means script execution stops immediately, i.e. the trace can't be generated through the usual channels. So, if you have set a custom error handler to throw exceptions, it won't be invoked for fatal errors as per the PHP manual:

The following error types cannot be handled with a user defined function: E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING, and most of E_STRICT raised in the file where set_error_handler() is called.

The easiest way to get info about what happened in the fatal error (short of turning display_errors on, which isn't an option in production environments) is to manually build the error information yourself in the shutdown handler. Also, I wouldn't use xdebug on a production server ... it's for debugging your development environment and will add unnecessary overhead in a production environment.

So, modifying your shutdown handler you could do something like this:

function shutdown()
{
  if ( ! $err = error_get_last()) {
    return;
  }

  $fatals = array(
    E_USER_ERROR      => 'Fatal Error',
    E_ERROR           => 'Fatal Error',
    E_PARSE           => 'Parse Error', 
    E_CORE_ERROR      => 'Core Error',
    E_CORE_WARNING    => 'Core Warning',
    E_COMPILE_ERROR   => 'Compile Error',
    E_COMPILE_WARNING => 'Compile Warning'
  );

  if (isset($fatals[$err['type']])) {
    $msg = $fatals[$err['type']] . ': ' . $err['message'] . ' in ';
    $msg.= $err['file'] . ' on line ' . $err['line'];
    error_log($msg);
  }
}
like image 118
rdlowrey Avatar answered Sep 22 '22 08:09

rdlowrey


If you use HHVM you can set

hhvm.error_handling.call_user_handler_on_fatals = 1

in your php.ini to have it call the error handler (with a stack trace) on fatal errors. (more info)

Using a profiler might also be an option - e.g. use XHProf and calling xhprof_disable() in the fatal handler might get you something remotely resembling a stack trace. (No idea if this actually works.)

like image 42
Tgr Avatar answered Sep 23 '22 08:09

Tgr