2011-06-13 10 views
34

Tengo un fragmento de código que está inicializando un registrador como se muestra a continuación.el registro Python garantiza que se agregue un controlador solo una vez

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 

Desafortunadamente este código se llama varias veces, ¿hay alguna manera de comprobar para ver si el controlador ya existe - yo preferiría para implementar esto sin tener que utilizar un Singleton.

EDIT: Lo sentimos, se olvidó de mencionar esto está en Python 2.5 - vivas, Richard

+0

creo que debería volver a evaluar las respuestas, porque la respuesta de mouad (en el momento de escribir esto) pasa por alto el hecho de que varias llamadas a un método devolver el mismo objeto registrador * CAN * agregar controladores duplicados. Narayan hace un buen trabajo al explicar esto. –

Respuesta

21

Como @offbyone comenta, es posible agregar manejadores redundantes a la misma instancia del registrador. Los python docs for logging decir-

"Múltiples llamadas a getLogger() con el mismo nombre, se devolverá una referencia al mismo objeto registrador."

Así que no tenemos que preocuparnos por hacer que la implementación sea única, como ya lo es.

Desafortunadamente lo mismo es no verdadero para los manejadores asociados con la misma instancia del registrador. Hay puede ser manejadores duplicados adjuntos.

Ejemplo-

  1. Copia este código y guardarlo en main.py

    import logging 
    print 'inside main.py', 
    print '-'*50 
    def logger(): 
    
         print 'initializing logger....' 
         logPath = '.' 
         fileName = 'temp' 
    
         # configure log formatter 
         logFormatter = logging.Formatter("%(asctime)s [%(filename)s] [%(funcName)s] [%(levelname)s] [%(lineno)d] %(message)s") 
    
         # configure file handler 
         fileHandler = logging.FileHandler("{0}/{1}.log".format(logPath, fileName)) 
         fileHandler.setFormatter(logFormatter) 
    
         # configure stream handler 
         consoleHandler = logging.StreamHandler() 
         consoleHandler.setFormatter(logFormatter) 
    
         # get the logger instance 
         logger = logging.getLogger(__name__) 
    
         # set the logging level 
         logger.setLevel(logging.DEBUG) 
    
         print 'adding handlers- ' 
    
         #if not len(logger.handlers): 
         logger.addHandler(fileHandler) 
         logger.addHandler(consoleHandler) 
    
         print 'logger initialized....\n' 
         print 'associated handlers - ', len(logger.handlers) 
         for handler in logger.handlers: 
          print handler 
         print 
         return logger 
    
    main_logger = logger() 
    main_logger.info('utilizing main.py logger.') 
    print 'exiting main.py', 
    print '-'*50 
    
  2. y el código siguiente en sub.py

    print 'inside sub.py', 
    print '-'*50 
    print 'importing main.py' 
    import main 
    print 'imported main.py' 
    import logging 
    print 'getting logger instance in sub' 
    sub_logger = main.logger() 
    print 'got logger instance in sub' 
    sub_logger.info("utilizing sub_logger") 
    print 'exiting sub.py', 
    print '-'*50 
    
  3. Ejecutar Sub.PY

    [email protected]:~/code/so$ python sub.py 
    inside sub.py -------------------------------------------------- 
    importing main.py 
    inside main.py -------------------------------------------------- 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 2 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    
    2015-08-04 07:41:01,824 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
    exiting main.py -------------------------------------------------- 
    imported main.py 
    getting logger instance in sub 
    initializing logger.... 
    adding handlers- 
    logger initialized.... 
    
    associated handlers - 4 # <===== 4 handlers (duplicates added) 
    <logging.FileHandler object at 0x7f7158740c90> 
    <logging.StreamHandler object at 0x7f7158710b10> 
    <logging.FileHandler object at 0x7f7158710bd0> 
    <logging.StreamHandler object at 0x7f7158710c10> 
    
    got logger instance in sub 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    2015-08-04 07:41:01,824 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
    exiting sub.py -------------------------------------------------- 
    

