Регистрация неперехваченных исключений в Python - PullRequest
148 голосов
/ 04 июня 2011

Как вызвать необработанные исключения для вывода через модуль logging вместо stderr?

Я понимаю, что лучший способ сделать это будет:

try:
    raise Exception, 'Throwing a boring exception'
except Exception, e:
    logging.exception(e)

Но моя ситуация такова, что было бы действительно хорошо , если бы logging.exception(...) вызывался автоматически всякий раз, когда исключение не было обнаружено.

Ответы [ 8 ]

138 голосов
/ 08 июня 2013

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

import sys
import logging
logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)
logger.addHandler(handler)

def handle_exception(exc_type, exc_value, exc_traceback):
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return

    logger.error("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))

sys.excepthook = handle_exception

if __name__ == "__main__":
    raise RuntimeError("Test unhandled")
  • Игнорировать KeyboardInterrupt, чтобы консольная Python-программа могла завершиться с помощью Ctrl + C.

  • Полностью полагайтесь на модуль журналирования python для форматирования исключения.

  • Используйте настраиваемый регистратор с примером обработчика.Это изменяет необработанное исключение, чтобы перейти к stdout, а не к stderr, но вы можете добавить все виды обработчиков в этом же стиле к объекту регистратора.

122 голосов
/ 04 июня 2011

Как указал Нед, sys.excepthook вызывается каждый раз, когда возникает исключение и не обрабатывается.Практическое значение этого заключается в том, что в вашем коде вы можете переопределить поведение по умолчанию sys.excepthook, чтобы делать все, что вы хотите (включая использование logging.exception).

Как пример соломенного человека:

>>> import sys
>>> def foo(exctype, value, tb):
...     print 'My Error Information'
...     print 'Type:', exctype
...     print 'Value:', value
...     print 'Traceback:', tb
... 

Переопределить sys.excepthook:

>>> sys.excepthook = foo

Зафиксировать явную синтаксическую ошибку (пропустить двоеточие) и получить информацию о пользовательской ошибке:

>>> def bar(a, b)
My Error Information
Type: <type 'exceptions.SyntaxError'>
Value: invalid syntax (<stdin>, line 1)
Traceback: None

Для получения дополнительной информации о sys.excepthook: http://docs.python.org/library/sys.html#sys.excepthook

26 голосов
/ 04 июня 2011

Метод sys.excepthook будет вызван, если исключение не найдено: http://docs.python.org/library/sys.html#sys.excepthook

Когда исключение возникает и не обрабатывается, интерпретатор вызывает sys.excepthook с тремя аргументами, класс исключения, экземпляр исключения и объект трассировки.В интерактивном сеансе это происходит непосредственно перед тем, как управление возвращается к приглашению;в программе Python это происходит непосредственно перед выходом из программы.Обработку таких исключений верхнего уровня можно настроить, назначив sys.excepthook еще одну функцию с тремя аргументами.

17 голосов
/ 17 ноября 2011

Почему бы и нет:

import sys
import logging
import traceback

def log_except_hook(*exc_info):
    text = "".join(traceback.format_exception(*exc_info))
    logging.error("Unhandled exception: %s", text)

sys.excepthook = log_except_hook

None()

Вот вывод с sys.excepthook, как показано выше:

$ python tb.py
ERROR:root:Unhandled exception: Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

Вот вывод с sys.excepthook закомментированным:

$ python tb.py
Traceback (most recent call last):
  File "tb.py", line 11, in <module>
    None()
TypeError: 'NoneType' object is not callable

Единственное отличие состоит в том, что у первого есть ERROR:root:Unhandled exception: в начале первой строки.

6 голосов
/ 11 мая 2012

Чтобы построить ответ Джаинды, но используя объект логгера:

def catchException(logger, typ, value, traceback):
    logger.critical("My Error Information")
    logger.critical("Type: %s" % typ)
    logger.critical("Value: %s" % value)
    logger.critical("Traceback: %s" % traceback)

# Use a partially applied function
func = lambda typ, value, traceback: catchException(logger, typ, value, traceback)
sys.excepthook = func
4 голосов
/ 01 июля 2012

Оберните входной вызов приложения в блок try...except, чтобы вы могли перехватывать и регистрировать (и, возможно, повторно поднимать) все необработанные исключения. Например. вместо:

if __name__ == '__main__':
    main()

Сделайте это:

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise
3 голосов
/ 04 июня 2011

Может быть, вы могли бы сделать что-то в верхней части модуля, который перенаправляет stderr в файл, а затем зарегистрировать этот файл в нижней части

sock = open('error.log', 'w')               
sys.stderr = sock

doSomething() #makes errors and they will log to error.log

logging.exception(open('error.log', 'r').read() )
2 голосов
/ 05 декабря 2015

Хотя ответ @ gnu_lorien дал мне хорошую отправную точку, моя программа аварийно завершает работу при первом исключении.

Я пришел с настроенным (и / или) улучшенным решением, которое автоматически регистрирует исключения из функций, которые отмечены @handle_error.

import logging

__author__ = 'ahmed'
logging.basicConfig(filename='error.log', level=logging.DEBUG)


def handle_exception(exc_type, exc_value, exc_traceback):
    import sys
    if issubclass(exc_type, KeyboardInterrupt):
        sys.__excepthook__(exc_type, exc_value, exc_traceback)
        return
    logging.critical(exc_value.message, exc_info=(exc_type, exc_value, exc_traceback))


def handle_error(func):
    import sys

    def __inner(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except Exception, e:
            exc_type, exc_value, exc_tb = sys.exc_info()
            handle_exception(exc_type, exc_value, exc_tb)
        finally:
            print(e.message)
    return __inner


@handle_error
def main():
    raise RuntimeError("RuntimeError")


if __name__ == "__main__":
    for _ in xrange(1, 20):
        main()
...