Автор оригинала: Doug Hellmann.
Цель:
cgitb предоставляет более подробную информацию о трассировке, чем
проследить
cgitb
– ценный инструмент отладки в стандартной библиотеке. Первоначально он был разработан для отображения ошибок и отладочной информации в веб-приложениях, а затем был обновлен и теперь включает также вывод в виде обычного текста, но, к сожалению, никогда не был переименован. Это привело к неизвестности, и модуль не используется так часто, как мог бы.
Стандартные дампы трассировки
Поведение Python по умолчанию при обработке исключений заключается в том, чтобы напечатать обратную трассировку в стандартный поток вывода ошибок со стеком вызовов, ведущим к позиции ошибки. Этот основной вывод часто содержит достаточно информации, чтобы понять причину исключения и разрешить исправление.
cgitb_basic_traceback.py
def func2(a, divisor): return a / divisor def func1(a, b): c b - 5 return func2(a, c) func1(1, 5)
В этом примере программы есть небольшая ошибка в func2 ()
.
$ python3 cgitb_basic_traceback.py Traceback (most recent call last): File "cgitb_basic_traceback.py", line 18, infunc1(1, 5) File "cgitb_basic_traceback.py", line 16, in func1 return func2(a, c) File "cgitb_basic_traceback.py", line 11, in func2 return a / divisor ZeroDivisionError: division by zero
Включение подробной трассировки
Хотя базовая трассировка содержит достаточно информации, чтобы обнаружить ошибку, включение cgitb
дает более подробную информацию. cgitb
заменяет sys.excepthook
функцией, которая обеспечивает расширенную обратную трассировку.
cgitb_local_vars.py
import cgitb cgitb.enable(format'text')
Отчет об ошибке из этого примера намного шире оригинала. Перечислен каждый кадр стека, а также:
- Полный путь к исходному файлу, а не только базовое имя
- Значения аргументов каждой функции в стеке
- Несколько строк исходного контекста вокруг строки в пути ошибки
- Значения переменных в выражении, вызывающем ошибку
Наличие доступа к переменным, участвующим в стеке ошибок, может помочь найти логическую ошибку, которая возникает где-то выше в стеке, чем строка, в которой генерируется фактическое исключение.
$ python3 cgitb_local_vars.py ZeroDivisionError Python 3.7.1: .../bin/python3 Sun Dec 9 10:46:17 2018 A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. .../cgitb_local_vars.py in() 18 def func1(a, b): 19 c = b - 5 20 return func2(a, c) 21 22 func1(1, 5) func1 = .../cgitb_local_vars.py in 18 def func1(a, b): 19 c = b - 5 20 return func2(a, c) 21 22 func1(1, 5) global func2 = a = 1 c = 0 .../cgitb_local_vars.py in 13 14 def func2(a, divisor): 15 return a / divisor 16 17 a = 1 divisor = 0 ZeroDivisionError: division by zero __cause__ = None __class__ = __context__ = None __delattr__ = __dict__ = {} __dir__ = __doc__ = 'Second argument to a division or modulo operation was zero.' __eq__ = __format__ = __ge__ = __getattribute__ = __gt__ = __hash__ = __init__ = __init_subclass__ = __le__ = __lt__ = __ne__ = __new__ = __reduce__ = __reduce_ex__ = __repr__ = __setattr__ = __setstate__ = __sizeof__ = __str__ = __subclasshook__ = __suppress_context__ = False __traceback__ = args = ('division by zero',) with_traceback = The above is a description of an error in a Python program. Here is the original traceback: Traceback (most recent call last): File "cgitb_local_vars.py", line 22, in func1(1, 5) File "cgitb_local_vars.py", line 20, in func1 return func2(a, c) File "cgitb_local_vars.py", line 15, in func2 return a / divisor ZeroDivisionError: division by zero
В случае этого кода с ZeroDivisionError
очевидно, что проблема возникает при вычислении значения c
в func1 ()
, а не там, где значение используется в func2 ()
.
Конец вывода также включает полные сведения об объекте исключения (в случае, если у него есть атрибуты, отличные от message
, которые могут быть полезны для отладки) и исходную форму дампа трассировки.
Локальные переменные в трассировке
Код в cgitb
, который проверяет переменные, используемые в кадре стека, приводящие к ошибке, достаточно умен, чтобы оценивать атрибуты объекта и отображать их.
cgitb_with_classes.py
import cgitb cgitb.enable(format'text', context12) class BrokenClass: """This class has an error. """ def __init__(self, a, b): """Be careful passing arguments in here. """ self.a a self.b b self.c self.a * self.b # Really # long # comment # goes # here. self.d self.a / self.b return o BrokenClass(1, 0)
Если функция или метод включает в себя много встроенных комментариев, пробелов или другого кода, который делает их очень длинными, то наличие по умолчанию пяти строк контекста может не обеспечить достаточного направления. Когда тело функции выталкивается из отображаемого окна кода, не хватает контекста, чтобы понять местоположение ошибки. Эту проблему решает использование большего значения контекста с cgitb
. Передача целого числа в качестве аргумента context
в enable ()
контролирует количество кода, отображаемого для каждой строки трассировки.
Эти выходные данные показывают, что self.a
и self.b
участвуют в коде, подверженном ошибкам.
$ python3 cgitb_with_classes.py ZeroDivisionError Python 3.7.1: .../bin/python3 Sun Dec 9 10:46:17 2018 A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. .../cgitb_with_classes.py in() 21 self.a = a 22 self.b = b 23 self.c = self.a * self.b 24 # Really 25 # long 26 # comment 27 # goes 28 # here. 29 self.d = self.a / self.b 30 return 31 32 o = BrokenClass(1, 0) o undefined BrokenClass = .../cgitb_with_classes.py in >, 21 self.a = a 22 self.b = b 23 self.c = self.a * self.b 24 # Really 25 # long 26 # comment 27 # goes 28 # here. 29 self.d = self.a / self.b 30 return 31 32 o = BrokenClass(1, 0) self = <__main__.BrokenClass object> self.d undefined self.a = 1 self.b = 0 ZeroDivisionError: division by zero __cause__ = None __class__ = __context__ = None __delattr__ = __dict__ = {} __dir__ = __doc__ = 'Second argument to a division or modulo operation was zero.' __eq__ = __format__ = __ge__ = __getattribute__ = __gt__ = __hash__ = __init__ = __init_subclass__ = __le__ = __lt__ = __ne__ = __new__ = __reduce__ = __reduce_ex__ = __repr__ = __setattr__ = __setstate__ = __sizeof__ = __str__ = __subclasshook__ = __suppress_context__ = False __traceback__ = args = ('division by zero',) with_traceback = The above is a description of an error in a Python program. Here is the original traceback: Traceback (most recent call last): File "cgitb_with_classes.py", line 32, in o = BrokenClass(1, 0) File "cgitb_with_classes.py", line 29, in __init__ self.d = self.a / self.b ZeroDivisionError: division by zero
Свойства исключения
Помимо локальных переменных из каждого фрейма стека, cgitb
показывает все свойства объекта исключения. Дополнительные свойства для пользовательских типов исключений печатаются как часть отчета об ошибке.
cgitb_exception_properties.py
import cgitb cgitb.enable(format'text') class MyException(Exception): """Add extra properties to a special exception """ def __init__(self, message, bad_value): self.bad_value bad_value Exception.__init__(self, message) return raise MyException('Normal message', bad_value99)
В этом примере свойство bad_value
включено вместе со стандартными значениями message
и args
.
$ python3 cgitb_exception_properties.py MyException Python 3.7.1: .../bin/python3 Sun Dec 9 10:46:17 2018 A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. .../cgitb_exception_properties.py in() 19 self.bad_value = bad_value 20 Exception.__init__(self, message) 21 return 22 23 raise MyException('Normal message', MyException = bad_value undefined MyException: Normal message __cause__ = None __class__ = __context__ = None __delattr__ = __dict__ = {'bad_value': 99} __dir__ = __doc__ = 'Add extra properties to a special exception\n ' __eq__ = __format__ = __ge__ = __getattribute__ = __gt__ = __hash__ = __init__ = __init_subclass__ = __le__ = __lt__ = __module__ = '__main__' __ne__ = __new__ = __reduce__ = __reduce_ex__ = __repr__ = __setattr__ = __setstate__ = __sizeof__ = __str__ = __subclasshook__ = __suppress_context__ = False __traceback__ = __weakref__ = None args = ('Normal message',) bad_value = 99 with_traceback = The above is a description of an error in a Python program. Here is the original traceback: Traceback (most recent call last): File "cgitb_exception_properties.py", line 23, in raise MyException('Normal message', MyException: Normal message
Вывод HTML
Поскольку cgitb
изначально был разработан для обработки исключений в веб-приложениях, обсуждение не будет полным без упоминания исходного формата вывода HTML. Все предыдущие примеры показывают вывод в виде обычного текста. Чтобы вместо этого создать HTML, оставьте аргумент format
(или укажите «html»
). Большинство современных веб-приложений построено с использованием инфраструктуры, которая включает средство сообщения об ошибках, поэтому HTML-форма в значительной степени устарела.
Ведение журнала трассировки
Для многих ситуаций наилучшим решением является печать деталей трассировки до стандартной ошибки. Однако в производственной системе регистрировать ошибки еще лучше. Функция enable ()
включает необязательный аргумент logdir
, чтобы включить регистрацию ошибок. Если указано имя каталога, каждое исключение регистрируется в собственном файле в данном каталоге.
cgitb_log_exception.py
import cgitb import os LOGDIR os.path.join(os.path.dirname(__file__), 'LOGS') if not os.path.exists(LOGDIR): os.makedirs(LOGDIR) cgitb.enable( logdirLOGDIR, displayFalse, format'text', ) def func(a, divisor): return a / divisor func(1, 0)
Несмотря на то, что отображение ошибок подавлено, печатается сообщение, описывающее, где найти журнал ошибок.
$ python3 cgitb_log_exception.pyA problem occurred in a Python script. .../LOGS/tmpdl2oafqt.txt contains the description of this error. $ ls LOGS tmpdl2oafqt.txt $ cat LOGS/*.txt ZeroDivisionError Python 3.7.1: .../bin/python3 Sun Dec 9 10:46:17 2018 A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred. .../cgitb_log_exception.py in
() 24 25 def func(a, divisor): 26 return a / divisor 27 28 func(1, 0) func = .../cgitb_log_exception.py in 24 25 def func(a, divisor): 26 return a / divisor 27 28 func(1, 0) a = 1 divisor = 0 ZeroDivisionError: division by zero __cause__ = None __class__ = __context__ = None __delattr__ = __dict__ = {} __dir__ = __doc__ = 'Second argument to a division or modulo operation was zero.' __eq__ = __format__ = __ge__ = __getattribute__ = __gt__ = __hash__ = __init__ = __init_subclass__ = __le__ = __lt__ = __ne__ = __new__ = __reduce__ = __reduce_ex__ = __repr__ = __setattr__ = __setstate__ = __sizeof__ = __str__ = __subclasshook__ = __suppress_context__ = False __traceback__ = args = ('division by zero',) with_traceback = The above is a description of an error in a Python program. Here is the original traceback: Traceback (most recent call last): File "cgitb_log_exception.py", line 28, in func(1, 0) File "cgitb_log_exception.py", line 26, in func return a / divisor ZeroDivisionError: division by zero
Смотрите также
- стандартная библиотека документации для cgitb
- traceback – Стандартный библиотечный модуль для работы с трассировками.
- inspect – модуль
inspect
включает больше функций для проверки стека. - sys – модуль
sys
обеспечивает доступ к текущему значению исключения и обработчикуexcepthook
, который вызывается при возникновении исключения. - Улучшенный модуль трассировки – Обсуждение в списке рассылки разработчиков Python улучшений в модуле трассировки и связанных с ним улучшений другие разработчики используют локально.