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?

147
demandé sur Renato Damas 0000-00-00 00:00:00

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.

182
répondu Vinay Sajip 2013-09-13 07:47:38

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.

72
répondu Stan Prokop 2016-03-20 08:34:01

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.

19
répondu Yarkee 2018-06-07 12:08:15

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.

5
répondu deeshank 2014-01-06 09:28:23

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.

5
répondu Tommy 2015-11-12 01:11:58

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.

4
répondu phil_20686 2016-03-18 11:34:02

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")
3
répondu deeshank 2013-11-11 18:29:24