57 votes

La journalisation python garantit qu'un gestionnaire n'est ajouté qu'une seule fois.

J'ai un morceau de code qui initialise un logger comme ci-dessous.

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)

Malheureusement, ce code est appelé plusieurs fois. Existe-t-il un moyen de vérifier si le gestionnaire existe déjà ? Je préférerais mettre en œuvre ce système sans avoir à utiliser un Singleton.

EDIT : Désolé, j'ai oublié de mentionner que c'est sur python 2.5 - merci, Richard.

49voto

narayan Points 969

Comme le commente @offbyone, il est possible d'ajouter des gestionnaires redondants à la même instance du logger. Le site documentation python sur la journalisation dis-

"Des appels multiples à getLogger() avec le même nom retourneront une référence au même objet logger".

Nous n'avons donc pas à nous soucier de faire de l'implémentation un singleton, puisqu'elle l'est déjà.

Malheureusement, il en est de même pas vrai pour les gestionnaires associés à la même instance de l'enregistreur. Il y a peut être des manipulateurs en double attachés.

Exemple -

  1. Copiez ce code et enregistrez-le dans 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. et le code suivant dans 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. Exécuter sub.py

    narayan@y510p:~/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 --------------------------------------------------

Ainsi, les appels multiples à la méthode renvoyant le même logger ajoutaient des gestionnaires en double.

Maintenant, pour votre question

Y a-t-il un moyen de vérifier si le gestionnaire existe déjà ?

Oui, il y a

logger.handlers renvoie une liste de tous les gestionnaires associés à l'objet donné. logger .

Avant d'ajouter des gestionnaires à une instance du logger, assurez-vous de ne pas ajouter des gestionnaires en double Dans main.py, il suffit de décommenter la ligne qui dit if not len(logger.handlers): et indenter correctement les deux lignes suivantes-

if not len(logger.handlers):
    logger.addHandler(fileHandler)
    logger.addHandler(consoleHandler)

Maintenant, exécutez à nouveau sub.py

narayan@y510p:~/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 --------------------------------------------------

De plus, si vous voulez limiter le type de gestionnaires à ajouter à l'instance du logger, vous pouvez faire quelque chose comme ceci-

    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'

J'espère que cela vous aidera !

Editar:

Malheureusement, il en est de même pas vrai pour les gestionnaires associés à la même instance de l'enregistreur. Il y a peut être en double handlers attachés.

Il s'avère que l'affirmation ci-dessus n'est pas entièrement vraie.

Supposons que nous ayons créé et configuré un enregistreur appelé journal principal (main_logger) dans le module principal (qui configure simplement le logger, ne retourne rien).

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

Maintenant, dans un sous-module, si nous créons un enregistreur enfant en suivant la hiérarchie de dénomination Journal principal, journal secondaire, journal de module Nous n'avons pas besoin de le configurer dans le sous-module. Il suffit de créer le logger en suivant la hiérarchie de nommage.

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

Et il n'ajoutera pas de gestionnaire de doublons non plus.

Référence- Utilisation de la journalisation dans plusieurs modules

18voto

mouad Points 21520

Eh bien, le logger.addHandler() n'ajoutera pas de gestionnaire si le gestionnaire existe déjà. Pour vérifier si le gestionnaire existe déjà, vous pouvez consulter la liste 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>]

En outre, je vous suggère de placer ce code dans votre fonction main() si vous en avez une ou dans la fonction __init__.py de votre paquet pour ne pas avoir à l'appeler à chaque fois. Je vous suggère également d'utiliser un logger nommé, et de ne pas utiliser le logger Root. Quelque chose comme ceci :

logger = logging.getLogger(__name__)
...

J'espère que cela vous a aidé :)

4voto

Tom Barron Points 242

Vous pouvez aussi simplement vérifier si la liste des gestionnaires est vide. Voici la solution que j'ai trouvée :

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)))

Je voyais le gestionnaire ajouté plusieurs fois, de sorte que chaque message était écrit dans le fichier journal plus d'une fois, et ceci a réglé le problème.

2voto

Edward Corrigall Points 153

Si vous connaissez AWS Lambda, alors vous savez peut-être déjà que dans certains contextes, les gestionnaires sont préconfigurés [1]. En supposant que logger.handlers n'est pas vide, n'est pas suffisante. Je recommande de définir un attribut sur l'instance du logger, comme ceci :

def init_logger(logger):
    if hasattr(logger, 'initialized'):
        return logger  # No need for addHandler
    else:
        setattr(logger, 'initialized', True)

    # Initialize the logger
    # ...

[1] Utilisation de la journalisation python avec AWS Lambda

1voto

multipleinterfaces Points 2917

Essayez de vérifier si logger est déjà réglé. Par exemple, si ce code est à l'intérieur d'une fonction :

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)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X