Wednesday 11 April 2007

Developing a simple Zend_Log_Filter

In the new Zend_Log component of the Zend Framework Beta 0.9.2 release there are currently two log Filter available. The Zend_Log_Filter_Priority object can be used to filter out log Events having a log Priority greater than a specified log Priority value. The other Filter named Zend_Log_Filter_Message might be used to filter out specific log messages matching a Regular expression. The realization of a own Filter can be achieved quick and easy by implementing the Zend_Log_Filter_Interface interface.

The following code snippet shows a custom Filter for filtering out log Events lower to a specific log Priority value. This emerged from an use case that in an audit log the basic log Priorities are not required and shouldn't be allowed due to possible developer mistakes. The custom Filter has to be located in the applications custom library directory i.e. recordshelf/libary/Recordshelf/Log/Filter/Priority.

<?php   

require_once 'Zend/Log/Filter/Interface.php';

class Recordshelf_Log_Filter_Priority_Minimum implements Zend_Log_Filter_Interface
{
/**
* @var integer
*/
protected $_priority;

/**
* Filter out any log messages lower than $priority.
*
* @param integer $priority Minimum priority to pass through the filter
* @throws Zend_Log_Exception
*/
public function __construct($priority)
{
if (! is_integer($priority)) {
throw new Zend_Log_Exception('Priority must be an integer');
}

$this->_priority = $priority;
}

/**
* Returns TRUE to accept the message, FALSE to block it.
*
* @param array $event event data
* @return boolean accepted?
*/
public function accept($event)
{
return $event['priority'] >= $this->_priority;
}

}
I admit this isn't quite a hard one, but it shows the stepz needed to implement a custom filter. As you can the see the minimum conditional for the filter is set via the constructor and is validated through the accept method.

The next code snippet shows how the filter is added to the audit logger and configured to only let pass log Events beyond the log Priority audit. Currently there is no method like getPriorityNumber('audit') in Zend_Log available to retrieve the Priority number of a custom log Priority.
<?php 

$auditWriter = new Zend_Log_Writer_Stream('/path/to/auditfile');

/* Use a customized Formatting and add custom log Events */
$formatter = new Zend_Log_Formatter_Xml('audit', array('msg' => 'message',
'level' => 'priorityName',
'ip-visitor' => 'visitorIp',
'host-visitor' => 'visitorHost'));
$auditWriter->setFormatter($formatter);
$auditLogger = new Zend_Log($auditWriter);

/* Add a custom log Priority to the audit logger */
$auditLogger->addPriority('audit', 8);

/* Add some custom log Events to the audit logger */
$auditLogger->setEventItem('visitorIp', $_SERVER['REMOTE_ADDR']);
$auditLogger->setEventItem('visitorHost', $_SERVER['REMOTE_HOST']);

/* Add the custom Filter for all following log Events */
$filter = new Recordshelf_Log_Filter_Priority_Minimum(8);
$auditLogger->addFilter($filter);

/* Gets blocked by filter */
$auditLogger->info('Informational message');

/* Passes filter and gets logged */
$auditLogger->audit('Auditional message');

?>

5 comments:

Anonymous said...

Great job! Today was the first day I was trying out the new 0.9.2 and was taking some time trying to figure out how to do what you just so elegantly described in a few lines. Thanks.

Anonymous said...

By the way that was my comment earlier, and for some reason I seem unable to login to my google account. I wonder if it is my gmail username I need to use or if I need to turn on or sign up for some additional service. No matter.

I have a quick question. I noticed on another post that you are initializing the loggers and using the Registry to store them and access them later within the application.

What about an approach where you create a class like say App_Logger with static methods to invoke these functions? Would there be any benefits to that? The only thing I can think of is simplicity, since I would not need to do a Registry::get inside each function that I need logging in (which is pretty much all of them). Or would the discussions about hard linkages in Zend_Log and other things which honestly went over my head apply here?

I'd like to hear your thoughts on this.

Thanks, and great site! Lot of information here.

Raphael Stolt said...

I guess the simplest solution to avoid registry 'round trips' inside every method, would be to assign the needed logger instance to a class property at object intialisation and than use this instance for logging in the methods of the object.

Anonymous said...

What about the option of having a static logger class within the application? Like App_Logger, I guess pretty much creating a static wrapper which would function much the same as Zend_Log did before.

What are your thoughts on that?

Raphael Stolt said...

Sure you can add a logger class that mimics the one of the former Zend Framework version and goes beyond it. What I like most on the new logger class is the 'dynamic' approach of adding custom log levels and messages, but I have no further insights on how this would be achieved within a custom static logger class. Maybe you leave your insights on the road to your implementation.