Journalisation Python - Désactivez la journalisation des modules importés

100

J'utilise le module de journalisation Python et je souhaite désactiver les messages de journal imprimés par les modules tiers que j'importe. Par exemple, j'utilise quelque chose comme ce qui suit:

logger = logging.getLogger()
logger.setLevel(level=logging.DEBUG)
fh = logging.StreamHandler()
fh_formatter = logging.Formatter('%(asctime)s %(levelname)s %(lineno)d:%(filename)s(%(process)d) - %(message)s')
fh.setFormatter(fh_formatter)
logger.addHandler(fh)

Cela imprime mes messages de débogage lorsque je fais un logger.debug ("mon message!"), Mais il imprime également les messages de débogage de n'importe quel module que j'importe (comme les requêtes, et un certain nombre d'autres choses).

Je voudrais voir uniquement les messages de journal des modules qui m'intéressent. Est-il possible de faire en sorte que le module de journalisation fasse cela?

Idéalement, j'aimerais pouvoir dire à l'enregistreur d'imprimer les messages de "ModuleX, ModuleY" et d'ignorer tous les autres.

J'ai regardé ce qui suit, mais je ne veux pas avoir à désactiver / activer la journalisation avant chaque appel à une fonction importée: journalisation - comment ignorer les journaux de modules importés?

aveugle motoneige
la source

Réponses:

69

Le problème est que l'appel getLoggersans arguments renvoie l' enregistreur racine. Ainsi, lorsque vous définissez le niveau, logging.DEBUGvous définissez également le niveau pour d'autres modules qui utilisent cet enregistreur.

Vous pouvez résoudre ce problème en n'utilisant tout simplement pas l'enregistreur racine. Pour ce faire, passez simplement un nom comme argument, par exemple le nom de votre module:

logger = logging.getLogger('my_module_name')
# as before

cela créera un nouvel enregistreur et ne changera donc pas par inadvertance le niveau de journalisation pour d'autres modules.


De toute évidence, vous devez utiliser logger.debugau lieu de logging.debugpuisque cette dernière est une fonction pratique qui appelle la debugméthode de l'enregistreur racine.

Ceci est mentionné dans le didacticiel de journalisation avancé . Il vous permet également de savoir quel module a déclenché le message de journal de manière simple.

Bakuriu
la source
37
Je crée un enregistreur avec __name__r mais je vois toujours les logs des modules importés. J'essaie de configurer la journalisation avec un fichier de configuration ini, que dois-je faire pour cela?
Durga Swaroop
8
Créer un enregistreur avec __name__n'a pas non plus fonctionné pour moi. Peut-être parce que j'utilise un script autonome et non un "module"? Ce qui a fonctionné pour moi était de configurer la journalisation pour les modules importés ( matpplotlib, dans mon cas) via logging.getLogger("matplotlib").setLevel(logging.WARNING)et pour mon script via logging.basicConfig.
bli
1
Je voulais juste souligner la valeur de votre ligne "Evidemment, vous devez utiliser logger.debugau lieu de logging.debug". C'est une erreur facile à faire en utilisant la journalisation au lieu de l' enregistreur, mais cela usurpe toute la configuration intelligente que vous souhaitez configurer. J'ai passé les dernières heures à vivre ça!
timdadev le
@bli Il y a une grande différence entre la journalisation lors du développement d'une bibliothèque et lors du développement d'un exécutable. En gros: si vous écrivez un module / package destiné à être importé, vous ne devez rien configurer. Le module ne doit contenir que les logger = logging.getLogger('package.my_module')appels et vos appels logger.debug/warning, aucune configuration des niveaux de journalisation ou des gestionnaires. Lorsque vous y écrivez l'application binaire , vous devez décider du niveau des différents journaux et des gestionnaires. Les bibliothèques contenant une configuration de journalisation seront toujours un problème.
Bakuriu le
Dans mon cas, le package que j'importe utilise le root logger (via logging.info). Existe-t-il un moyen de désactiver spécifiquement les journaux racine de ce package?
IanS
45

Si vous allez utiliser le loggingpackage python , c'est une convention courante de définir un enregistreur dans chaque module qui l'utilise.

logger = logging.getLogger(__name__)

De nombreux packages python populaires le font, y compris requests. Si un package utilise cette convention, il est facile d'activer / désactiver la journalisation pour celui-ci, car le nom du journal sera le même nom que le package (ou sera un enfant de ce journal). Vous pouvez même l'enregistrer dans le même fichier que vos autres enregistreurs.

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

requests_logger = logging.getLogger('requests')
requests_logger.setLevel(logging.DEBUG)

handler = logging.StreamHandler()
handler.setLevel(logging.DEBUG)
logger.addHandler(handler)
requests_logger.addHandler(handler)
Brendan Abel
la source
15
Sachez que lorsque vous essayez de configurer vos enregistreurs comme dans le didacticiel de base officiel avec logging.basicConfig(...)tous les enregistreurs, ils seront désormais sortis vers logging.lastResort(à partir de Python 3.2, qui est stderr) si aucun gestionnaire n'a été donné ou vers le gestionnaire que vous avez défini. Ne l'utilisez donc pas ou vous continuerez de toute façon à recevoir tous les messages du journal.
user136036
44

Je ne sais pas si cela est approprié pour publier, mais j'étais coincé pendant longtemps et je voulais aider quiconque avec le même problème, car je ne l'avais trouvé nulle part ailleurs!

Je recevais des journaux de débogage de matplotlib malgré la documentation assez simple du didacticiel avancé de journalisation et du dépannage . J'étais en main()train de lancer mon logger dans un fichier et d'importer une fonction pour créer un tracé à partir d'un autre fichier (où j'avais importé matplotlib).

