Python: registro personalizado en todos los módulos

Tarea

Tengo una colección de scripts y me gustaría que produjeran mensajes de registro unificados con modificaciones mínimas en los módulos que registran los mensajes reales.

He escrito un pequeño módulo ‘custom_logger’ al que planeo llamar desde la aplicación principal una vez, que devuelva un registrador, que luego seguiré usando.

Los submódulos que estaría importando en la aplicación solo deberían (o más bien deseo que lo hagan)

  • solo debe “importar el registro como registro”, de modo que no se requiera nada específico de mi sitio para que solo se ejecuten si alguien más lo encuentra útil.
  • solo debe registrar los mensajes con log.info/error(‘message ‘) sin agregarles nada específico del sitio
  • debe usar el registrador ‘root’ ya configurado con todo su formato y manejadores, y no afectar la configuración del registrador raíz

* custom_logger.py *

import logging import logging.handlers import os import sys def getLogger(name='root', loglevel='INFO'): logger = logging.getLogger(name) # if logger 'name' already exists, return it to avoid logging duplicate # messages by attaching multiple handlers of the same type if logger.handlers: return logger # if logger 'name' does not already exist, create it and attach handlers else: # set logLevel to loglevel or to INFO if requested level is incorrect loglevel = getattr(logging, loglevel.upper(), logging.INFO) logger.setLevel(loglevel) fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s' fmt_date = '%Y-%m-%dT%T%Z' formatter = logging.Formatter(fmt, fmt_date) handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) if logger.name == 'root': logger.warning('Running: %s %s', os.path.basename(sys.argv[0]), ' '.join(sys.argv[1:])) return logger 

Luego viene el submódulo que contiene algunos mensajes de prueba con ejemplos de lo que funciona y lo que no.

submódulo.py

 import sys import custom_logger import logging class SubClass(object): def __init__(self): # NOK (no idea why since by default (no name parameter), it should return the root logger) #log = logging.getLogger() #log.info('message from SubClass / __init__') # OK (works as expected) #log = logging.getLogger('root') #log.info('message from SubClass / __init__') # OK (works as expected) log = custom_logger.getLogger('root') log.info('message from SubClass / __init__') def SomeMethod(self): # OK but I'd have to define `log` for every method, which is unacceptable # Please see question below all code snippets log = custom_logger.getLogger('root') log.info('message from SubClass / SomeMethod') 

Y la aplicación principal: app.py Nada especial aquí:

 #!/usr/bin/python import custom_logger import submodule log = custom_logger.getLogger('root', loglevel='DEBUG') log.debug('debug message') log.info('info message') log.warning('warning message') log.error('error message') a = submodule.SubClass() # this should produce a log message a.SomeMethod() # so should this 

Salida que estoy buscando y que estoy obteniendo, solo de una manera extremadamente fea:

 % ./app.py 2013-04-08T03:07:46BST custom_logger.py WARNING : Running: app.py 2013-04-08T03:07:46BST app.py DEBUG : debug message 2013-04-08T03:07:46BST app.py INFO : info message 2013-04-08T03:07:46BST app.py WARNING : warning message 2013-04-08T03:07:46BST app.py ERROR : error message 2013-04-08T03:07:46BST submodule.py INFO : message from SubClass / __init__ 2013-04-08T03:07:46BST submodule.py INFO : message from SubClass / SomeMethod 

Quiero poder definir un registrador en app.py y luego, en los submódulos, solo uso la biblioteca de registro estándar de Python para hacer uso de un registrador ya configurado en app.py.

También, una solución fea: si coloco el siguiente código después de las importaciones en submodule.py:

 log = custom_logger.getLogger('root') 

se ejecutará antes de que mi registrador se configure en app.py, haciendo el submódulo de manera efectiva, no mi registro de configuración de la aplicación.

Otra solución que consideré: dentro del constuctor de la clase SubClass, podría definir

self.log = custom_logger.getLogger('root')

y luego use self.log.error (‘algún error’). Debe haber una forma más agradable: si puede sugerir algo útil o señalar dónde entendí mal la documentación, ¡le estaría muy agradecido!

PD. Me he pasado un buen rato leyendo el howto de registro de Python (básico y avanzado) y el libro de cocina, así que si me he perdido algo útil, indíquelo.

¡Gracias!

Si desea cambiar el registrador raíz, puede usar getLogger() todas partes, sin argumentos.

Con respecto a la configuración de la instancia solo en el módulo principal, puede crear una instancia de su registrador, agregar su propio controlador y usarlo en todos los otros submódulos (como lo hice a continuación).

Creé una clase que hereda el StreamHandler en custom_logger.py :

 class MyHandler(logging.StreamHandler): def __init__(self): logging.StreamHandler.__init__(self) fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s' fmt_date = '%Y-%m-%dT%T%Z' formatter = logging.Formatter(fmt, fmt_date) self.setFormatter(formatter) 

Luego, en submodule.py , puse el getLogger después de las importaciones y lo comenté en los métodos:

 import sys import logging log = logging.getLogger('root') class SubClass(object): def __init__(self): log.info('message from SubClass / __init__') def SomeMethod(self): log.info('message from SubClass / SomeMethod') 

Luego, en app.py creé una instancia de Logger (que será la misma en todos los módulos) y agregué mi controlador, que formatea la salida:

 #!/usr/bin/python import logging import custom_logger import submodule log = logging.getLogger('root') log.setLevel('DEBUG') log.addHandler(custom_logger.MyHandler()) log.debug('debug message') log.info('info message') log.warning('warning message') log.error('error message') a = submodule.SubClass() # this should produce a log message a.SomeMethod() # so should this 

Salida:

 ./app.py 2013-04-08T15:20:05EEST app.py DEBUG : debug message 2013-04-08T15:20:05EEST app.py INFO : info message 2013-04-08T15:20:05EEST app.py WARNING : warning message 2013-04-08T15:20:05EEST app.py ERROR : error message 2013-04-08T15:20:05EEST submodule.py INFO : message from SubClass / __init__ 2013-04-08T15:20:05EEST submodule.py INFO : message from SubClass / SomeMethod