2011-08-24 12 views
50

Estoy usando el registrador de Python. La siguiente es mi código:Salida de registro duplicada al usar el módulo de registro de Python

import os 
import time 
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger = logging.getLogger('ProvisioningPython') 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     return logger 

El problema que tengo es que tengo varias entradas en el archivo de registro para cada logger.info llamada. ¿Como puedo resolver esto?

+0

funciona para mí. Python 3.2 y Windows XP. – Zuljin

+1

¿Estás seguro de que no creas varias instancias de registrador? – Gandi

+0

Sí. en un archivo diferente estoy tomando una nueva instancia como lo hicimos en los proyectos de Java. Por favor, especifíqueme si eso está creando un problema o no. – user865438

Respuesta

54

El logging.getLogger() ya es un producto único. (Documentation)

El problema es que cada vez que llama a myLogger(), agrega otro controlador a la instancia, lo que causa los registros duplicados.

Quizás algo como esto?

import os 
import time 
import datetime 
import logging 

loggers = {} 

def myLogger(name): 
    global loggers 

    if loggers.get(name): 
     return loggers.get(name) 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      '/root/credentials/Logs/ProvisioningPython' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 

     return logger 
+3

Creo que debería tener loggers.update (dict ((name, logger))) en su lugar. – acrophobia

8

Llama a Logger.myLogger() más de una vez. Almacene la instancia del registrador que devuelve en algún lugar y reutilice que.

También tenga en cuenta que si inicia sesión antes de agregar cualquier manejador, se creará un StreamHandler(sys.stderr) predeterminado.

+0

En realidad estoy tratando de acceder a la instancia del registrador como lo usamos en java. Pero no sé si es necesario crear una instancia solo una vez para un proyecto completo o no. – user865438

+1

@ user865483: Solo una vez. Todos los registradores de biblioteca estándar son singletons. –

3

Su registrador debería funcionar como singleton. No deberías crearlo más de una vez. Aquí es ejemplo de cómo podría ser:

import os 
import time 
import datetime 
import logging 
class Logger : 
    logger = None 
    def myLogger(self): 
     if None == self.logger: 
      self.logger=logging.getLogger('ProvisioningPython') 
      self.logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      self.logger.addHandler(handler) 
     return self.logger 

s = Logger() 
m = s.myLogger() 
m2 = s.myLogger() 
m.info("Info1") 
m2.info("info2") 
+0

luego otra vez si voy a tomar la instancia diferente en un archivo diferente. Supongamos en el archivo 1 s = Logger() m = s.myLogger() y en el archivo 2 s = Logger() Funcionará o no m2 = s.myLogger() – user865438

+0

Todavía recibo copia del mismo registro varias veces. Tengo una duda aquí si el hilo interno Log imprime más de uno o no. Por favor, ayúdenme en esto. – user865438

+1

