Наследовать имя логгера в Python - PullRequest
1 голос
/ 09 октября 2019

Я пытаюсь записать необработанное исключение в python. Для этого я устанавливаю sys.excepthook в моем __init__.py следующим образом.

import sys
import logging
import traceback


def log_except_hook(*exc_info):
    logger = logging.getLogger(__name__)
    text = "".join(traceback.format_exception(*exc_info))
    logger.critical(f"Unhandled exception:\n{text}")


sys.excepthook = log_except_hook

Моя проблема заключается в том, что когда я запускаю процесс и возникает исключение, имя модуля в зарегистрированном исключении являетсяимя папки, содержащей код (в данном случае src), а не имя функции, в которой это произошло (в данном примере foo_produce_an_error). Ниже приведен пример:

2019-10-09 18:55:48,638 src CRITICAL: Unhandled exception:
Traceback (most recent call last):
  File "/Users/ivallesp/Projects/Project_Folder/main.py", line 109, in <module>
    foo_function(7)
  File "/Users/ivallesp/Projects/Project_Folder/src/new_module.py", line 8, in foo_function
    foo_produce_an_error(x)
  File "/Users/ivallesp/Projects/Project_Folder/src/new_module.py", line 12, in foo_produce_an_error
    x / 0
ZeroDivisionError: division by zero

Как сделать запись в журнал, чтобы показать модуль и имя функции, в которой произошла ошибка в первой строке записи?

Ответы [ 2 ]

1 голос
/ 10 октября 2019

Вы не предоставили достаточно информации, чтобы ответить на вопрос - например, как вы настроили ведение журнала (в частности, использовалась строка формата / форматер). Я могу проиллюстрировать, как добиться желаемого результата в целом, на примере. Предположим, у вас есть неисправная функция в модуле failfunc:

# failfunc.py
def the_failing_func():
    1 / 0

Тогда ваш главный скрипт может быть:

# logtest_ue.py
import logging
import sys

from failfunc import the_failing_func

def log_except_hook(*exc_info):
    logger = logging.getLogger(__name__)
    tb = exc_info[-1]
    # get the bottom-most traceback entry
    while tb.tb_next:
        tb = tb.tb_next
    modname = tb.tb_frame.f_globals.get('__name__')
    funcname = tb.tb_frame.f_code.co_name
    logger.critical('Unhandled in module %r, function %r: %s',
                    modname, funcname, exc_info[1], exc_info=exc_info)

sys.excepthook = log_except_hook

def main():
    the_failing_func()

if __name__ == '__main__':
    logging.basicConfig(format='%(levelname)s %(message)s')
    sys.exit(main())

, когда он запущен, он печатает

CRITICAL Unhandled in module 'failfunc', function 'the_failing_func': division by zero
Traceback (most recent call last):
  File "logtest_ue.py", line 23, in <module>
    sys.exit(main())
  File "logtest_ue.py", line 19, in main
    the_failing_func()
  File "/home/vinay/projects/scratch/failfunc.py", line 2, in the_failing_func
    1 / 0
ZeroDivisionError: division by zero

Обратите внимание на несколько более простой способ получения трассировки в журнале, используя параметр ключевого слова exc_info. Также обратите внимание, что в этом случае обычное имя модуля и функции (которое может отображаться с использованием %(module)s и %(funcName)s в строке формата) будут теми, на которые указывает sys.excepthook, а не значениями, где фактически произошло исключение,Для этого вам придется использовать объект трассировки, как я иллюстрировал, чтобы получить самый нижний фрейм (где на самом деле произошло исключение) и получить имена модулей и функций из этого фрейма.

0 голосов
/ 10 октября 2019

Модуль трудно получить, но вы можете иметь имя файла и номер строки. Они содержатся в exc_info, который вам не нужно форматировать самостоятельно. Вы можете просто передать их в функцию журнала и использовать средство форматирования для отображения всего этого. Вот пример кода, чтобы дать вам представление, как это делается:

import sys
import logging
import traceback


def log_except_hook(*exc_info):
    logger = logging.getLogger(__name__)
    handler = logging.StreamHandler()
    handler.setFormatter(logging.Formatter('%(asctime)s %(fname)s:%(lnum)s %(message)s %(exc_info)s'))
    logger.addHandler(handler)

    frame = traceback.extract_tb(exc_info[2])[-1]
    fname = frame.filename
    lnum = frame.lineno
    logger.critical("Unhandled exception:", exc_info=exc_info, extra={'fname':fname, 'lnum':lnum})

sys.excepthook = log_except_hook
...