147 votes

Comment ajouter un niveau de log personnalisé à la fonction de journalisation de Python

J'aimerais utiliser loglevel TRACE (5) pour mon application, car je ne pense pas que debug() est suffisant. De plus, log(5, msg) n'est pas ce que je veux. Comment puis-je ajouter un niveau de journalisation personnalisé à un enregistreur Python?

J'ai mylogger.py avec le contenu suivant:

 import logging

@property
def log(obj):
    myLogger = logging.getLogger(obj.__class__.__name__)
    return myLogger
 

Dans mon code, je l'utilise de la manière suivante:

 class ExampleClass(object):
    from mylogger import log

    def __init__(self):
        '''The constructor with the logger'''
        self.log.debug("Init runs")
 

J'aimerais maintenant appeler self.log.trace("foo bar")

Merci d'avance pour votre aide.

204voto

pfa Points 411

@Eric S.

Eric S. la réponse est excellente, mais j'ai appris par l'expérience que ce sera toujours provoquer des messages enregistrés au nouveau niveau de débogage à être imprimés -- peu importe le niveau de journalisation est réglé. Donc, si vous faites un nouveau niveau nombre de 9, si vous appelez setLevel(50), le niveau inférieur des messages à tort être imprimé. Pour éviter cela, vous avez besoin d'une autre ligne à l'intérieur de la "debugv" fonction pour vérifier si le niveau d'enregistrement en question est effectivement activé.

Fixe exemple qui vérifie si le niveau de journalisation est activée:

import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    if self.isEnabledFor(DEBUG_LEVELV_NUM):
        self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv

Si vous regardez le code pour class Logger en logging.__init__.py pour Python 2.7, c'est ce que toutes les fonctions de log ne (.critiques, .debug, etc.).

J'ai apparemment ne peut pas envoyer des réponses aux réponses des autres pour manque de notoriété... j'espère Eric sera mise à jour de son poste si il voit cette. =)

67voto

Eric S. Points 309

J'ai pris la réponse "éviter de voir lambda" et j'ai dû modifier l'endroit où le log_at_my_log_level était ajouté. J'ai moi aussi vu le problème rencontré par Paul "Je ne pense pas que cela fonctionne. N'avez-vous pas besoin de logger comme premier argument de log_at_my_log_level?" Cela a fonctionné pour moi

 import logging
DEBUG_LEVELV_NUM = 9 
logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV")
def debugv(self, message, *args, **kws):
    # Yes, logger takes its '*args' as 'args'.
    self._log(DEBUG_LEVELV_NUM, message, args, **kws) 
logging.Logger.debugv = debugv
 

43voto

Wisperwind Points 21

Cette question est assez ancienne, mais je viens de traiter du même sujet et de trouver un moyen similaire à ceux déjà mentionnés qui me parait un peu plus propre. Ceci a été testé sur 3.4, donc je ne suis pas sûr que les méthodes utilisées existent dans les versions antérieures:

 from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET

VERBOSE = 5

class MyLogger(getLoggerClass()):
    def __init__(self, name, level=NOTSET):
        super().__init__(name, level)

        addLevelName(VERBOSE, "VERBOSE")

    def verbose(self, msg, *args, **kwargs):
        if self.isEnabledFor(VERBOSE):
            self._log(VERBOSE, msg, args, **kwargs)

setLoggerClass(MyLogger)
 

20voto

schlamar Points 3108

Qui a commencé la mauvaise pratique consistant à utiliser des méthodes internes ( self._log ) et pourquoi chaque réponse est-elle basée sur cela?! La solution pythonique consisterait à utiliser self.log afin que vous n'ayez pas à vous soucier de choses internes:

 import logging

SUBDEBUG = 5
logging.addLevelName(SUBDEBUG, 'SUBDEBUG')

def subdebug(self, message, *args, **kws):
    self.log(SUBDEBUG, message, *args, **kws) 
logging.Logger.subdebug = subdebug

logging.basicConfig()
l = logging.getLogger()
l.setLevel(SUBDEBUG)
l.subdebug('test')
l.setLevel(logging.DEBUG)
l.subdebug('test')
 

8voto

LtPinback Points 363

Je trouve plus facile de créer un nouvel attribut pour l'objet logger qui transmet la fonction log (). Je pense que le module de journalisation fournit le addLevelName () et le log () pour cette raison même. Ainsi, aucune sous-classe ou nouvelle méthode n'est nécessaire.

 import logging

@property
def log(obj):
    logging.addLevelName(5, 'TRACE')
    myLogger = logging.getLogger(obj.__class__.__name__)
    setattr(myLogger, 'trace', lambda *args: myLogger.log(5, *args))
    return myLogger
 

maintenant

 mylogger.trace('This is a trace message')
 

devrait fonctionner comme prévu.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X