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')
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:
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.
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'
).
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__