2011-02-10 3 views
5

Salut
Je voudrais prolonger mon enregistreur (prise par logging.getLogger (« rrcheck »)) avec mes propres méthodes telles que: def warnpfx(...): Comment écrire propres méthodes d'exploitation pour propres niveaux de journalisation

Comment faire le meilleur?

Mon souhait original est d'avoir un enregistreur racine écrit tout dans un fichier et en plus nommé logger ("rrcheck") écrivant à stdout, mais ce dernier devrait également avoir d'autres méthodes et niveaux. J'en ai besoin pour préfixer certains messages avec le préfixe "! PFXWRN" (mais seulement ceux qui vont sur stdout) et laisser les autres messages inchangés. Je voudrais également définir le niveau de journalisation séparément pour root et pour l'enregistreur nommé.

Ceci est mon code:

class CheloExtendedLogger(logging.Logger): 
    """ 
    Custom logger class with additional levels and methods 
    """ 
    WARNPFX = logging.WARNING+1 

    def __init__(self, name): 
     logging.Logger.__init__(self, name, logging.DEBUG)     

     logging.addLevelName(self.WARNPFX, 'WARNING') 

     console = logging.StreamHandler() 
     console.setLevel(logging.DEBUG) 
     # create formatter and add it to the handlers 
     formatter = logging.Formatter("%(asctime)s [%(funcName)s: %(filename)s,%(lineno)d] %(message)s") 
     console.setFormatter(formatter) 

     # add the handlers to logger 
     self.addHandler(console) 

     return 

    def warnpfx(self, msg, *args, **kw): 
     self.log(self.WARNPFX, "! PFXWRN %s" % msg, *args, **kw) 


logging.setLoggerClass(CheloExtendedLogger)  
rrclogger = logging.getLogger("rrcheck") 
rrclogger.setLevel(logging.INFO) 

def test(): 
    rrclogger.debug("DEBUG message") 
    rrclogger.info("INFO message") 
    rrclogger.warnpfx("warning with prefix") 

test() 

Et ceci est une sortie - fonction et le numéro de lilne est erroné: warnpfx au lieu de essai

2011-02-10 14:36:51,482 [test: log4.py,35] INFO message 
2011-02-10 14:36:51,497 [warnpfx: log4.py,26] ! PFXWRN warning with prefix 

Peut-être ma propre approche de l'enregistreur est pas le meilleur?
Dans quelle direction iriez-vous (propre enregistreur, propre gestionnaire, propre formateur, etc.)?

Comment procéder si je souhaite avoir un autre enregistreur?
exploitation forestière Unfortunatelly n'a pas la possibilité d'enregistrer un enregistreur propre, donc alors getLogger (nom) prendrait une demande ...

Cordialement,
Zbigniew

Répondre

4

Si vous cochez Python sources, vous verrez que le coupable est la méthode Logger.findCaller qui parcourt la pile des appels et recherche une première ligne qui ne figure pas dans le fichier logging.py. Pour cette raison, votre appel personnalisé à self.log dans CheloExtendedLogger.warnpfx enregistre une ligne incorrecte.

Malheureusement, le code logging.py n'est pas très modulaire, le correctif est assez laid: vous devez redéfinir la même méthode findCaller dans votre sous-classe, pour qu'il prenne en compte à la fois le fichier logging.py et le fichier dans lequel votre logger réside (notez qu'il ne devrait pas y avoir de code autre que l'enregistreur dans votre fichier, ou encore les résultats seront inexacts). Cela nécessite un changement d'une ligne dans le corps de la méthode:

class CheloExtendedLogger(logging.Logger): 

    [...] 

    def findCaller(self): 
     """ 
     Find the stack frame of the caller so that we can note the source 
     file name, line number and function name. 
     """ 
     f = logging.currentframe().f_back 
     rv = "(unknown file)", 0, "(unknown function)" 
     while hasattr(f, "f_code"): 
      co = f.f_code 
      filename = os.path.normcase(co.co_filename) 
      if filename in (_srcfile, logging._srcfile): # This line is modified. 
       f = f.f_back 
       continue 
      rv = (filename, f.f_lineno, co.co_name) 
      break 
     return rv 

Pour que cela fonctionne, vous devez définir votre propre _srcfile variable dans votre fichier. Encore une fois, logging.py ne pas utiliser une fonction, mais met plutôt tout le code au niveau du module, vous devez copier-coller à nouveau:

if hasattr(sys, 'frozen'): #support for py2exe 
    _srcfile = "logging%s__init__%s" % (os.sep, __file__[-4:]) 
elif string.lower(__file__[-4:]) in ['.pyc', '.pyo']: 
    _srcfile = __file__[:-4] + '.py' 
else: 
    _srcfile = __file__ 
_srcfile = os.path.normcase(_srcfile) 

Eh bien, peut-être si vous ne vous inquiétez pas pour les versions compilées, deux dernières lignes suffiront.

Votre code fonctionne comme prévu:

2011-02-10 16:41:48,108 [test: lg.py,16] INFO message 
2011-02-10 16:41:48,171 [test: lg.py,17] ! PFXWRN warning with prefix 

Comme pour les classes d'enregistreur multiples, si cela ne vous dérange pas la dépendance entre un nom de l'enregistreur et une classe de l'enregistreur, vous pouvez faire une sous-classe de logging.Logger que déléguerait ses appels à une classe logger appropriée, en fonction de son nom. Il y a probablement d'autres possibilités, plus élégantes, mais je ne peux pas y penser maintenant.

+0

Merci DzinX. J'ai fait quelques essais et j'ai l'impression d'appeler self._log au lieu de self.log. – Zbigniew

+0

Aaah, c'est parce que 'currentframe()' renvoie la quatrième fonction supérieure sur la pile d'appel :) Quelle laideur d'entre eux :) – DzinX

Questions connexes