Использование log4php в статическом контексте
Вопрос
В настоящее время я перехожу с нашего собственного решения для ведения журналов на log4php.
В нашем проекте мы используем множество классов только со статическими методами.Документация определяет базовый вариант использования, например:
class MyClass {
private $logger;
public function __construct() {
$this->logger = Logger::getLogger(__CLASS__);
$this->logger->debug('currently in constructor');
}
}
Но я не могу это использовать, потому что мне нужно $logger
быть доступным и в статическом контексте.Изготовление $logger
static тоже не помогает, потому что конструктор моего класса никогда не вызывается (поскольку все его члены статичны).
В документации говорится, что тогда мне нужно использовать статический инициализатор для этого члена.Но тогда мне пришлось бы не забыть вызвать это для всех классов, которые я использую.И это кажется слишком подверженным ошибкам.
Итак, я придумал следующее:
class Foo {
private static $logger = null;
private static function logger() {
if( null == self::$logger ) self::$logger = Logger::getLogger( __CLASS__ );
return self::$logger;
}
public static function bar() {
self::logger()->debug( "test" );
}
}
Foo::bar();
Но это тоже кажется слишком большими накладными расходами.Итак, есть предложения?
Решение
Я придумал одно решение, которое работает довольно хорошо, но требует $logger
быть публичным.
class Foo {
public static $logger = null;
public static function bar() {
self::$logger->debug( "test" );
}
}
$loggerName = "logger";
// Iterate over all declared classes
$classes = get_declared_classes();
foreach( $classes as $class ) {
$reflection = new ReflectionClass( $class );
// If the class is internally defined by PHP or has no property called "logger", skip it.
if( $reflection->isInternal() || !$reflection->hasProperty( $loggerName ) ) continue;
// Get information regarding the "logger" property of this class.
$property = new ReflectionProperty( $class, $loggerName );
// If the "logger" property is not static or not public, then it is not the one we are interested in. Skip this class.
if( !$property->isStatic() || !$property->isPublic() ) continue;
// Initialize the logger for this class.
$reflection->setStaticPropertyValue( $loggerName, Logger::getLogger( $class ) );
}
Это мне нужно только определить $logger
свойство один раз для каждого класса и один раз запустить мой код инициализации (я думаю, после require_once
раздел точки входа моего приложения).
Влияние этого кода на производительность незначительно, тем более что он запускается только один раз (по сравнению с моим первоначальным решением).Вот что я измерил внутри виртуальной машины VirtualBox на процессоре Intel Core2 Q9450 с частотой 2,66 ГГц:
10000 iterations for 157 classes completed in 2.6794s. Average per iteration: 0.00026794s