Python: module de journalisation-globalement
j'ai
class customLogger(logging.Logger):
...
dans un fichier avec ses formateurs et d'autres choses. L'enregistreur fonctionne parfaitement sur son propre.
- je importer ce module dans mon main.py fichier et créer un objet comme ceci:
self.log = log.customLogger(arguments)
Mais évidemment je ne peux pas accéder à cet objet à partir d'autres parties de mon code. Est-ce que j'utilise une mauvaise approche? Est-il une meilleure façon de faire cette?
4 réponses
Utiliser logging.getLogger(name)
pour créer un logger global nommé.
main.py
import log
logger = log.setup_custom_logger('root')
logger.debug('main message')
import submodule
log.py
import logging
def setup_custom_logger(name):
formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s')
handler = logging.StreamHandler()
handler.setFormatter(formatter)
logger = logging.getLogger(name)
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)
return logger
submodule.py
import logging
logger = logging.getLogger('root')
logger.debug('submodule message')
Sortie
2011-10-01 20:08:40,049 - DEBUG - main - main message
2011-10-01 20:08:40,050 - DEBUG - submodule - submodule message
comme je n'ai pas trouvé de réponse satisfaisante, je voudrais développer un peu la réponse à la question afin de donner un aperçu du fonctionnement et des intentions de la logging
bibliothèque, qui vient avec la bibliothèque standard de Python.
contrairement à L'approche de L'OP (poster original), la bibliothèque sépare clairement l'interface de l'enregistreur et la configuration de l'enregistreur lui-même.
la configuration des manipulateurs est la prérogative du développeur d'application qui utilise votre bibliothèque.
cela signifie Que vous devez créer une classe Logger personnalisée et configurer le logger à l'intérieur de cette classe en ajoutant n'importe quelle configuration ou n'importe quoi.
logging
bibliothèque présente quatre composantes: enregistreurs,les gestionnaires,filtres et formateurs.
- les bûcherons exposent interface que le code d'application utilise directement.
- les gestionnaires envoient les enregistrements (créés par les bûcherons) à la destination appropriée.
- les filtres fournissent une facilité plus fine pour déterminer quels enregistrements log à la sortie.
- les formateurs spécifient la disposition des enregistrements log dans la sortie finale.
une structure de projet commune ressemble à ceci:
Project/
|-- .../
| |-- ...
|
|-- project/
| |-- package/
| | |-- __init__.py
| | |-- module.py
| |
| |-- __init__.py
| |-- project.py
|
|-- ...
|-- ...
dans votre code (comme dans module.py) vous vous référez à l'instance logger de votre module pour enregistrer les événements à leur niveau spécifique.
une bonne convention à utiliser pour nommer les loggers est d'utiliser un logger au niveau du module, dans chaque module qui utilise la journalisation, nommé comme suit:
logger = logging.getLogger(__name__)
La variable spéciale __name__
renvoie au nom de votre module et ressemble à quelque chose comme project.package.module
selon le code de votre application structure.
module.py (et de toute autre catégorie) pourrait essentiellement ressembler à ceci:
import logging
...
log = logging.getLogger(__name__)
class ModuleClass:
def do_something(self):
log.debug('do_something() has been called!')
L'enregistreur dans chaque module propagera n'importe quel événement à l'enregistreur parent qui en retour transmet les informations à son attaché gestionnaire! De la même manière que pour la structure paquet/module python, le logger parent est déterminé par l'espace de noms en utilisant des "noms de modules en pointillés". C'est pourquoi il est préférable d'initialiser l'enregistreur avec la __name__
variable (dans l'exemple ci-dessus nom correspond à la chaîne "projet.paquet.module").
il y a deux options pour configurer le logger globalement:
Instancier un enregistreur project.py le nom
__package__
qui est égal à "projet" dans cet exemple et est donc le logger parent des loggers de tous les sous-modules. Il suffit d'ajouter un gestionnaire approprié et formateur à logger.configurer un logger avec un handler et un formatter dans le script d'exécution (comme main.py) avec le nom du paquet le plus haut.
lors du développement d'une bibliothèque qui utilise la journalisation, vous devez prendre soin de documenter comment la bibliothèque utilise la journalisation - par exemple, les noms des loggers utilisés.
le script d'exécution, comme main.py par exemple, pourrait enfin ressembler à quelque chose comme ceci:
import logging
from project import App
def setup_logger():
# create logger
logger = logging.getLogger('project')
logger.setLevel(logging.DEBUG)
# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(level)
# create formatter
formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
if __name__ == '__main__' and __package__ is None:
setup_logger()
app = App()
app.do_some_funny_stuff()
l'appel de méthode log.setLevel(...)
spécifie le message de journal de la plus faible sévérité qu'un logger va poignée mais pas nécessairement de sortie! Cela signifie simplement que le message est transmis au gestionnaire aussi longtemps que le niveau de gravité du message est supérieur (ou égal) à celui qui est défini. Mais l' gestionnaire responsable manutention le message log (par impression ou le stockage par exemple).
D'où le logging
bibliothèque offre une approche structurée et modulaire qui doit juste être exploitée en fonction de ses besoins.
Créer une instance de customLogger
dans votre module log et l'utiliser comme un singleton-il suffit d'utiliser l'instance importée, plutôt que la classe.
vous pouvez simplement lui passer une chaîne avec une sous-chaîne commune avant la première période. Les parties de la chaîne séparées par la période (".") peut être utilisé pour différentes classes / modules / fichiers / etc. Comme ainsi (en particulier le logger = logging.getLogger(loggerName)
):
def getLogger(name, logdir=LOGDIR_DEFAULT, level=logging.DEBUG, logformat=FORMAT):
base = os.path.basename(__file__)
loggerName = "%s.%s" % (base, name)
logFileName = os.path.join(logdir, "%s.log" % loggerName)
logger = logging.getLogger(loggerName)
logger.setLevel(level)
i = 0
while os.path.exists(logFileName) and not os.access(logFileName, os.R_OK | os.W_OK):
i += 1
logFileName = "%s.%s.log" % (logFileName.replace(".log", ""), str(i).zfill((len(str(i)) + 1)))
try:
#fh = logging.FileHandler(logFileName)
fh = RotatingFileHandler(filename=logFileName, mode="a", maxBytes=1310720, backupCount=50)
except IOError, exc:
errOut = "Unable to create/open log file \"%s\"." % logFileName
if exc.errno is 13: # Permission denied exception
errOut = "ERROR ** Permission Denied ** - %s" % errOut
elif exc.errno is 2: # No such directory
errOut = "ERROR ** No such directory \"%s\"** - %s" % (os.path.split(logFileName)[0], errOut)
elif exc.errno is 24: # Too many open files
errOut = "ERROR ** Too many open files ** - Check open file descriptors in /proc/<PID>/fd/ (PID: %s)" % os.getpid()
else:
errOut = "Unhandled Exception ** %s ** - %s" % (str(exc), errOut)
raise LogException(errOut)
else:
formatter = logging.Formatter(logformat)
fh.setLevel(level)
fh.setFormatter(formatter)
logger.addHandler(fh)
return logger
class MainThread:
def __init__(self, cfgdefaults, configdir, pidfile, logdir, test=False):
self.logdir = logdir
logLevel = logging.DEBUG
logPrefix = "MainThread_TEST" if self.test else "MainThread"
try:
self.logger = getLogger(logPrefix, self.logdir, logLevel, FORMAT)
except LogException, exc:
sys.stderr.write("%s\n" % exc)
sys.stderr.flush()
os._exit(0)
else:
self.logger.debug("-------------------- MainThread created. Starting __init__() --------------------")
def run(self):
self.logger.debug("Initializing ReportThreads..")
for (group, cfg) in self.config.items():
self.logger.debug(" ------------------------------ GROUP '%s' CONFIG ------------------------------ " % group)
for k2, v2 in cfg.items():
self.logger.debug("%s <==> %s: %s" % (group, k2, v2))
try:
rt = ReportThread(self, group, cfg, self.logdir, self.test)
except LogException, exc:
sys.stderr.write("%s\n" % exc)
sys.stderr.flush()
self.logger.exception("Exception when creating ReportThread (%s)" % group)
logging.shutdown()
os._exit(1)
else:
self.threads.append(rt)
self.logger.debug("Threads initialized.. \"%s\"" % ", ".join([t.name for t in self.threads]))
for t in self.threads:
t.Start()
if not self.test:
self.loop()
class ReportThread:
def __init__(self, mainThread, name, config, logdir, test):
self.mainThread = mainThread
self.name = name
logLevel = logging.DEBUG
self.logger = getLogger("MainThread%s.ReportThread_%s" % ("_TEST" if self.test else "", self.name), logdir, logLevel, FORMAT)
self.logger.info("init database...")
self.initDB()
# etc....
if __name__ == "__main__":
# .....
MainThread(cfgdefaults=options.cfgdefaults, configdir=options.configdir, pidfile=options.pidfile, logdir=options.logdir, test=options.test)