Por lo tanto múltiples llamadas al método que regresan el mismo registrador añaden manipuladores duplicados.

Ahora, para su pregunta-

¿Hay alguna manera puedo comprobar para ver si el controlador ya existe

Sí, hay iS-

logger.handlers devuelve una lista de todos los controladores asociados con el dado logger.

Antes de añadir controladores a una instancia del registrador, asegúrese de no agregar controladores duplicados En main.py, simplemente no-comentar la línea que dice if not len(logger.handlers): y sangría a las dos líneas siguientes properly-

if not len(logger.handlers): 
    logger.addHandler(fileHandler) 
    logger.addHandler(consoleHandler) 
Ahora, de nuevo

ejecutar sub.py

[email protected]:~/code/so$ python sub.py 
inside sub.py -------------------------------------------------- 
importing main.py 
inside main.py -------------------------------------------------- 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

2015-08-04 08:14:45,620 [main.py] [<module>] [INFO] [41] utilizing main.py logger. 
exiting main.py -------------------------------------------------- 
imported main.py 
getting logger instance in sub 
initializing logger.... 
adding handlers- 
logger initialized.... 

associated handlers - 2 # <===== Still 2 handlers (no duplicates) 
<logging.FileHandler object at 0x7fd67a891c90> 
<logging.StreamHandler object at 0x7fd67a862b10> 

got logger instance in sub 
2015-08-04 08:14:45,620 [sub.py] [<module>] [INFO] [10] utilizing sub_logger 
exiting sub.py -------------------------------------------------- 

Además, si desea limitar el tipo de manipuladores que se añadirán a la instancia registrador, se puede hacer algo como esto:

print 'adding handlers- ' 
    # allows to add only one instance of file handler and stream handler 
    if len(logger.handlers) > 0: 
     print 'making sure we do not add duplicate handlers' 
     for handler in logger.handlers: 
       # add the handlers to the logger 
       # makes sure no duplicate handlers are added 

       if not isinstance(handler, logging.FileHandler) and not isinstance(handler, logging.StreamHandler): 
        logger.addHandler(fileHandler) 
        print 'added file handler' 
        logger.addHandler(consoleHandler) 
        print 'added stream handler' 
    else: 
     logger.addHandler(fileHandler) 
     logger.addHandler(consoleHandler) 
     print 'added handlers for the first time' 

Espero que ayude!

Editar:

Por desgracia, la misma no es cierto para los manipuladores asociados con la misma instancia del registrador. Hay puede ser duplicados manejadores adjuntos.

Resulta que esa declaración anterior no es del todo cierto.

Supongamos que hemos creado y configurado un registrador llamado 'main_logger' en el módulo principal (que simplemente configura el registrador, no devuelve nada).

# get the logger instance 
logger = logging.getLogger("main_logger") 
# configuration follows 
... 

Ahora en un sub-módulo, si creamos un registrador de niño después de la jerarquía de nombres 'main_logger.sub_module_logger', no es necesario configurarlo en el sub-módulo. Solo la creación del registrador siguiendo la jerarquía de nombres es suficiente.

# get the logger instance 
logger = logging.getLogger("main_logger.sub_module_logger") 
# no configuration needed 
# it inherits the configuration from the parent logger 
... 

Y no agregará el controlador duplicado también.

Referencia-Using logging in multiple modules

+1

Esta es definitivamente la mejor respuesta. Desafortunadamente, la respuesta de mouad es miope, ya que se pierde la posibilidad de manipuladores duplicados (no únicos) para una sola instancia de un registrador (el singleton). Tu respuesta me ayudó a resolver mi problema. –

+0

