web-dev-qa-db-fra.com

Utilisation de Python pour vous connecter à plusieurs modules

J'ai un petit projet python qui présente 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 sur stdout et un fichier journal. Pour utiliser le module de journalisation, une initialisation est nécessaire -

import logging.config

logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')

logger.info('testing')

À l'heure actuelle, j'effectue cette initialisation dans chaque module avant de commencer à consigner des messages. Est-il possible d'effectuer cette initialisation une seule fois à un endroit donné de manière à ce que les mêmes paramètres soient réutilisés en se connectant dans tout le projet?

203
Quest Monger

La meilleure pratique consiste, dans chaque module, à définir un enregistreur comme suit:

import logging
logger = logging.getLogger(__name__)

près du haut du module, puis dans un autre code du module, par ex.

logger.debug('My message with %s', 'variable data')

Si vous devez subdiviser l’activité de journalisation à l’intérieur d’un module, utilisez par exemple.

loggerA = logging.getLogger(__+ '.A')
loggerB = logging.getLogger(__+ '.B')

et connectez-vous à loggerA et loggerB selon le cas.

Dans votre ou vos programmes principaux, par exemple:

def main():
    "your program code"

if __== '__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 __== '__main__':
    main()

Voir ici pour la journalisation à partir 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.

Mise à jour: Lorsque vous appelez fileConfig(), vous pouvez spécifier disable_existing_loggers=False si vous utilisez Python 2.6 ou version ultérieure (voir la documentation pour plus d'informations). La valeur par défaut est True pour la compatibilité ascendante, ce qui entraîne la désactivation de tous les enregistreurs existants par fileConfig(), sauf si eux-mêmes ou leur ancêtre sont explicitement nommés dans la configuration. Avec la valeur définie sur False, les enregistreurs existants sont laissés seuls. Si vous utilisez Python 2.7/Python 3.2 ou version ultérieure, vous voudrez peut-être prendre en compte l'API dictConfig() qui est meilleure que fileConfig() car elle donne davantage de contrôle sur la configuration.

238
Vinay Sajip

En réalité, chaque enregistreur est un enfant de l'enregistreur de paquets du parent (c'est-à-dire que package.subpackage.module hérite de la configuration de package.subpackage), il vous suffit donc de configurer l'enregistreur racine. Ceci peut être réalisé avec logging.config.fileConfig (votre propre configuration pour les enregistreurs) ou logging.basicConfig (définit le consignateur racine). La journalisation de l'installation dans votre module d'entrée (__main__.py ou tout ce que vous voulez exécuter, par exemple main_script.py. __init__.py fonctionne également)

en utilisant 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 Didacticiel de journalisation avancée .

97
Stan Prokop

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, importez simplement la configuration.

from log_conf import Logger

Logger.logr.info("Hello World")

Il s'agit d'un modèle de singleton à consigner, simplement et efficacement.

20
Yarkee

Plusieurs de ces réponses suggèrent que vous faites en haut d'un module

import logging
logger = logging.getLogger(__name__)

Je crois comprendre que cela est considéré comme une très mauvaise pratique . La raison en est que la configuration de 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()

À présent, le journal spécifié dans logging.ini sera vide, car le consignateur existant a été désactivé par appel de fileconfig.

Bien qu'il soit certainement possible de contourner ce problème (disable_existing_Loggers = False), de manière réaliste de nombreux clients de votre bibliothèque ne seront pas informés de ce problème et ne recevront pas vos journaux. Simplifiez la tâche de vos clients en appelant toujours logging.getLogger localement. Astuce Chapeau: J'ai appris ce comportement de site Web de Victor Lin .

Il est donc préférable de toujours appeler logging.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')    

De plus, si vous utilisez fileconfig dans votre tâche principale, définissez disable_existing_loggers = False, au cas où vos concepteurs de bibliothèques utilisent des instances de consignation au niveau du module.

8
phil_20686

Jeter dans une autre solution.

Dans mon module init. Py j'ai quelque chose comme:

# mymodule/__init__.py
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 module, j'ai besoin d'un enregistreur, je fais:

# mymodule/foo.py
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 dont ils proviennent.

6
Tommy

@ La solution de Yarkee semblait meilleure. J'aimerais ajouter quelque chose à cela -

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 connecté à l’ensemble de l’application. J'espère que cela a du sens et de la valeur.

5
deeshank

Vous pouvez 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

Vous pouvez désormais utiliser plusieurs enregistreurs dans le même module et sur l'ensemble du projet si ce qui précède est défini dans un module séparé et importé dans d'autres modules si la journalisation est requise.

a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
4
deeshank

Il y a plusieurs réponses. Je me suis retrouvé avec une solution similaire, mais différente, qui a du sens pour moi, peut-être que cela le sera aussi pour vous. Mon objectif principal était de pouvoir transmettre les journaux aux gestionnaires en fonction de leur niveau (journaux de niveau de débogage vers la console, avertissements et fichiers supérieurs):

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# make default logger output everything to the console
logging.basicConfig(level=logging.DEBUG)

rotating_file_handler = RotatingFileHandler(filename="logs.log")
rotating_file_handler.setLevel(logging.INFO)

app.logger.addHandler(rotating_file_handler)

créé un fichier Nice util appelé logger.py:

import logging

def get_logger(name):
    return logging.getLogger("flask.app." + name)

flask.app est une valeur codée en dur dans flask. le consignateur d'application commence toujours par flask.app comme nom du module.

maintenant, dans chaque module, je peux l'utiliser dans le mode suivant:

from logger import get_logger
logger = get_logger(__name__)

logger.info("new log")

Cela créera un nouveau journal pour "app.flask.MODULE_NAME" avec un minimum d'effort.

2
Ben Yitzhaki

La meilleure pratique serait de créer un module séparément qui n’aurait qu’une méthode à laquelle nous attribuerions un gestionnaire de journalisation à la méthode appelante. Enregistrez ce fichier sous le nom m_logger.py.

import logger, logging

def getlogger():
    # logger
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.DEBUG)
    # create console handler and set level to debug
    #ch = logging.StreamHandler()
    ch = logging.FileHandler(r'log.txt')
    ch.setLevel(logging.DEBUG)
    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
    # add formatter to ch
    ch.setFormatter(formatter)
    # add ch to logger
    logger.addHandler(ch)
    return logger

Appelez maintenant la méthode getlogger () chaque fois que le gestionnaire de journalisation est nécessaire.

from m_logger import getlogger
logger = getlogger()
logger.info('My mssg')
1
Mousam Singh