Como imprimir o traceback completo sem parar o programa?
estou a escrever um programa que analisa 10 websites, localiza ficheiros de dados, guarda os ficheiros, e depois analisa-os para fazer dados que possam ser facilmente usados na biblioteca NumPy. Existem toneladas de erros que este ficheiro encontra através de ligações más, XML mal formado, entradas em falta e outras coisas que ainda não categorizei. Inicialmente fiz este programa para lidar com erros como este:
try:
do_stuff()
except:
pass
mas agora quero registar erros:
try:
do_stuff()
except Exception, err:
print Exception, err
Note que esta é a impressão num ficheiro de registo para revisão posterior. Isto normalmente imprime dados inúteis. O que eu quero é imprimir exatamente as mesmas linhas impressas quando o erro despoleta sem a tentativa-exceto interceptar a exceção, mas eu não quero que ele pare o meu programa, uma vez que ele é aninhado em uma série de loops para que eu gostaria de ver para completar.
8 answers
Alguma outra resposta já indicou o módulotraceback .
Por favor, note que com print_exc
, em alguns casos de Canto, você não vai obter o que você poderia esperar. Em Python 2.x:
import traceback
try:
raise TypeError("Oups!")
except Exception, err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_exc()
...irá mostrar a localização do último excepção:
Traceback (most recent call last):
File "e.py", line 7, in <module>
raise TypeError("Again !?!")
TypeError: Again !?!
Se você realmente precisa acessar o original traceback uma solução é colocar o exceção infos como devolvidos exc_info
em um local variável e visualizá-la com print_exception
:
import traceback
import sys
try:
raise TypeError("Oups!")
except Exception, err:
try:
exc_info = sys.exc_info()
# do you usefull stuff here
# (potentially raising an exception)
try:
raise TypeError("Again !?!")
except:
pass
# end of useful stuff
finally:
# Display the *original* exception
traceback.print_exception(*exc_info)
del exc_info
Produzindo:
Traceback (most recent call last):
File "t.py", line 6, in <module>
raise TypeError("Oups!")
TypeError: Oups!
Mas há poucas armadilhas com isto.
-
Do doc de
sys_info
:Atribuir o valor de retorno de traceback a uma variável local numa função que está a lidar com uma excepção irá causar uma referência circular . Isto impedirá que qualquer coisa referenciada por uma variável local na mesma função ou pelo traceback seja recolhida no lixo. [...] Se você precisar do traceback, certifique-se de apagá-lo após o uso (melhor feito com uma tentativa ... declaração final)
-
Mas, do mesmo doc:
Começando com o Python 2.2, esses ciclos são automaticamente recuperados quando a coleta de lixo é ativada e eles se tornam inalcançáveis, mas permanece mais eficiente para evitar a criação de ciclos.
Por outro lado, permitindo-lhe aceder ao traceback associado a uma excepção, o Python 3 produz um resultado menos surpreendente:
import traceback
try:
raise TypeError("Oups!")
except Exception as err:
try:
raise TypeError("Again !?!")
except:
pass
traceback.print_tb(err.__traceback__)
... irá mostrar:
File "e3.py", line 4, in <module>
raise TypeError("Oups!")
traceback.format_exc()
ou sys.exc_info()
vai dar mais informações, se é isso que queres.
import traceback
import sys
try:
do_stuff()
except Exception:
print(traceback.format_exc())
# or
print(sys.exc_info()[0])
Se está a depurar e só quer ver o traço actual da pilha, pode simplesmente ligar para:
Não há necessidade de abrir uma excepção manualmente só para voltar a apanhá-la.Como imprimir a localização completa sem parar o programa?
Quando você não quer parar o seu programa com um erro, você precisa lidar com esse erro com uma tentativa / exceto:
try:
do_something_that_might_error()
except Exception as error:
handle_the_error(error)
Para extrair o traceback completo, vamos usar o módulo traceback
da biblioteca padrão:
import traceback
E criar um stacktrace decentemente complicado para demonstrar que temos o stacktrace completo:
def raise_error():
raise RuntimeError('something bad happened!')
def do_something_that_might_error():
raise_error()
Impressão
A imprimir a traceback completo, usar o método traceback.print_exc
:
try:
do_something_that_might_error()
except Exception as error:
traceback.print_exc()
Que imprime:
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
É melhor do que imprimir, registar:
No entanto, uma boa prática é ter um logger configurado para o seu módulo. Ele conhecerá o nome do módulo e será capaz de mudar os níveis (entre outros atributos, como manipuladores)
import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)
Nesse caso, vais querer a função logger.exception
em vez disso:
try:
do_something_that_might_error()
except Exception as error:
logger.exception(error)
Que registos:
ERROR:__main__:something bad happened!
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Ou talvez só queiras a corda, nesse caso ... , você vai querer a função traceback.format_exc
em vez disso:
try:
do_something_that_might_error()
except Exception as error:
logger.debug(traceback.format_exc())
Que registos:
DEBUG:__main__:Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Conclusão
E para todas as três opções, vemos que temos a mesma saída que quando temos um erro:
>>> do_something_that_might_error()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in do_something_that_might_error
File "<stdin>", line 2, in raise_error
RuntimeError: something bad happened!
Para obter o preciso stack trace, como uma string, que teria sido levantado se nenhuma tentativa / excepto se houvesse para pisar sobre ele, basta colocar isto no bloco excepto que apanha a excepção ofensiva.
desired_trace = traceback.format_exc(sys.exc_info())
Aqui está como usá-lo (assumindo que flaky_func
está definido, e log
chama o seu sistema de Registo favorito):
import traceback
import sys
try:
flaky_func()
except KeyboardInterrupt:
raise
except Exception:
desired_trace = traceback.format_exc(sys.exc_info())
log(desired_trace)
É uma boa idéia pegar e re-levantar {[[4]}, para que você ainda possa matar o programa usando Ctrl-C. loging está fora do escopo do pergunta, mas uma boa opção é registar . Documentação para os módulos etraceback .
Terá de colocar a tentativa / excepto dentro do mais interior onde o erro possa ocorrer, ou seja
for i in something:
for j in somethingelse:
for k in whatever:
try:
something_complex(i, j, k)
except Exception, e:
print e
try:
something_less_complex(i, j)
except Exception, e:
print e
... e assim por diante
Em outras palavras, você vai precisar embrulhar declarações que podem falhar em tentativa/exceto o mais específico possível, no mais interior-loop possível.
Para além da resposta de @Aaron Hall, se estiver a registar-se, mas não quiser usar {[[1]} (Dado que se regista no nível de erro), pode usar um nível mais baixo e passar exc_info=True
. por exemplo
try:
do_something_that_might_error()
except Exception:
logger.info('General exception noted.', exc_info=True)
Queres o módulo traceback. Ele vai deixar você imprimir pilha dumps como Python normalmente faz. Em particular, a função print_last irá imprimir a última excepção e um traço de pilha.