Keep your logs with FirePHP

If you are a web developer, It’s not possible that you did not heard of Firebug. One of the greatest extensions written for Firefox! In the recent days I finally found some spare time to handle logging for my CMS framework Boop. I had Zend Framework and I decided to use Zend_Log which had a great feature like sending your logs to FirePHP, an extension for Firebug.


Zend_Log is really a clean and easy logger tool. How much complicated can it be anyway. But the way Zend handles it makes it a bit complicated. Lots of classes extending each other, you might find yourself lost while you walk around the code and classes. That’s not much of the case for Zend_Log hopefully. You have your Zend_Log and Zend_Log_Writer_*. Basically you add a writer to your logger class (you can add multiple writers too) and you start using them. You could wrap Zend_Log just as I did in case that you decide to use just another library instead of Zend and you will obtain something like this:

logger = new Zend_Log();
        if ($logEnabled) {
            $this->level = 6;
	    $channel = Zend_Wildfire_Channel_HttpHeaders::getInstance();
            $channel->setRequest(new Zend_Controller_Request_Http());
            $channel->setResponse(new Zend_Controller_Response_Http());
            $this->logger->addWriter(new Zend_Log_Writer_Firebug());
            ob_start(); //This might be optional if you have ob management too...
        } else {
            $this->level = 0;
        }
    }
   /**
     * The destructor function for the logger which will call flush or other things if you need to...
     */
    public function  __destruct() {
        $this->flush();
    }
   /**
     * It flushes the contents of the Firebug/FirePHP to the browser
     * 
     * @return void
     */
    public function flush() {
            Zend_Wildfire_Channel_HttpHeaders::getInstance()->flush();
            Zend_Wildfire_Channel_HttpHeaders::getInstance()->getResponse()->sendHeaders();
    }

    /**
     * Returns the instance of the logger
     *
     * @return Logger
     */
    public static function getInstance() {
        if (! self::$self instanceof Logger) {
            self::$self = new Logger();
        }
        return self::$self;
    }

    /**
     * Logs any event and writes to appropriate places
     *  according to the writers I have set
     *
     * @param string $str
     * @param integer $mod[optional]
     * @return void
     */
    public function log($str, $mod = Zend_Log::DEBUG) {
            if ($this->level >= $mod) { //Only log if the level allows it
                $this->logger->log($str, $mod);
            }
    }

    /**
     * Logs an info event
     *
     * @see log()
     *
     * @param string $str
     * @return void
     */
    public function info($str) {
        $this->log($str, Zend_Log::INFO);
    }
}
?>

You realize that there are some basic things here. We are creating a static self reference in the class for Singleton purpose. Than in our constructor we are creating Zend_Log object and set it in one of our wrapper class’ attribute. 


// Start output buffering
ob_start();
if ($this->level >= $mod) { //Only log if the level allows it
$this->logger->log($str, $mod);
//Flush the logs to the browser for firephp
Zend_Wildfire_Channel_HttpHeaders::getInstance()->flush();
Zend_Wildfire_Channel_HttpHeaders::getInstance()->getResponse()->sendHeaders();
}

[…]
ob_start()
[…]
//Flush the logs to the browser for firephp
Zend_Wildfire_Channel_HttpHeaders::getInstance()->flush();
Zend_Wildfire_Channel_HttpHeaders::getInstance()->getResponse()->sendHeaders();
[…]

You must get confused with this part. Why do we need these? The problem with FirePHP it does not directly communicate with your server side PHP code. And of course, that’s good for security purposes and lots of other things. So the logs you have created that you want to see on your FirePHP console, are sent in an other way than your source html. They are sent via this Wildfire channel. Once page loaded, FirePHP gathers all the information he finds sent to it by Wildfire and show it on the console. You may think of it as a “standardized” way for yet another ajax request

You must also be curious about the output buffer thing. Why do we need a buffer? The thing is in how the wildfire channel sends the data through ajax. It takes the current buffer and than add to the Zend_Controller_Response_Http which later this one will send the response to the browser and FirePHP wil handle it. That’s why you need to to set a reponse object to your wildfire channel.

Why I’m flushing the buffer right after I have written my log? Wouldn’t more resource friendly once I got all the logs and than flush it? The problem with this idea, if there are any output on the page, the flush function will get them too, and the response you will send will be unreadable for your FirePHP and you will see nothing. It took me hours to figure it out and finally I gave up on this.

The previous paragraph was completely wrong. Because of some other things, I guess I was unable to get the logs. It perfectly works with only one flush! I have updated the code part about this too.

You might think why? If that’s a “channel” it might be able to do this without Zend libraries. The reason is that, when you want to use this Zend_Log you should use it in a controller anyway. So if you are not using Zend’s MVC implementation you need a hack(!) like this.

Other than that, you will see that there is a check in the log(). This level is the level of the logs you are printing. You can set some logs with debug purposes or information or error or warning purposes. This level thing is just that. When you lower your level less logs will be seen as debug’s level is 7 and error’s level is 3. This is a thing I have done to minimize the logs seen on the FirePHP and help me understand better on what’s going on. If I want, I can see the full log too.

I have also wrapped the log() with info() to easily write on the code without writing much characters. I used my own log() to log them but you can always use the Zend_Log’s native functions like info() and err(). It’s just a thing I have done just like my wrapper.

It was fun to work with Zend Framework for this time. But it’s too big and too complicated and I hated the part where I had to create the Controller objects. I was not using Zend’s MVC implementation and just for FirePHP logging I have added something like this. It doesn’t look ok for me. You can find additional server side FirePHP implementation here and of course you can use FirePHP Core too.