Рубрики
Без рубрики

cgitb – подробные отчеты об отслеживании

Автор оригинала: 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, in 
    func1(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.py

A 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 улучшений в модуле трассировки и связанных с ним улучшений другие разработчики используют локально.