2011-02-10 11 views
5

Hola
Me gustaría extender mi registrador (tomada por logging.getLogger ("rrcheck")) con mis propios métodos como: def warnpfx(...): Cómo escribir propios métodos de registro para propios niveles de registro

cómo hacerlo lo mejor?

Mi deseo original es tener un registrador de la raíz escribir todo a un archivo y, además, nombrado registrador ("rrcheck") escrito por la salida estándar, pero éste también debe tener algunos otros métodos y niveles. Necesito preceder algunos mensajes con el prefijo "! PFXWRN" (pero solo aquellos que van a stdout) y dejar otros mensajes sin cambios. También me gustaría configurar el nivel de registro por separado para root y para el registrador con nombre.

Este es mi código:

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() 

Y esto es una salida - la función y el número lilne es falso: warnpfx en lugar de prueba

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 

Tal vez mi propio enfoque Logger es no el mejor?
¿Qué dirección propondrías ir (propio registrador, manejador propio, formateador propio, etc.)?

¿Cómo proceder si quisiera tener otro registrador?
Desafortunadamente registro no tiene la posibilidad de registrar un registrador propia, por lo que entonces getLogger (nombre) tomaría un ser requerida ...

Saludos,
Zbigniew

Respuesta

4

Si marca Python sources, verá que El culpable es el método Logger.findCaller que recorre la pila de llamadas y busca una primera línea que no está en el archivo logging.py. Debido a esto, su llamada personalizada al self.log en CheloExtendedLogger.warnpfx registra una línea incorrecta.

Por desgracia, el código en logging.py no es muy modular, por lo que la solución es bastante feo: hay que redefinir el método findCaller sí mismo en su subclase, de modo que tome en cuenta tanto el archivo logging.py y el archivo en el que su el registrador reside (tenga en cuenta que no debe haber ningún código que no sea el registrador en su archivo, o de nuevo los resultados serán inexactos). Esto requiere un cambio de una línea en el cuerpo del método:

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 

Para que esto funcione, es necesario definir su propio _srcfile variable en su archivo. Una vez más, logging.py no utiliza una función, sino que pone todo el código en el nivel de módulo, por lo que tienes que copiar y pegar de nuevo:

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) 

Bueno, tal vez si no le importa para las versiones compiladas, dos últimas líneas serán suficientes.

Ahora, el código funciona como se esperaba:

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 

En cuanto a múltiples clases de registrador, si no le importa la dependencia entre un nombre y una clase registrador registrador, se puede hacer una subclase de logging.Logger que delegaría sus llamadas a una clase de registrador apropiada, según su nombre. Probablemente haya otras posibilidades más elegantes, pero no puedo pensar en ninguna ahora.

+0

Gracias DzinX. Hice algunos intentos más y parece que llamar a self._log en lugar de self.log lo hace funcionar. – Zbigniew

+0

Aaah, eso es porque 'currentframe()' devuelve la cuarta función superior en la pila de llamadas :) Qué feo de ellos :) – DzinX

Cuestiones relacionadas