@ user865438, no necesitamos preocuparnos por hacer que la implementación sea singleton (ya lo es). Para iniciar sesión en los submódulos, siga el Logging Cookbook oficial [link] (https://docs.python.org/2/howto/logging-cookbook.html#logging-cookbook). Básicamente debe seguir la jerarquía de nombres al nombrar los registradores y se ocupa del resto. – narayan

30
import datetime 
import logging 
class Logger : 
    def myLogger(self): 
     logger=logging.getLogger('ProvisioningPython') 
     if not len(logger.handlers): 
      logger.setLevel(logging.DEBUG) 
      now = datetime.datetime.now() 
      handler=logging.FileHandler('/root/credentials/Logs/ProvisioningPython'+ now.strftime("%Y-%m-%d") +'.log') 
      formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
      handler.setFormatter(formatter) 
      logger.addHandler(handler) 
     return logger 

hizo el truco para mí

usando Python 2.7

+2

+1 para la verificación de longitud de logger.handlers. – kakyo

+1

Esto funciona incluso cuando el módulo se recarga (que no es el caso de las otras respuestas) – yco

+1

Gracias por la sugerencia, por cierto, para verificar si una lista está vacía o no, no es necesario que utilice el operador "len" que pueda utilizar directamente si my_list: .. – redobot

1

La implementación de registrador ya es un producto único.

Las llamadas múltiples a logging.getLogger ('someLogger') devuelven una referencia al mismo objeto logger. Esto es cierto no solo en el mismo módulo , sino también en todos los módulos, siempre que se encuentre en el mismo proceso de intérprete de Python . Es cierto para las referencias al mismo objeto; Además, el código de la aplicación puede definir y configurar un registrador padre en un módulo y crear (pero no configurar) un registrador hijo en un módulo separado, y todas las llamadas de registrador al hijo pasarán al padre . Aquí es un módulo principal

Fuente- Using logging in multiple modules

Así que la forma se debe utilizar esto es -

Supongamos que hemos creado y configurado un registrador de llamada '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.

Consulte this pregunta para obtener una respuesta un poco más detallada.

+1

redefinir los controladores después de que getLogger me funcione: 'logger = logging.getLogger ('my_logger'); logger.handlers = [logger.handlers [0],] ' – radtek

0

Doble (o triple o ..- en función del número de recarga) la salida del registrador también puede ocurrir cuando recarga su módulo a través de importlib.reload (por la misma razón explicada en la respuesta aceptada). Estoy agregando esta respuesta solo para una futura referencia, ya que me tomó un tiempo averiguar por qué mi salida es duplicada (triple).

0

Una solución sencilla es como

logger.handlers[:] = [handler] 

esta manera se evita añadiendo nuevo manejador de la lista "manejadores" subyacentes.

0
from logging.handlers import RotatingFileHandler 
import logging 
import datetime 

# stores all the existing loggers 
loggers = {} 

def get_logger(name): 

    # if a logger exists, return that logger, else create a new one 
    global loggers 
    if name in loggers.keys(): 
     return loggers[name] 
    else: 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     now = datetime.datetime.now() 
     handler = logging.FileHandler(
      'path_of_your_log_file' 
      + now.strftime("%Y-%m-%d") 
      + '.log') 
     formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') 
     handler.setFormatter(formatter) 
     logger.addHandler(handler) 
     loggers.update(dict(name=logger)) 
     return logger 
+0

Agregue la explicación para que esta respuesta sea más valiosa para el uso a largo plazo. –

0

que son capaces de obtener la lista de todos los controladores para el registrador en particular, por lo que se puede hacer algo como esto

logger = logging.getLogger(logger_name) 
handler_installed = False 
for handler in logger: 
    # Here your condition to check for handler presence 
    if isinstance(handler, logging.FileHandler) and handler.baseFilename == log_filename: 
     handler_installed = True 
     break 

if not handler_installed: 
    logger.addHandler(your_handler) 

En el ejemplo anterior, comprobamos si el controlador para un archivo especificado ya está enganchado al registrador, pero tener acceso a la lista de todos los manejadores le da la capacidad de decidir en qué criterios debe agregar otro manejador o no.

0

Tuvo este problema hoy. Como mis funciones eran @staticmethod, las sugerencias anteriores se resolvieron con random().

buscando algo como:

import random 

logger = logging.getLogger('ProvisioningPython.{}'.format(random.random())) 
8

Como Python 3.2 que sólo puede comprobar si los controladores ya están presentes y si es así, claro ellos antes de añadir nuevos controladores. Esto es muy conveniente cuando la depuración y el código de inicialización incluye su registrador

if (logger.hasHandlers()): 
    logger.handlers.clear() 

logger.addHandler(handler) 
+0

Buena respuesta, Thx :)) –

0

Ésta es una adición a la respuesta de @ rm957377 pero con una explicación de por qué ocurre esto. Cuando ejecuta una función lambda en AWS, llaman a su función desde una instancia de envoltura que permanece activa para varias llamadas. Es decir, si llama al addHandler() dentro del código de su función, continuará agregando manejadores duplicados al singleton de registro cada vez que se ejecuta la función. El singleton de registro persiste a través de múltiples llamadas de su función lambda.

para resolver este puede borrar sus manejadores antes de que las plantes a través de:

logging.getLogger().handlers.clear() 
logging.getLogger().addHandler(...) 
Cuestiones relacionadas