web-dev-qa-db-fra.com

Python - Évitez de passer la référence de l'enregistreur entre les fonctions?

J'ai un simple script Python qui utilise le logging intégré).

Je configure la journalisation dans une fonction. La structure de base serait quelque chose comme ceci:

#!/usr/bin/env python
import logging
import ...

def configure_logging():
    logger = logging.getLogger("my logger")
    logger.setLevel(logging.DEBUG)
    # Format for our loglines
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    # Setup console logging
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    # Setup file logging as well
    fh = logging.FileHandler(LOG_FILENAME)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    return logger

def count_parrots():
    ...
    logger.debug??

if __== '__main__':
    logger = configure_logging()
    logger.debug("I'm a log file")
    parrots = count_parrots()

Je peux bien appeler l'enregistreur de l'intérieur __main__. Cependant, comment appeler l'enregistreur depuis la fonction count_parrots ()? Quelle est la façon la plus pythonique de gérer la configuration d'un enregistreur comme celui-ci?

47
victorhooi

Vous pouvez soit utiliser l'enregistreur racine (par défaut), et donc les fonctions de niveau module logging.debug, ... ou obtenez votre enregistreur dans la fonction en l'utilisant. En effet, la fonction getLogger est une fonction de type usine avec un registre (de type singleton), c'est-à-dire qu'elle renvoie toujours la même instance pour le nom de l'enregistreur donné. Vous pouvez ainsi récupérer votre logger dans count_parrots en utilisant simplement

logger = logging.getLogger("my logger") 

au début. Cependant, la convention consiste à utiliser un nom hiérarchique en pointillés pour votre enregistreur. Voir http://docs.python.org/library/logging.html#logging.getLogger

ÉDITER:

Vous pouvez utiliser un décorateur pour ajouter le comportement de journalisation à vos fonctions individuelles, par exemple:

def debug(loggername):
    logger = logging.getLogger(loggername) 
    def log_(enter_message, exit_message=None):
        def wrapper(f):
            def wrapped(*args, **kargs):
                logger.debug(enter_message)
                r = f(*args, **kargs)
                if exit_message:
                    logger.debug(exit_message)
                return r
            return wrapped
        return wrapper
    return log_

my_debug = debug('my.logger')

@my_debug('enter foo', 'exit foo')
def foo(a, b):
    return a+b

vous pouvez "coder en dur" le nom de l'enregistreur et supprimer la fermeture de niveau supérieur et my_debug.

40
Yannick Loiseau

Vous pouvez simplement faire:

logger = logging.getLogger("my logger") 

dans votre méthode count_parrots(). Lorsque vous transmettez le nom utilisé précédemment (c'est-à-dire "mon enregistreur"), le module de journalisation renvoie la même instance qui a été créée correspondant à ce nom.

Mise à jour: à partir du tutoriel de journalisation (emphais mine)

getLogger () renvoie une référence à une instance d'enregistreur avec le nom spécifié s'il est fourni, ou root dans le cas contraire. Les noms sont des structures hiérarchiques séparées par des périodes. Plusieurs appels à getLogger () avec le même nom renverront une référence au même objet logger.

15
sateesh

La manière typique de gérer la journalisation est d'avoir un enregistreur par module stocké dans une variable globale. Toutes les fonctions et méthodes de ce module font alors simplement référence à la même instance de l'enregistreur.

Ceci est discuté brièvement dans l'introduction du tutoriel de journalisation avancée dans la documentation: http://docs.python.org/howto/logging.html#advanced-logging-tutorial

Vous pouvez transmettre des instances d'enregistreur comme paramètres, mais cela est généralement rare.

9
ncoghlan

Si vous n'avez pas besoin des messages de journal sur votre console, vous pouvez les utiliser de manière minimaliste.

Vous pouvez également utiliser tail -f myapp.log pour voir les messages sur la console.

import logging

logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', \
    filename='myapp.log', \
    level=logging.INFO)

def do_something():
    logging.info('Doing something')

def main():
    logging.info('Started')
    do_something()
    logging.info('Finished')

if __== '__main__':
    main()
0
Ricardo Brandao

J'ai été confus par le fonctionnement des variables globales en Python. Dans une fonction, vous devez seulement déclarer global logger Si vous faites quelque chose comme logger = logging.getLogger("my logger") et espérez modifier le global logger.

Ainsi, pour modifier votre exemple, vous pouvez créer un objet enregistreur global au début du fichier. Si votre module peut être importé par un autre, vous devez ajouter le NullHandler afin que si l'importateur de la bibliothèque ne souhaite pas que la journalisation soit activée, il n'a aucun problème avec votre lib ( ref ).

#!/usr/bin/env python
import logging
import ...

logger = logging.getLogger("my logger").addHandler(logging.NullHandler())

def configure_logging():
    logger.setLevel(logging.DEBUG)
    # Format for our loglines
    formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
    # Setup console logging
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
    ch.setFormatter(formatter)
    logger.addHandler(ch)
    # Setup file logging as well
    fh = logging.FileHandler(LOG_FILENAME)
    fh.setLevel(logging.DEBUG)
    fh.setFormatter(formatter)
    logger.addHandler(fh)

def count_parrots():
    ...
    logger.debug('counting parrots')
    ...
    return parrots

if __== '__main__':
    configure_logging()
    logger.debug("I'm a log file")
    parrots = count_parrots()
0
raphael