Utilisation de la journalisation Python dans plusieurs modules
J'ai un petit projet python qui a la structure suivante -
Project
-- pkg01
-- test01.py
-- pkg02
-- test02.py
-- logging.conf
Je prévois d'utiliser le module de journalisation par défaut pour imprimer des messages vers stdout et un fichier journal. Pour utiliser le module de journalisation, une certaine initialisation est requise -
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')
logger.info('testing')
Actuellement, j'effectue cette initialisation dans chaque module avant de commencer à journaliser les messages. Est-il possible d'effectuer cette initialisation une seule fois au même endroit afin que les mêmes paramètres soient réutilisés en se connectant sur tout le projet?
7 réponses
La Meilleure pratique est, dans chaque module, d'avoir un enregistreur définie comme ceci:
import logging
logger = logging.getLogger(__name__)
Près du haut du module, puis dans un autre code dans le module, faites par exemple
logger.debug('My message with %s', 'variable data')
Si vous devez subdiviser l'activité de journalisation dans un module, utilisez par exemple
loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')
Et connectez-vous à loggerA
et loggerB
selon le cas.
Dans votre ou vos programmes principaux, faites par exemple:
def main():
"your program code"
if __name__ == '__main__':
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
main()
Ou
def main():
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
# your program code
if __name__ == '__main__':
main()
Voir ici pour l'enregistrement de plusieurs modules, et ici pour la journalisation de la configuration du code qui sera utilisé comme module de bibliothèque par un autre code.
Update: lorsque vous appelez fileConfig()
, vous pouvez spécifier disable_existing_loggers=False
Si vous utilisez Python 2.6 ou une version ultérieure (voir les documents pour plus d'informations). La valeur par défaut est True
pour la rétrocompatibilité, ce qui entraîne la désactivation de tous les enregistreurs existants par fileConfig()
à moins qu'ils ou leur ancêtre ne soient explicitement nommés dans la configuration. Avec la valeur False
, Les enregistreurs existants sont laissés seul. Si vous utilisez Python 2.7 / Python 3.2 ou version ultérieure, vous voudrez peut-être considérer l'API dictConfig()
qui est meilleure que fileConfig()
car elle donne plus de contrôle sur la configuration.
En fait, chaque enregistreur est un enfant de l'enregistreur de paquets du parent (c'est-à-dire package.subpackage.module
hérite de la configuration de package.subpackage)
, donc tout ce que vous devez faire est de configurer l'enregistreur racine. Ceci peut être réalisé par logging.config.fileConfig
(votre propre config pour les bûcherons) ou logging.basicConfig
(définit la racine de l'enregistreur). Configurez la connexion dans votre module d'entrée (__main__.py
ou tout ce que vous voulez exécuter, par exemple main_script.py
. __init__.py
fonctionne aussi)
Utilisation de basicConfig:
# package/__main__.py
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
En utilisant fileConfig:
# package/__main__.py
import logging
import logging.config
logging.config.fileConfig('logging.conf')
Puis créez chaque enregistreur en utilisant:
# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)
log.info("Hello logging!")
Pour plus d'informations, voir tutoriel de journalisation avancée.
Je le fais toujours comme ci-dessous.
Utilisez un seul fichier python pour configurer mon journal en tant que modèle singleton nommé 'log_conf.py
'
#-*-coding:utf-8-*-
import logging.config
def singleton(cls):
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return get_instance()
@singleton
class Logger():
def __init__(self):
logging.config.fileConfig('logging.conf')
self.logr = logging.getLogger('root')
Dans un autre module, il suffit d'importer la configuration.
from log_conf import Logger
Logger.logger.info("Hello World")
C'est un modèle singleton à enregistrer, simplement et efficacement.
La solution de @ Yarkee semblait meilleure. Je voudrais ajouter somemore à elle -
class Singleton(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances.keys():
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
return cls._instances[cls]
class LoggerManager(object):
__metaclass__ = Singleton
_loggers = {}
def __init__(self, *args, **kwargs):
pass
@staticmethod
def getLogger(name=None):
if not name:
logging.basicConfig()
return logging.getLogger()
elif name not in LoggerManager._loggers.keys():
logging.basicConfig()
LoggerManager._loggers[name] = logging.getLogger(str(name))
return LoggerManager._loggers[name]
log=LoggerManager().getLogger("Hello")
log.setLevel(level=logging.DEBUG)
Donc LoggerManager peut être un pluggable à l'application entière. Espérons que cela a du sens et de la valeur.
Lancer une autre solution.
Dans le module principal init j'ai quelque chose comme:
import logging
def get_module_logger(mod_name):
logger = logging.getLogger(mod_name)
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
return logger
Ensuite, dans chaque classe, j'ai besoin d'un enregistreur, je fais:
from [modname] import get_module_logger
logger = get_module_logger(__name__)
Lorsque les journaux sont manqués, vous pouvez différencier leur source par le module de provenance.
Plusieurs de ces réponses suggèrent qu'en haut d'un module vous faites
import logging
logger = logging.getLogger(__name__)
Je crois comprendre que cela est considéré comme Très mauvaise pratique . La raison en est que la configuration du fichier désactivera tous les enregistreurs existants par défaut. Par exemple
#my_module
import logging
logger = logging.getLogger(__name__)
def foo():
logger.info('Hi, foo')
class Bar(object):
def bar(self):
logger.info('Hi, bar')
Et dans votre module principal :
#main
import logging
# load my module - this now configures the logger
import my_module
# This will now disable the logger in my module by default, [see the docs][1]
logging.config.fileConfig('logging.ini')
my_module.foo()
bar = my_module.Bar()
bar.bar()
Maintenant le journal spécifié dans la journalisation.ini sera vide, car l'enregistreur existant a été désactivé par l'appel fileconfig.
Alors qu'il est certainement possible de contourner cela (disable_existing_Loggers=False), de manière réaliste, de nombreux clients de votre bibliothèque ne connaîtront pas ce comportement et ne recevront pas vos journaux. Rendez-le facile pour vos clients en appelant toujours la journalisation.getlogger localement. Astuce: j'ai appris ce comportement sur le site Web de Victor Lin .
Donc, la bonne pratique est de toujours appeler la journalisation.getlogger localement. Par exemple
#my_module
import logging
logger = logging.getLogger(__name__)
def foo():
logging.getLogger(__name__).info('Hi, foo')
class Bar(object):
def bar(self):
logging.getLogger(__name__).info('Hi, bar')
Aussi, si vous utilisez fileconfig dans votre main, définissez disable_existing_loggers = False, juste au cas où votre les concepteurs de bibliothèques utilisent des instances de logger au niveau du module.
Vous pourriez aussi trouver quelque chose comme ça!
def get_logger(name=None):
default = "__app__"
formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s',
datefmt='%Y-%m-%d %H:%M:%S')
log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"}
if name:
logger = logging.getLogger(name)
else:
logger = logging.getLogger(default)
fh = logging.FileHandler(log_map[name])
fh.setFormatter(formatter)
logger.addHandler(fh)
logger.setLevel(logging.DEBUG)
return logger
Maintenant, vous pouvez utiliser plusieurs enregistreurs dans le même module et dans tout le projet si ce qui précède est défini dans un module séparé et importé dans d'autres modules, la journalisation est requise.
a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")