Регистрация запрещенных исключений в 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.