Ce qui a fonctionné pour moi a été de définir le niveau de matplotlib avant de l' importer, plutôt qu'après comme je l'avais fait pour d'autres modules dans mon fichier principal. Cela m'a semblé contre-intuitif, donc si quelqu'un a un aperçu de la façon dont vous pouvez définir la configuration d'un enregistreur qui n'a pas encore été importé, je serais curieux de savoir comment cela fonctionne. Merci!

Dans mon dossier principal:

import logging
import requests
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
logging.getLogger('requests').setLevel(logging.DEBUG)

def main():
  ...

Dans mon plot.pydossier:

import logging
logging.getLogger('matplotlib').setLevel(logging.WARNING)
import matplotlib.pyplot as plt

def generatePlot():
  ...
Finlandais
la source
J'ai eu une erreur: l'objet 'Logger' n'a pas d'attribut 'DEBUG'. logger.DEBUGdevrait êtrelogging.DEBUG
foxiris
Merci! Cela aide vraiment! J'ai défini le niveau de journalisation de matplotlib après ma configuration de journalisation principale et avant la commande qui importera matplotlib. Résolu!
gph le
J'ai défini la journalisation matplotlibsur WARNING après avoir importé le module, car l'ajouter avant l'importation donnerait une erreur de peluche. Cela fonctionnait toujours pour moi. J'utilise matplotlib==3.3.2en Python 3.7 si cela aide.
Fin-2-Fin
9

@Bakuriu explique assez élégamment la fonction. Inversement, vous pouvez utiliser la getLogger()méthode pour récupérer et reconfigurer / désactiver les enregistreurs indésirables.

Je voulais aussi ajouter que la logging.fileConfig()méthode accepte un paramètre appelé disable_existing_loggersqui désactivera tous les enregistreurs précédemment définis (c'est-à-dire dans les modules importés).

apex-meme-seigneur
la source
9

Cela désactive tous les enregistreurs existants, tels que ceux créés par les modules importés, tout en utilisant toujours l'enregistreur racine (et sans avoir à charger un fichier externe).

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': True,
})

Notez que vous devez d'abord importer tous les modules que vous ne souhaitez pas enregistrer! Sinon, ceux-ci ne seront pas considérés comme des «enregistreurs existants». Il désactivera alors tous les enregistreurs de ces modules. Cela pourrait vous conduire à passer à côté d'erreurs importantes!

Pour des exemples plus détaillés utilisant les options associées pour la configuration, voir https://gist.github.com/st4lk/6287746 , et voici un exemple (partiellement fonctionnel) utilisant YAML pour la configuration avec la coloredlogbibliothèque.

avv
la source
Quelle est votre question?
user1767754
1
Cela fonctionne par requestexemple, mais cela ne fonctionnera pas lorsque les modules importés créeront leurs enregistreurs dans leur classe que vous appelleriez plus tard, comme le APSchedulerfait lorsque vous appelez BackgroundScheduler.BackgroundScheduler(). Voir ici pour une solution: stackoverflow.com/a/48891485/2441026
user136036
Cela fonctionne pour mon cas avec le fichier de configuration yaml
user4015990
4

Vous pouvez utiliser quelque chose comme:

logging.getLogger("imported_module").setLevel(logging.WARNING)
logging.getLogger("my_own_logger_name").setLevel(logging.DEBUG)

Cela définira le niveau de journalisation de mon propre module sur DEBUG, tout en empêchant le module importé d'utiliser le même niveau.

Remarque: "imported_module"peut être remplacé par imported_module.__name__(sans guillemets), et "my_own_logger_name"peut être remplacé par __name__si c'est la façon dont vous préférez le faire.

Kamiku
la source
1

J'ai eu le même problème. J'ai un fichier logging_config.py que j'importe dans tous les autres fichiers py. Dans le fichier logging_config.py, j'ai défini le niveau de journalisation de l'enregistreur racine sur ERROR (par défaut, son avertissement):

logging.basicConfig(
    handlers=[
        RotatingFileHandler('logs.log',maxBytes=1000, backupCount=2),
        logging.StreamHandler(), #print to console
    ],
    level=logging.ERROR
)

Dans d'autres modules, j'importe logging_config.py et déclare un nouveau logger et règle son niveau sur debug:

log = logging.getLogger(__name__)
log.setLevel(logging.DEBUG)

De cette façon, tout ce que je connecte dans mes fichiers py est enregistré, mais les éléments enregistrés au niveau du débogage et des informations par des modules importés tels que urllib, request, boto3, etc. ne sont pas enregistrés. S'il y a une erreur dans ce module d'importation, il est consigné, car j'ai défini le niveau des enregistreurs racine sur ERROR.

Aseem
la source
0

Une autre chose à considérer est la propriété propagate de la classe Logger.

Par exemple, la bibliothèque py-suds pour gérer les appels savon, même mise sur ERROR

logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)

enregistre des journaux sur un module appelé sxbasics.py creationg une énorme quantité de journaux

entrez la description de l'image ici

que parce que la propagation des journaux est True par défaut, la valeur False, j'ai récupéré 514 Mo de journaux.

import logging
logging.getLogger("suds").propagate = False
logging.getLogger('suds.client').setLevel(logging.ERROR)
logging.getLogger('suds.transport').setLevel(logging.ERROR)
logging.getLogger('suds.xsdschema').setLevel(logging.ERROR)
logging.getLogger('suds.wsdl').setLevel(logging.ERROR)
Andrea Bisello
la source