web-dev-qa-db-fra.com

Comment utiliser logging.getLogger (__ name__) dans plusieurs modules

Extrait du logging howto for Python 2.7 (c'est moi qui souligne):

Une bonne convention à utiliser pour nommer les enregistreurs consiste à utiliser un enregistreur de niveau module, dans chaque module qui utilise la journalisation, nommé comme suit:

logger = logging.getLogger(__name__)

Cela signifie que les noms des enregistreurs suivent la hiérarchie des packages/modules , et il est intuitivement évident où les événements sont enregistrés uniquement à partir du nom de l'enregistreur.

Cela ressemble à de bons conseils.

Maintenant, le logging livre de recettes fournit un exemple pour plusieurs modules, qui utilise des noms d'enregistreurs codés en dur au lieu de la constante __name__. Dans le "module principal" de l'exemple, nous trouvons

logger = logging.getLogger('spam_application')

et dans le "module auxiliaire" on trouve

module_logger = logging.getLogger('spam_application.auxiliary')

J'ai copié cet exemple textuellement dans un dossier de package avec la structure suivante:

cookbook-example
|- __init__.py
|- main_module.py
|- auxiliary_module.py

Cela fonctionne sans problème, produisant la sortie de journalisation attendue à la fois du module principal et du module auxiliaire, mais voici la chose:

Si je remplace maintenant les noms d'enregistreurs codés en dur par la constante __name__, Comme recommandé par le logging howto , l'exemple de livre de cuisine tombe en panne: je ne reçois que les messages de journalisation du module principal, mais rien du module auxiliaire.

Je dois manquer quelque chose d'évident. Des idées sur ce que je fais mal?

Remarque:

Il y a beaucoup de questions et réponses très similaires, par exemple: 1 , 2 , , 4 , 5 , 6 , et bien d'autres. Cependant, aucun de ceux-ci ne semble répondre à cette question spécifique.

--Modifier--

Voici un exemple minimal basé sur l'exemple du livre de recettes, avec les chaînes de nom explicites remplacées par __name__.

main_module.py

import logging
import auxiliary_module

# create and configure main logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
# create console handler with a higher log level
handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
# create formatter and add it to the handler
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
# add the handler to the logger
logger.addHandler(handler)

logger.info('message from main module')
auxiliary_module.some_function()

auxiliaire_module.py

import logging

# create logger
module_logger = logging.getLogger(__name__) 

def some_function():
    module_logger.info('message from auxiliary module')
15
djvg

Comme indiqué par @shmee dans cette réponse , la hiérarchie de l'enregistreur doit être définie explicitement dans le nom de l'enregistreur, en utilisant la notation par points. Autrement dit, si le nom de l'enregistreur dans main_module.py Est par exemple 'a', Le nom de l'enregistreur dans auxiliary_module.py Doit être 'a.b' (Pas seulement 'b'), Pour qu'il hérite de la configuration de l'enregistreur 'a'. Ceci est également mentionné dans la documentation getLogger() .

Cependant, cela devrait être pris en charge automatiquement lors de l'utilisation de __name__, Comme indiqué dans le logging how-to :

Cela signifie que les noms des enregistreurs suivent la hiérarchie des packages/modules, et il est intuitivement évident où les événements sont enregistrés uniquement à partir du nom de l'enregistreur.

Le fait est que pour que cela fonctionne, vous devez utiliser __name__ De la bonne manière, et je ne l'ai pas fait.

Le problème dans mon exemple est dans l'organisation des fichiers dans le dossier du package cookbook-example:

Le module principal et le module auxiliaire sont tous deux au même niveau (c'est-à-dire dans le même dossier). Ainsi, comme expliqué ici , le __name__ Pour le module principal sera alors '__main__' (Comme c'est le script de niveau supérieur), et le __name__ pour le module auxiliaire sera 'auxiliary_module' (c'est-à-dire le nom de fichier), PAS '__main__.auxiliary_module'.

En conséquence, l'enregistreur dans le module auxiliaire sera un enfant de l'enregistreur racine, pas un enfant de l'enregistreur '__main__', Et il héritera donc de la configuration de l'enregistreur racine (qui a toujours le niveau d'enregistrement par défaut WARNING) au lieu de la configuration spécifiée dans le module principal.

Donc, pour que l'exemple fonctionne, nous avons plusieurs options:

  1. Remplacez getLogger(__name__) dans le module principal par getLogger(). Cela appliquera la configuration à l'enregistreur racine et donc également à l'enregistreur de module auxiliaire, comme suggéré par @shmee.

  2. Remplacez getLogger(__name__) dans le module auxiliaire par getLogger('__main__.' + __name__). Le résultat sera équivalent à l'exemple de livre de recettes d'origine (sauf que l'enregistreur principal s'appelle désormais '__main__' Au lieu de 'spam_application').

16
djvg

Le nom des enregistreurs est ce qui vous manque. Dans l'exemple, un enregistreur nommé spam_application est créé dans le module principal. Ensuite, des gestionnaires et des formateurs sont créés et ajoutés à cet enregistreur.

Dans auxiliary_module les enregistreurs sont créés avec des noms commençant par spam_application resp. spam_application.auxiliary. Cela crée effectivement une hiérarchie d'enregistreurs qui se propagent à leurs parents respectifs, sauf s'ils sont explicitement désactivés. Cette hiérarchie est spam_appliclation <- spam_application.auxiliary <- spam_application.auxiliary.Auxiliary ou logger <- module_logger <- self.logger dans le cas de l'exemple de livre de cuisine.

Si vous remplacez les noms explicites de l'enregistreur par __name__ vous finissez par avoir un enregistreur configuré nommé __main__ dans votre module principal, qui est configuré avec des gestionnaires, mais le nom de vos enregistreurs auxiliaires ne crée pas une hiérarchie, par conséquent vos enregistreurs auxiliaires_module se propagent vers l'enregistreur racine implicite qui n'a aucun gestionnaire configuré.

Essayez ce qui suit: Modifiez la méthode init de votre classe comme suit:

def __init__(self):
    self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')
    print self.logger.parent
    self.logger.info('creating an instance of Auxiliary')

Exécutez ensuite votre module principal une fois avec

self.logger = logging.getLogger('spam_application.auxiliary.Auxiliary')

et une fois avec

self.logger = logging.getLogger(__name__)

La sortie devrait ressembler à ceci:

<Logger spam_application.auxiliary (WARNING)> # with explicit logger name
<RootLogger root (WARNING)>                   # using __name__
6
shmee