most - traceback python 3
Obtener el rastreo completo (4)
Como respondió mechmind, el seguimiento de la pila consiste solo en cuadros entre el sitio donde se generó la excepción y el sitio del bloque try
. Si necesita la traza de pila completa, aparentemente no tiene suerte.
Excepto que, obviamente, es posible extraer las entradas de la pila desde el nivel superior al marco actual: traceback.extract_stack
maneja bien. El problema es que la información obtenida por traceback.extract_stack
proviene de la inspección directa de los marcos de la pila sin crear un objeto de rastreo en ningún punto, y la API de logging
requiere que un objeto de rastreo afecte la salida de rastreo.
Afortunadamente, el logging
no requiere un objeto de rastreo real , requiere un objeto que pueda pasar a las rutinas de formato del módulo de traceback
. traceback
tampoco le importa, solo usa dos atributos de la traza de retorno, el marco y el número de línea. Por lo tanto, debería ser posible crear una lista enlazada de objetos falsos de rastreo de tipo pato y pasarla como el rastreo.
import sys
class FauxTb(object):
def __init__(self, tb_frame, tb_lineno, tb_next):
self.tb_frame = tb_frame
self.tb_lineno = tb_lineno
self.tb_next = tb_next
def current_stack(skip=0):
try: 1/0
except ZeroDivisionError:
f = sys.exc_info()[2].tb_frame
for i in xrange(skip + 2):
f = f.f_back
lst = []
while f is not None:
lst.append((f, f.f_lineno))
f = f.f_back
return lst
def extend_traceback(tb, stack):
"""Extend traceback with stack info."""
head = tb
for tb_frame, tb_lineno in stack:
head = FauxTb(tb_frame, tb_lineno, head)
return head
def full_exc_info():
"""Like sys.exc_info, but includes the full traceback."""
t, v, tb = sys.exc_info()
full_tb = extend_traceback(tb, current_stack(1))
return t, v, full_tb
Con estas funciones implementadas, su código solo requiere una modificación trivial:
import logging
def func():
try:
raise Exception(''Dummy'')
except:
logging.error("Something awful happened!", exc_info=full_exc_info())
def func2():
func()
func2()
... para dar el resultado esperado:
ERROR:root:Something awful happened!
Traceback (most recent call last):
File "a.py", line 52, in <module>
func2()
File "a.py", line 49, in func2
func()
File "a.py", line 43, in func
raise Exception(''Dummy'')
Exception: Dummy
Tenga en cuenta que los objetos faux-traceback son totalmente utilizables para la introspección (mostrar variables locales o como argumento para pdb.post_mortem()
porque contienen referencias a marcos de pila reales.
¿Cómo puedo obtener un seguimiento completo en el siguiente caso, incluidas las llamadas de funciones func2
y func
?
import traceback
def func():
try:
raise Exception(''Dummy'')
except:
traceback.print_exc()
def func2():
func()
func2()
Cuando ejecuto esto, me sale:
Traceback (most recent call last):
File "test.py", line 5, in func
raise Exception(''Dummy'')
Exception: Dummy
traceback.format_stack()
no es lo que quiero, ya que necesito que el objeto de traceback
se pase a un módulo de terceros.
Estoy particularmente interesado en este caso:
import logging
def func():
try:
raise Exception(''Dummy'')
except:
logging.exception("Something awful happened!")
def func2():
func()
func2()
En este caso estoy recibiendo:
ERROR:root:Something awful happened!
Traceback (most recent call last):
File "test.py", line 9, in func
raise Exception(''Dummy'')
Exception: Dummy
El seguimiento de la pila se recopila cuando la excepción aumenta. Así que debes imprimir el rastreo en la parte superior de la pila deseada:
import traceback
def func():
raise Exception(''Dummy'')
def func2():
func()
try:
func2()
except:
traceback.print_exc()
Hay algo más de información que se puede extraer del rastreo, y a veces prefiero una información más "clara" y más lógica en lugar del blob multilínea con archivos, números de línea y fragmentos de código dado por el rastreo. Preferiblemente una línea debe decir todo lo esencial.
Para lograr esto utilizo la siguiente función:
def raising_code_info():
code_info = ''''
try:
frames = inspect.trace()
if(len(frames)):
full_method_name = frames[0][4][0].rstrip(''/n/r'').strip()
line_number = frames[1][2]
module_name = frames[0][0].f_globals[''__name__'']
if(module_name == ''__main__''):
module_name = os.path.basename(sys.argv[0]).replace(''.py'','''')
class_name = ''''
obj_name_dot_method = full_method_name.split(''.'', 1)
if len(obj_name_dot_method) > 1:
obj_name, full_method_name = obj_name_dot_method
try:
class_name = frames[0][0].f_locals[obj_name].__class__.__name__
except:
pass
method_name = module_name + ''.''
if len(class_name) > 0:
method_name += class_name + ''.''
method_name += full_method_name
code_info = ''%s, line %d'' % (method_name, line_number)
finally:
del frames
sys.exc_clear()
return code_info
Da . y número de línea, por ejemplo:
(ejemplo de nombre del módulo: test.py):
(line 73:)
def function1():
print 1/0
class AClass(object):
def method2(self):
a = []
a[3] = 1
def try_it_out():
# try it with a function
try:
function1()
except Exception, what:
print ''%s: /"%s/"'' % (raising_code_info(), what)
# try it with a method
try:
my_obj_name = AClass()
my_obj_name.method2()
except Exception, what:
print ''%s: /"%s/"'' % (raising_code_info(), what)
if __name__ == ''__main__'':
try_it_out()
test.function1(), line 75: "integer division or modulo by zero"
test.AClass.method2(), line 80: "list assignment index out of range"
Lo que podría ser un poco más ordenado en algunos casos de uso.
He escrito un módulo que escribe un rastreo más completo.
El módulo está here documentación es docs
(También puede obtener el módulo de pypi
sudo pip install pd
)
Para atrapar y pring excepciones haga lo siguiente:
import pd
try:
<python code>
except BaseException:
pd.print_exception_ex( follow_objects = 1 )
La traza de pila se parece a esta aquí:
Exception: got it
#1 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 1) at t test_pd.py:29
Calls next frame at:
raise Exception(''got it'') at: test_pd.py:29
#2 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 2) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#3 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 3) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#4 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 4) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#5 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 5) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#6 def kuku2(self = {''a'': 42, ''b'': [1, 2, 3, 4]}, depth = 6) at test_pd.py:28
Calls next frame at:
self.kuku2( depth - 1 ) at: test_pd.py:28
#7 def main() at test_pd.py:44
Local variables:
n = {''a'': 42, ''b'': [1, 2, 3, 4]}
Calls next frame at:
pd.print_exception_ex( follow_objects = 1 ) at: test_pd.py:44
follow_objects = 0 no imprimirá el contenido del objeto (con estructuras de datos complejas, follow_objects puede llevar mucho tiempo).