Cutting Use of Zend_Log in Half
As part of the framework we use at work, we borrow what we feel are the best components out there and logging is a key part of that. Logging should be simple to setup, easy to use and should minimize work on the developer. After all, you are going to do a lot of logging, right? We use Zend_Log exclusively and while I like it when coupled with Zend_Registry you’ve always got the following two lines of code:
// at the beginning of our application (in a singleton or in
// index.php at the very least)
$logger = new Zend_Log($writer);
$registry->set(?logger? , $logger);
// Every time you log you have these two lines.
$logger = Zend_Registry::get(?logger?);
$logger->log($errorMessage,1);
Not bad, not bad at all. In fact Zend_Registry is a handy little class. But why call Zend_Registry::get() explicitly over and over if you don’t have to? I mean wouldn’t it be nice if you could simply do:
MyLog::log('SomeMessage', Zend_Log::DEBUG);
That’s right, let’s log things statically and let all the magic happen behind the scenes. We’ll do just that with a small bit of code and best of all you won’t even need Zend_Registry (again, great class but why use it if you don’t need it). How? Well you need to use something that acts like Zend_Registry but allows us to log messages statically. To do that we’ll create a new class called MyLog and the first thing is the allow the application (or any plugins) to register their loggers (click read more):
public static function registerLogger($loggerName, $zendWriter = '',
$isDefault = false)
{
if (!isset(self::$instances[$loggerName])) {
self::$instances[$loggerName] = new Zend_Log($zendWriter);
}
// If we weren't told this is the default logger yet none exists then force it
if (!self::getDefaultLoggerName() AND !$isDefault) $isDefault = true;
if ($isDefault) {
if (isset(self::$defaultLogger)) {
throw new Exception('Default logger is already defined');
}
self::$defaultLogger = $loggerName;
}
}
The $loggerName is a logical name given to the logger you are registering. This must be unique. $zendWriter is one of the many Zend_Writer children you can use. Finally $isDefault indicates if the logger given will serve as the default logger. This will allow us to use shorter notation in our application. Ok, once you have this you can register all your loggers:
// You only ever do these once
MyLog::registerLogger('kernel', new Zend_Log_Writer_Stream(getOption('logFile')), true);
MyLog::registerLogger('somePlugin', new Zend_Log_Writer_Stream(getOption('path_logs') .
'SomePlugin.log'));
MyLog::registerLogger('propel', new Zend_Log_Writer_Stream(getOption('path_logs') .
'Propel.log'));
This then allow us to log a message with a single line with any of the following
// Log to the default 'kernel' logger with only an informational message.
// Default log level is Zend_Log::INFO
MyLog::log('Kernel starting up');
// Log to plugin's log
MyLog::log('Plugin failed to initialize', Zend_Log::ERR, 'somePlugin');
While the above is nice we can improve by providing simple pass through functions for each Zend_Log log level:
// Same log to the kernel from above but shorter
MyLog::info('Kernel starting up');
// Log to plugin log like above but shorter
MyLog::err('Plugin failed to initialize', 'somePlugin');
If you like all this logging goodness you simply need to grab this file along with Zend_Log and you are on your way. Please note the requires at the beginning of the class I’m providing. It is using a method called getOption() which you will either need to implement or replace it with relative pathing. We do it this way because we’ve optimized our application to use APC which likes a) require instead of require_once and b) absolute paths over relative paths.
I still don’t love it, but I think I’m instead going to use:
Zend_Registry::get(‘logger’)->info(‘my log message’);
for now.