Cómo usar logging.getLogger (__ name__) en múltiples módulos

Del manual de logging para Python 2.7 (mi énfasis):

Una buena convención para usar cuando se asignan nombres a los registradores es utilizar un registrador de nivel de módulo, en cada módulo que utiliza el registro, denominado de la siguiente manera:

logger = logging.getLogger(__name__)

Esto significa que los nombres del registrador rastrean la jerarquía del paquete / módulo , y es intuitivamente obvio donde los eventos se registran solo desde el nombre del registrador.

Suena como un buen consejo.

Ahora, el libro de cocina de logging proporciona un ejemplo para varios módulos, que utiliza nombres de registrador codificados en vez de la constante __name__ . En el “módulo principal” del ejemplo encontramos

logger = logging.getLogger('spam_application')

y en el “módulo auxiliar” nos encontramos con

module_logger = logging.getLogger('spam_application.auxiliary')

Copié este ejemplo literalmente en una carpeta de paquete con la siguiente estructura:

 cookbook-example |- __init__.py |- main_module.py |- auxiliary_module.py 

Esto se ejecuta sin problemas, produciendo la salida de registro esperada tanto del módulo principal como del módulo auxiliar, pero aquí está la cosa:

Si ahora sustituyo los nombres del registrador codificado por la constante __name__ , tal como lo recomienda el método de logging , el ejemplo del libro de cocina se descompone: solo obtengo mensajes de registro del módulo principal, pero nada del módulo auxiliar.

Debo estar perdiendo algo obvio. ¿Alguna idea de lo que estoy haciendo mal?

Nota:

Hay muchas preguntas similares y respuestas relacionadas, por ejemplo: 1 , 2 , 3 , 4 , 5 , 6 y muchas más. Sin embargo, ninguno de ellos parece abordar esta pregunta específica.

–Editar–

Aquí hay un ejemplo mínimo basado en el ejemplo del libro de cocina, con las cadenas de nombre explícitas reemplazadas por __name__ .

main_module.py

 import logging import auxiliary_module # create and configure main logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # create console handler with a higher log level handler = logging.StreamHandler() handler.setLevel(logging.DEBUG) # create formatter and add it to the handler formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') handler.setFormatter(formatter) # add the handler to the logger logger.addHandler(handler) logger.info('message from main module') auxiliary_module.some_function() 

auxiliar_module.py

 import logging # create logger module_logger = logging.getLogger(__name__) def some_function(): module_logger.info('message from auxiliary module') 

Como lo señaló @shmee en esta respuesta , la jerarquía del registrador debe definirse explícitamente en el nombre del registrador, utilizando la notación de puntos. Es decir, si el nombre del registrador en main_module.py es, por ejemplo, 'a' , entonces el nombre del registrador en auxiliary_module.py debe ser 'a.b' (no solo 'b' ), para que pueda heredar la configuración del registrador 'a' . Esto también se menciona en la documentación de getLogger() .

Sin embargo, esto debe ser __name__ automáticamente cuando se usa __name__ , como se indica en el logging cómo hacerlo :

Esto significa que los nombres del registrador rastrean la jerarquía del paquete / módulo, y es intuitivamente obvio donde los eventos se registran solo desde el nombre del registrador.

La cosa es que, para que esto funcione, necesitas usar __name__ de la manera correcta, y no lo hice.

El problema en mi ejemplo está en la organización de los archivos en la carpeta del paquete de cookbook-example :

Tanto el módulo principal como el módulo auxiliar se encuentran en el mismo nivel (es decir, en la misma carpeta). Entonces, como se explica aquí , el __name__ para el módulo principal será '__main__' (ya que es el script de nivel superior), y el __name__ para el módulo auxiliar será 'auxiliary_module' (es decir, el nombre del archivo), NO '__main__.auxiliary_module' .

Como resultado, el registrador en el módulo auxiliar será un hijo del registrador raíz, no un hijo del registrador '__main__' , y por lo tanto heredará la configuración del registrador raíz (que todavía tiene el nivel de registro predeterminado WARNING ) en lugar de La configuración especificada en el módulo principal.

Entonces, para hacer que el ejemplo funcione, tenemos varias opciones:

  1. Reemplace getLogger(__name__) en el módulo principal por getLogger() . Esto aplicará la configuración al registrador raíz y, por lo tanto, también al registrador del módulo auxiliar, como lo sugiere @shmee.

  2. Reemplace getLogger(__name__) en el módulo auxiliar por getLogger('__main__.' + __name__) . El resultado será equivalente al ejemplo original del libro de cocina (excepto que el registrador principal ahora se llama '__main__' lugar de 'spam_application' ).

El nombre de los madereros es lo que te falta. En el ejemplo, se crea un registrador llamado spam_application en el módulo principal. Luego, los manejadores y formateadores se crean y agregan a ese registrador.

En auxiliary_module registradores se crean con nombres que comienzan con spam_application . spam_application.auxiliary . Esto crea efectivamente una jerarquía de registradores que se propagan a sus respectivos padres a menos que se deshabiliten explícitamente. Esta jerarquía es spam_appliclation <- spam_application.auxiliary <- spam_application.auxiliary.Auxiliary o logger <- module_logger <- self.logger en el caso del ejemplo del libro de cocina.

Si reemplaza los nombres explícitos del registrador por __name__ , terminará teniendo un registrador configurado llamado __main__ en su módulo principal, que está configurado con manejadores, pero la denominación de sus registradores auxiliares no es de una manera que pueda crear una jerarquía, de ahí su los registradores auxiliares de módulo se propagan al registrador raíz implícito que no tiene controladores configurados.

Intente lo siguiente: cambie el método de inicio de su clase de la siguiente manera:

 def __init__(self): self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary') print self.logger.parent self.logger.info('creating an instance of Auxiliary') 

A continuación, ejecute su módulo principal una vez con

 self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary') 

y una vez con

 self.logger = logging.getLogger(__name__) 

La salida debería verse así:

  # with explicit logger name  # using __name__