CWebApplication / register_shutdown_function / logging

I’m working a lot with ‘distributed’ applications and it’s often hard to ‘debug’.

The problem is, that php-errors will not be logged.

So please add a register_shutdown_function to the CWebApplication that will log these errors too, maybe extra configurable or always if debug-mode is on.

Something like this:




  register_shutdown_function('logPhpError');




  /**

 * Log all PHP errors

 */

function logPhpError()

{

	if(is_null($e = error_get_last()) === false)

	{

		$message = sprintf('Error %s: %s in file "%s" on line %d',$e['type'],$e['message'],$e['file'],$e['line']);

		Yii::log($message,'error','PHP');		

	}

}




I think this can be done by attaching to CApplication::onError(). Did you try this?

Registering onError is not enough for PHP errors. When the script is aborted because a PHP error, the onError will not be executed.

But adding the code below to the constructor of the CApplication.php will really log all errors.





abstract class CApplication extends CModule

{


        public $logErrorsOnShutDown; //add a config var like this


        public function __construct($config=null)

	{

	    

               Yii::setApplication($this);


		// set basePath at early as possible to avoid trouble

		if(is_string($config))

			$config=require($config);

		....

                 if ($this->logErrorsOnShutDown)  //<---- configurable

             register_shutdown_function(array($this,'logErrorsOnShutDown')); 

               	...

	}




  /**

 * Added new method

 */

public function logErrorsOnShutDown()

{

        if(is_null($e = error_get_last()) === false)

        {

                $message = sprintf('Error %s: %s in file "%s" on line %d',$e['type'],$e['message'],$e['file'],$e['line']);

                Yii::log($message,'error','PHP');    

                Yii::app()->end(); //important           

        }

}




My alternative in the moment is adding the code from my first topic message to every index.php.

But it would be nice to have it integrated in the Yii application code.

Hi all

I also think about logging fatal errors.

My suggestion is based on Joblo’s one, but with little differences:

  1. extend CWebApplication instead of CModule

  2. put register_shutdown_function in init() method instead of __construct

  3. raise onEndRequest event instead of yii::app()->end() as it does not write logs if app->end() already called (yii 1.1.9)




class WebApplication extends CWebApplication {

    

    /**

    * initialization

    * 

    */

    protected function init()

    {

        register_shutdown_function(array($this, 'onShutdownHandler'));

        parent::init();    

    }


    /**

     * shutdown handler

     * @return void

     *

     */


    public function onShutdownHandler()

    {   

        if($e = error_get_last()) {

            $message = sprintf('Error %s: %s in file "%s" on line %d',$e['type'],$e['message'],$e['file'],$e['line']);

            Yii::log($message, 'error', 'php');

            $this->raiseEvent('onEndRequest', new CEvent($this)); 

            // yii::app()->end(); not suitable as app->ended may already be true, and logs do not flushed to file. yii 1.1.9

        }


    }

}



if you have your own suggestions or remarks to this topic please reply…

I developed a solution similar to this because I encountered a completely blank / white screen in Yii. Research online kept giving the answer as being to set php.ini’s DISPLAY_ERRORS - that was wrong because the reason for the blank screen is YiiBase is suppressing the errors using the ‘@’ symbol.

It was painful enough to diagnose, even though the cause was a simple syntax error, that I decided I didn’t want to deal with it again. Now on deciding to document my approach I came across this thread.

My approach has a little more finesses I think, because it only checks for errors that the PHP docs say cannot be caught by custom error handlers. Any other error codes we assume have been caught (or could be caught) by other mechanisms so we can ignore them.

Same as the last post, I call this from the WebApplication’s init method. I avoid the whole issue about how to terminate the program with the correct logging still occurring, by leaving it to the other shutdown handlers. This will work fine if the error occurs during normal ‘run’ processing, but not otherwise - in which case turning YII_DEBUG to true will always reveal the problem.




public function onShutdown()

 {

    /*

       According to PHP description of set_error_handler:

          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

          (http://www.php.net/manual/en/function.set-error-handler.php)

       So the following checks to see if one of the above errors has 

       occurred, since they can't have been caught by the Yii error handler

    */

    if (($error = error_get_last()) !== null 

        &&

        in_array($error['type'], array (E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING))

       ) {

        $log="Unhandled PHP error code {$error['type']} at shutdown: {$error['message']} ({$error['file']}:{$error['line']})\n";

        if(isset($_SERVER['REQUEST_URI'])) {

            $log.='REQUEST_URI='.$_SERVER['REQUEST_URI'];

        }

        Yii::log($log,CLogger::LEVEL_ERROR,'php');

        if (YII_DEBUG) {

            Yii::app()->displayError($error['type'], $error['message'], $error['file'], $error['line']);

        }


        /*

            Note we don't want to die here - we want to allow shutdown to continue, otherwise

           (for example) messages won't get logged / distributed. However if the error gets detected 

           before other shutdown handlers have been registsred, or after they've run, then all bets are 

           off - in which case YII_DEBUG being set to true will catch it.

        */

    }

 }