Как распечатать полную трассировку без остановки программы? - PullRequest
628 голосов
/ 13 сентября 2010

Я пишу программу, которая анализирует 10 веб-сайтов, находит файлы данных, сохраняет файлы, а затем анализирует их, чтобы получить данные, которые можно легко использовать в библиотеке NumPy.Есть тонн ошибок, с которыми этот файл сталкивается из-за плохих ссылок, плохо сформированного XML, отсутствующих записей и других вещей, которые мне еще предстоит классифицировать.Первоначально я создал эту программу для обработки ошибок, подобных этой:

try:
    do_stuff()
except:
    pass

Но теперь я хочу регистрировать ошибки:

try:
    do_stuff()
except Exception, err:
    print Exception, err

Обратите внимание, что это печать в файл журнала для последующего просмотра.Это обычно печатает очень бесполезные данные.Я хочу напечатать точно такие же строки, которые печатаются, когда ошибка срабатывает без попытки, за исключением перехвата исключения, но я не хочу, чтобы это останавливало мою программу, поскольку она вложена в серию циклов for, которые я хотел бысмотреть до завершения.

Ответы [ 12 ]

626 голосов
/ 13 сентября 2010

traceback.format_exc() или sys.exc_info() даст больше информации, если вы этого хотите.

import traceback
import sys

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    # or
    print(sys.exc_info()[0])
445 голосов
/ 05 июня 2013

В другом ответе уже указан модуль traceback .

Обратите внимание, что с print_exc в некоторых угловых случаях вы не получите того, чего ожидаете. В Python 2.x:

import traceback

try:
    raise TypeError("Oups!")
except Exception, err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_exc()

... будет отображать трассировку последнего исключения:

Traceback (most recent call last):
  File "e.py", line 7, in <module>
    raise TypeError("Again !?!")
TypeError: Again !?!

Если вам действительно необходим доступ к исходной трассировке , одним из решений является кэширование информации об исключении , возвращенной из exc_info в локальной переменной и отобразите ее, используя 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

Производство:

Traceback (most recent call last):
  File "t.py", line 6, in <module>
    raise TypeError("Oups!")
TypeError: Oups!

Мало ловушек с этим, хотя:

  • Из документа sys_info:

    Присвоение возвращаемого значения traceback локальной переменной в функции, которая обрабатывает исключение, вызовет циклическую ссылку . Это предотвратит сбор мусора, на который ссылается локальная переменная в той же функции или трассировка. [...] Если вам нужна трассировка, обязательно удалите ее после использования (лучше всего делать с помощью оператора try ... finally)

  • но из того же документа:

    Начиная с Python 2.2, такие циклы автоматически восстанавливаются , когда сборка мусора включена и становится недоступной, но при этом остается более эффективным избегать создания циклов.


С другой стороны, предоставляя вам доступ к трассировке , связанной с исключением, Python 3 дает менее удивительный результат:

import traceback

try:
    raise TypeError("Oups!")
except Exception as err:
    try:
        raise TypeError("Again !?!")
    except:
        pass

    traceback.print_tb(err.__traceback__)

... будет отображаться:

  File "e3.py", line 4, in <module>
    raise TypeError("Oups!")
203 голосов
/ 29 апреля 2015

Если вы отлаживаете и хотите видеть текущую трассировку стека, вы можете просто позвонить:

traceback.print_stack()

Нет необходимости вручную вызывать исключение, чтобы просто перехватить его.

78 голосов
/ 16 июля 2015

Как напечатать полный текст трассировки, не останавливая программу?

Если вы не хотите останавливать вашу программу из-за ошибки, вам нужно обработать эту ошибку с помощью команды try /кроме:

try:
    do_something_that_might_error()
except Exception as error:
    handle_the_error(error)

Для извлечения полной трассировки мы будем использовать модуль traceback из стандартной библиотеки:

import traceback

И создать довольно сложную трассировку стека, чтобы продемонстрировать, чтомы получаем полную трассировку стека:

def raise_error():
    raise RuntimeError('something bad happened!')

def do_something_that_might_error():
    raise_error()

Печать

Для печать полная трассировка, используйте метод traceback.print_exc:

try:
    do_something_that_might_error()
except Exception as error:
    traceback.print_exc()

Что печатает:

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!

Лучше, чем печать, ведение журнала:

Тем не менее, рекомендуется настроить регистратор для вашего модуля.Он будет знать имя модуля и сможет изменять уровни (среди других атрибутов, таких как обработчики)

import logging
logging.basicConfig(level=logging.DEBUG)
logger = logging.getLogger(__name__)

В этом случае вам понадобится функция logger.exception вместо:

try:
    do_something_that_might_error()
except Exception as error:
    logger.exception(error)

Какие журналы:

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!

Или, возможно, вам просто нужна строка, и в этом случае вам понадобится функция traceback.format_exc вместо:

try:
    do_something_that_might_error()
except Exception as error:
    logger.debug(traceback.format_exc())

Какие журналы:

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!

Заключение

И для всех трех вариантов мы видим, что мы получаем тот же вывод, что и при появлении ошибки:

>>> 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!
12 голосов
/ 14 декабря 2018

Во-первых, не используйте print s для ведения журнала, для этого есть нестабильный, проверенный и продуманный модуль stdlib: logging.Вы определенно должны использовать его вместо этого.

Во-вторых, не поддавайтесь искушению сделать беспорядок с несвязанными инструментами, когда есть нативный и простой подход.Вот оно:

log = logging.getLogger(__name__)

try:
    call_code_that_fails()
except MyError:
    log.exception('Any extra info you want to see in your logs')

Вот и все.Вы закончили.

Объяснение для всех, кому интересно, как все работает под капотом

То, что на самом деле делает log.exception, это просто вызов log.error (то есть logсобытие с уровнем ERROR) и печать трассировки затем.

Почему это лучше?

Ну, вот некоторые соображения:

  • это просто право ;
  • это просто;
  • это просто.

Почему никто не должен использовать traceback или вызыватьлоггер с exc_info=True или пачкают руки с sys.exc_info?

ну просто потому что!Все они существуют для разных целей.Например, вывод traceback.print_exc немного отличается от трассировки, созданной самим интерпретатором.Если вы используете его, вы запутаете любого, кто читает ваши журналы, они будут бить себя по голове.

Передача exc_info=True для регистрации звонков просто неуместна. Но , это полезно при отлове восстанавливаемых ошибок, и вы также хотите их регистрировать (используя, например, INFO уровень) с помощью трассировок, поскольку log.exception создает журналы только одного уровня - ERROR.

И вам определенно следует избегать путаницы с sys.exc_info как можно больше.Это просто не общедоступный интерфейс, это внутренний интерфейс - вы можете использовать , если вы точно знаете, что делаете.Он не предназначен только для печати исключений.

7 голосов
/ 24 апреля 2018

В дополнение к ответу @Aaron Hall, если вы регистрируетесь, но не хотите использовать logging.exception() (поскольку он регистрирует на уровне ОШИБКИ), вы можете использовать более низкий уровень и передать exc_info=True.например,

try:
    do_something_that_might_error()
except Exception:
    logger.info('General exception noted.', exc_info=True)
6 голосов
/ 15 ноября 2015

Чтобы получить точную трассировку стека , в виде строки, что было бы было бы поднято, если бы не было попытки / кроме того, чтобы перешагнуть через него, просто поместите это в блок исключений что ловит оскорбительное исключение.

desired_trace = traceback.format_exc(sys.exc_info())

Вот как это использовать (при условии, что flaky_func определено, а log вызывает вашу любимую систему регистрации):

import traceback
import sys

try:
    flaky_func()
except KeyboardInterrupt:
    raise
except Exception:
    desired_trace = traceback.format_exc(sys.exc_info())
    log(desired_trace)

Это хорошая идея, чтобы поймать и повторно поднять KeyboardInterrupt s, так что вы все равно можете убить программу, используя Ctrl-C. Ведение журнала выходит за рамки вопроса, но хорошим вариантом является logging . Документация для модулей sys и traceback .

6 голосов
/ 13 сентября 2010

Вам нужно будет поместить попытку / исключение в самую внутреннюю петлю, где может возникнуть ошибка, т.е.

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

... и т. Д.

Другими словами, вам нужно будет обернуть операторы, которые могут потерпеть неудачу при попытке /, за исключением как можно более конкретных, в максимально возможной внутренней петле.

5 голосов
/ 06 ноября 2018

Замечание по поводу этого ответа комментарии: print(traceback.format_exc()) делает для меня лучшую работу, чем traceback.print_exc(). С последним hello иногда странным образом «смешивается» с текстом трассировки, например, если оба хотят одновременно писать в stdout или stderr, производя странный вывод (по крайней мере, при сборке из текстового редактора и просмотре вывод на панель «Результаты сборки»).

Traceback (последний последний вызов):
Файл "C: \ Users \ User \ Desktop \ test.py", строка 7, в
ад do_stuff ()
Файл "C: \ Users \ User \ Desktop \ test.py", строка 4, в do_stuff
1/0
ZeroDivisionError: целочисленное деление или по модулю на ноль
о
[Закончено в 0,1 с]

Поэтому я использую:

import traceback, sys

def do_stuff():
    1/0

try:
    do_stuff()
except Exception:
    print(traceback.format_exc())
    print('hello')
3 голосов
/ 13 сентября 2010

Требуется модуль traceback . Это позволит вам печатать дампы стека, как это обычно делает Python. В частности, функция print_last напечатает последнее исключение и трассировку стека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...