Usando log4php en un contexto estático
Pregunta
Actualmente estoy en el proceso de pasar de nuestra propia solución de registro patentada a log4php.
Usamos muchas clases con solo métodos estáticos en nuestro proyecto.La documentación define el caso de uso básico como:
class MyClass {
private $logger;
public function __construct() {
$this->logger = Logger::getLogger(__CLASS__);
$this->logger->debug('currently in constructor');
}
}
Pero no puedo usar eso, porque necesito $logger
estar disponible también en un contexto estático.Haciendo $logger
static tampoco ayuda, porque nunca se llama al constructor de mi clase (ya que todos sus miembros son estáticos).
La documentación me dice que use un inicializador estático para ese miembro en ese momento.Pero luego tendría que recordar llamar así para todas las clases que uso.Y eso parece demasiado propenso a errores.
Entonces se me ocurrió esto:
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();
Pero eso también parece demasiado.Entonces, ¿alguna sugerencia?
Solución
Se me ocurrió una solución que funciona bastante bien pero requiere $logger
ser público.
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 ) );
}
Esto solo me queda definir el $logger
propiedad una vez por clase y ejecutar mi código de inicialización una vez (supongo que después del require_once
sección del punto de entrada de mi solicitud).
El impacto en el rendimiento de ese código es insignificante, especialmente porque solo se ejecuta una vez (en comparación con mi solución inicial).Esto es lo que medí dentro de una máquina virtual VirtualBox en un Intel Core2 Q9450 a 2,66 GHz:
10000 iterations for 157 classes completed in 2.6794s. Average per iteration: 0.00026794s