print - ¿Cómo puedo registrar la línea actual y apilar información con Python?
traceback python 3 (8)
Tengo la función de registro de la siguiente manera.
logging.basicConfig(
filename = fileName,
format = "%(levelname) -10s %(asctime)s %(message)s",
level = logging.DEBUG
)
def printinfo(string):
if DEBUG:
logging.info(string)
def printerror(string):
if DEBUG:
logging.error(string)
print string
Necesito iniciar sesión en el número de línea, información de la pila. Por ejemplo:
1: def hello():
2: goodbye()
3:
4: def goodbye():
5: printinfo()
---> Line 5: goodbye()/hello()
¿Cómo puedo hacer esto con Python?
Resuelto
def printinfo(string):
if DEBUG:
frame = inspect.currentframe()
stack_trace = traceback.format_stack(frame)
logging.debug(stack_trace[:-1])
if LOG:
logging.info(string)
Me da esta información que es exactamente lo que necesito.
DEBUG 2011-02-23 10:09:13,500 [
'' File "/abc.py", line 553, in <module>/n runUnitTest(COVERAGE, PROFILE)/n'',
'' File "/abc.py", line 411, in runUnitTest/n printinfo(string)/n'']
A partir de Python 3.2, esto se puede simplificar al pasar el stack_info=True
a las llamadas de registro . Sin embargo, deberá usar una de las respuestas anteriores para cualquier versión anterior.
Aquí hay un ejemplo que espero te pueda ayudar:
import inspect
import logging
logging.basicConfig(
format = "%(levelname) -10s %(asctime)s %(message)s",
level = logging.DEBUG
)
def test():
caller_list = []
frame = inspect.currentframe()
this_frame = frame # Save current frame.
while frame.f_back:
caller_list.append(''{0}()''.format(frame.f_code.co_name))
frame = frame.f_back
caller_line = this_frame.f_back.f_lineno
callers = ''/''.join(reversed(caller_list))
logging.info(''Line {0} : {1}''.format(caller_line, callers))
def foo():
test()
def bar():
foo()
bar()
Resultado:
INFO 2011-02-23 17:03:26,426 Line 28 : bar()/foo()/test()
El nombre de la función actual, el módulo y el número de línea que puede hacer simplemente cambiando la cadena de formato para incluirlos.
logging.basicConfig(
filename = fileName,
format = "%(levelname) -10s %(asctime)s %(module)s:%(lineno)s %(funcName)s %(message)s",
level = logging.DEBUG
)
La mayoría de las personas solo quieren la pila cuando registran una excepción, y el módulo de registro lo hace automáticamente si llama a logging.exception()
. Si realmente desea información de la pila en otros momentos, deberá utilizar el módulo de rastreo para extraer la información adicional que necesita.
Esto se basa en la respuesta de @mouad pero se vuelve más útil (IMO) al incluir en cada nivel el nombre del archivo (pero no su ruta completa) y el número de línea de la pila de llamadas, y al dejar la pila en la forma más reciente llamada (from) es decir, NO revertido) orden porque así es como quiero leerlo :-)
Cada entrada tiene file: line: func () que es la misma secuencia que el stacktrace normal, pero todo en la misma línea es mucho más compacto.
import inspect
def callers(self):
caller_list = []
frame = inspect.currentframe()
while frame.f_back:
caller_list.append(''{2}:{1}:{0}()''.format(frame.f_code.co_name,frame.f_lineno,frame.f_code.co_filename.split("//")[-1]))
frame = frame.f_back
callers = '' <= ''.join(caller_list)
return callers
Es posible que deba agregar un f_back adicional si tiene alguna llamada intermedia para producir el texto de registro.
frame = inspect.currentframe().f_back
Produce una salida como esta:
file2.py:620:func1() <= file3.py:211:func2() <= file3.py:201:func3() <= main.py:795:func4() <= file4.py:295:run() <= main.py:881:main()
Solo necesito este stacktrace en dos funciones clave, así que agrego la salida de los que llaman al texto en la llamada logger.debug (), como htis:
logger.debug("/nWIRE: justdoit request -----/n"+callers()+"/n/n")
Mira el módulo de rastreo
>>> import traceback
>>> def test():
>>> print "/".join( str(x[2]) for x in traceback.extract_stack() )
>>> def main():
>>> test()
>>> main()
<module>/launch_new_instance/mainloop/mainloop/interact/push/runsource/runcode/<module>/main/test
Respuesta tardía, pero bueno.
Otra solución es que puede crear su propio formateador con un filtro como se especifica en los documentos here . Esta es una característica realmente excelente, ya que ahora ya no tiene que usar una función auxiliar (y tiene que poner la función auxiliar en todos los lugares donde desea que se realice el seguimiento de la pila). En su lugar, un formato personalizado lo implementa directamente en los propios registros.
import logging
class ContextFilter(logging.Filter):
def __init__(self, trim_amount)
self.trim_amount = trim_amount
def filter(self, record):
import traceback
record.stack = ''''.join(
str(row) for row in traceback.format_stack()[:-self.trim_amount]
)
return True
# Now you can create the logger and apply the filter.
logger = logging.getLogger(__name__)
logger.addFilter(ContextFilter(5))
# And then you can directly implement a stack trace in the formatter.
formatter = logging.Formatter(''%(asctime)s %(levelname)s %(message)s /n %(stack)s'')
Nota: En el código anterior, recorte los últimos 5 marcos de pila. Esto es solo por conveniencia y para que no mostremos los marcos de pila del propio paquete de registro de Python (también es posible que deba ajustarse para diferentes versiones del paquete de registro).
Utilice el módulo de traceback .
logging.error(traceback.format_exc())
import inspect
import traceback
def method():
frame = inspect.currentframe()
stack_trace = traceback.format_stack(frame)
print ''''.join(stack_trace)
Utilice stack_trace [: - 1] para evitar incluir method / printinfo en el seguimiento de la pila.