"logger.handlers devuelve una lista de todos los controladores asociados con el registrador dado" ... sí, pero ¿puedo preguntarle cómo lo descubrió? ¿Regresaste al código fuente? Debido a que la documentación del módulo de registro de Python https://docs.python.org/2/library/logging.html no revela esta información. A veces siento que la documentación estándar de Python es un poco deficiente ... ¿hay algo mejor que puedas recomendar? –

+0

@mikerodent Encontré sobre _logger.handlers_ en SO cuando estaba luchando con este problema de salida duplicada en el registro y verificado lo mismo en el código fuente. Con respecto a la documentación, la mayoría de las veces, los documentos estándar hacen el trabajo. [pymotw] (https://pymotw.com/2/contents.html) me ayuda cuando deseo que se haga algo específico con la ayuda de la biblioteca estándar de Python. – narayan

14

Bueno, la logger.addHandler() no va a agregar un controlador si el controlador ya existe. Para comprobar si el controlador está ya allí se puede comprobar la lista logger.handlers:

logger = logging.getLogger() 
hdlr = logging.FileHandler('logfile.log') 
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
hdlr.setFormatter(formatter) 
logger.addHandler(hdlr) 
logger.setLevel(logging.DEBUG) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 
logger.addHandler(hdlr) 
print logger.handlers 
# [<logging.FileHandler object at 0x14542d0>] 

Además de que yo sugeriría poner este código en su función main() si tiene uno o en el archivo de su paquete __init__.py para no tener que llamarlo cada vez. También le sugiero que use un registrador con nombre y no use el registrador de raíz. Algo como esto:

logger = logging.getLogger(__name__) 
... 

la esperanza que esto era útil :)

+2

es la lista 'Logger.handlers 'documentada en cualquier lugar? No pude encontrarlo en http: // docs.python.org/2/library/logging.html –

+1

@ MariuszPluciński: Sí, no creo que esté documentado en ninguna parte, pero si recuerdo bien tuve que verificar el código de la clase Logger para entender cómo funciona esto: http: // hg. python.org/cpython/file/482590320549/Lib/logging/__init__.py#l1215 – mouad

+1

Pero si no está documentado, puede desaparecer silenciosamente en alguna versión futura de la biblioteca de registro. No estoy seguro de si es bueno depender de esta característica (pero estoy de acuerdo en que no parece haber ninguna forma documentada de verificar esto). –

1

Intente comprobar si logger ya está establecido. Por ejemplo, si el código está dentro de una función:

logger = None 
def init_logger(): 
    global logger 
    if logger is not None: 
     #logger has already been initialized 
     return 
    logger = logging.getLogger() 
    hdlr = logging.FileHandler('logfile.log') 
    formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
    hdlr.setFormatter(formatter) 
    logger.addHandler(hdlr) 
    logger.setLevel(logging.DEBUG) 
+0

supongo que el cheque 'si logger no es Ninguno' no es seguro para subprocesos, por lo que no hay garantía de que el código de inicialización no se ejecutará más de una vez. –

2

Puede también acaba de comprobar para ver si la lista está vacía manejador. Aquí está la solución terminé con:

def setup_logging(self, logfile): 
    self._logger = logging.getLogger('TestSuite') 
    self._logger.setLevel(logging.INFO) 
    host = socket.gethostname().split('.')[0] 
    if self._logger.handlers == []: 
     fh = logging.handlers.RotatingFileHandler(logfile, 
                maxBytes=10*1024*1024, 
                backupCount=5) 
     strfmt = "%" + "(asctime)s [%s] " % host + "%" + "(message)s" 
     fmt = logging.Formatter(strfmt, datefmt="%Y.%m%d %H:%M:%S") 
     fh.setFormatter(fmt) 

     self._logger.addHandler(fh) 
    self._logger.info('-' * (55 - len(host))) 

que estaba viendo el manejador añade varias veces, por lo que cada mensaje de registro se estaba escrito en el archivo de registro más de una vez, y esto lo arregló.

Cuestiones relacionadas