Pablo Viquez Blog

Mi vida y cosas relacionadas

Skip to: Content | Sidebar | Footer

Zend_Log, FirePHP and Zend_Application. How to

25 November, 2009 (16:23) | debug, PHP, tech | By: Pablo Viquez

I wanted to enable logging of exceptions to my PHP log file and also display them using FireBug.

On a standard setup of Zend Framework, the Zend error handler plugin (Zend_Controller_Plugin_ErrorHandler) enable by default and it was designed for:

  • Errors due to missing controllers or actions
  • Errors occurring within action controllers

I wanted to keep using the error handler plugin and didn’t want to log the exceptions by using: Zend_Controller_Front::throwExceptions() or Zend_Controller_Response_Abstract::renderExceptions() or any other way since eventually might get complicated, besides, I think that the error handler plugin works fine and if it gets updated in the future, I won’t have to do a major refactor.

So this is how I did it:

Create the Zend Log Object

Using Zend_Application can be very helpful here, because you can init the logger and automatically store the object in the registry, making available to the controllers later on.

Summary, if you want an object created on the boostrap class, to be available on the controllers later, you need to return it from the init method, in this case, we’re initializing the Zend_Log, so we need to return the object.

<?php
class App_Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{

    /**
     * Initializes the logging on the application
     * 
     * We're using 2 ways for logging exceptions:
     * 1. PHP Error log file.
     * 2. Firebug for Development environments only.
     *
     * @return Zend_Log
     */
    protected function _initLogger()
    {
        // Create the Zend_Log object
        $logger = new Zend_Log();

        // Create the resource for the error log file
        // if you dont want to use the error suppressor "@"
        // you can always use: is_writable
        // http://php.net/manual/function.is-writable.php
        $stream = @fopen(ini_get('error_log'), 'a');
        if ($stream) {
            $stdWritter = new Zend_Log_Writer_Stream($stream);
            $stdWritter->addFilter(Zend_Log::DEBUG);
            $stdWritter->addFilter(Zend_Log::INFO);
            $logger->addWriter($stdWritter);
        }

        // Create the Firebug writter - Development only
        if ($this->_application->getEnvironment() != 'production') {
            // Init firebug logging
            $fbWriter = new Zend_Log_Writer_Firebug();
            $logger->addWriter($fbWriter);
        }

        return $logger;
    }

I have now the log object but I’m not logging anything yet, so I need to implement the logging part.

Log the Exception

For this part, I thought in 2 ways to do it:

  1. Extend the error handler plugin and log errors there.
  2. Log the errors on the error controller.

I choose the second one for several reasons:

  1. Easier.
  2. If the error handler changes somehow, you dont have to change your code.
  3. Extending the error plugin, might lead to repeated code and eventually deprecated code.

By default, Zend_Controller_Plugin_ErrorHandler will forward to ErrorController::errorAction() in the default module.

Now in order to retrieve a resource created in the bootstrap we need to access the bootstrap registered in the front controller, and from there we can get our logger:

// The method _initLogger on the class App_Bootstrap
// returns a Zend_Log object that is available
// on the Zend_Registry. To retrieve it we need to request the
// bootstrap from the Front controller and then get the logger.
$boot = $this->getInvokeArg('bootstrap');
if ($boot->hasResource('logger')) {
    $log_object = $boot->getResource('logger');
}

So using the Zend Framework default error controller (the one provided on the reference), with the logging will be something like this:

class ErrorController extends Zend_Controller_Action
{
    /**
     * Error action. This method is called by
     * Zend_Controller_Plugin_ErrorHandler
     *
     * @see Zend_Controller_Plugin_ErrorHandler
     * @return void
     */
    public function errorAction()
    {
        $errors = $this->_getParam('error_handler');

        // Log the Error.
        $boot = $this->getInvokeArg('bootstrap');
        if ($boot->hasResource('logger')) {
            $boot->getResource('logger')->err($errors->exception);
        }

        // Default error handling...
        switch ($errors->type) {
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_CONTROLLER:
            case Zend_Controller_Plugin_ErrorHandler::EXCEPTION_NO_ACTION:

                // 404 error -- controller or action not found
                $this->getResponse()->setHttpResponseCode(404);
                $this->view->message = 'Page not found';
                break;
            default:
                // application error
                $this->getResponse()->setHttpResponseCode(500);
                $this->view->message = 'Application error';
                break;
        }

        $this->view->exception = $errors->exception;
        $this->view->request   = $errors->request;
    }
}

Result

The final result looks like this:

Firefox with Firebug enabled:

Firefox Screenshot with Firebug enabled

PHP error log file:

[pviquez /usr/home/pviquez]$ cat php_error.log
2009-11-25T15:23:19-06:00 ERR (3): exception 'Zend_Exception' with message 'The logging is working!' in /usr/home/pviquez/app/deploy/application/modules/admin/controllers/IndexController.php:33
Stack trace:
#0 /usr/home/pviquez/app/deploy/library/Zend/Controller/Action.php(513): Admin_IndexController->indexAction()
#1 /usr/home/pviquez/app/deploy/library/Zend/Controller/Dispatcher/Standard.php(289): Zend_Controller_Action->dispatch('indexAction')
#2 /usr/home/pviquez/app/deploy/library/Zend/Controller/Front.php(946): Zend_Controller_Dispatcher_Standard->dispatch(Object(Zend_Controller_Request_Http), Object(Zend_Controller_Response_Http))
#3 /usr/home/pviquez/app/deploy/library/Zend/Application/Bootstrap/Bootstrap.php(77): Zend_Controller_Front->dispatch()
#4 /usr/home/pviquez/app/deploy/library/Zend/Application.php(346): Zend_Application_Bootstrap_Bootstrap->run()
#5 /usr/home/pviquez/app/deploy/public/index.php(23): Zend_Application->run()
#6 {main}

References