Регистрация запрещенных исключений в Python

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

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

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

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

Ответ 1

Как указал Нед, 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

Ответ 2

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

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, но вы могли бы добавить все виды обработчиков в этом же стиле к объекту журнала.

Ответ 3

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

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

Ответ 4

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

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: в начале первой строки.

Ответ 5

Чтобы построить ответ Jacinda, но используя объект logger:

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

Ответ 6

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

if __name__ == '__main__':
    main()

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

if __name__ == '__main__':
    try:
        main()
    except Exception as e:
        logger.exception(e)
        raise

Ответ 7

Возможно, вы могли бы сделать что-то наверху модуля, который перенаправляет 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() )

Ответ 8

Хотя ответ @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()

Ответ 9

Чтобы ответить на вопрос г-на Зеуса, который обсуждался в разделе комментариев принятого ответа, я использую его для регистрации необработанных исключений в интерактивной консоли (протестировано с PyCharm 2018-2019). Я обнаружил, что sys.excepthook не работает в оболочке Python, поэтому я посмотрел глубже и обнаружил, что могу вместо него использовать sys.exc_info. Однако sys.exc_info не принимает аргументов в отличие от sys.excepthook, который принимает 3 аргумента.

Здесь я использую sys.excepthook и sys.exc_info для записи обоих исключений в интерактивную консоль и сценарий с функцией оболочки. Чтобы прикрепить функцию ловушки к обеим функциям, у меня есть два разных интерфейса в зависимости от того, заданы аргументы или нет.

Вот код:

def log_exception(exctype, value, traceback):
    logger.error("Uncaught exception occurred!",
                 exc_info=(exctype, value, traceback))


def attach_hook(hook_func, run_func):
    def inner(*args, **kwargs):
        if not (args or kwargs):
            # This condition is for sys.exc_info
            local_args = run_func()
            hook_func(*local_args)
        else:
            # This condition is for sys.excepthook
            hook_func(*args, **kwargs)
        return run_func(*args, **kwargs)
    return inner


sys.exc_info = attach_hook(log_exception, sys.exc_info)
sys.excepthook = attach_hook(log_exception, sys.excepthook)

Настройки ведения журнала можно найти в ответе gnu_lorien.