Python: logging.warn () добавить трассировку стека - PullRequest
0 голосов
/ 04 сентября 2018

В устаревшей кодовой базе, над которой я работаю сегодня, есть несколько logging.warn('....') вызовов.

Я хочу лучше понять вывод журнала. До сих пор logging.warn() испускает одну строку. Но одной этой строки недостаточно для понимания контекста.

Я бы хотел увидеть трассировку стека интерпретатора.

Поскольку в моем коде много logging.warn('....') строк, я хотел бы оставить их такими, какие они есть, и изменить только конфигурацию ведения журнала.

Как я могу добавить трассировку стека интерпретатора к каждому warn() или error() вызову автоматически?

Я знаю, что logging.exception("message") показывает трассировку стека, но я бы хотел оставить строки logging.warn() нетронутыми.

Ответы [ 3 ]

0 голосов
/ 10 сентября 2018

Я не верю, что обработчик - это ваше решение. Перейти на фильтр:

import os.path
import traceback
import logging
_LOGGING_FILE = os.path.normcase(logging.addLevelName.__code__.co_filename)
_CURRENT_FILE = os.path.normcase(__file__)
_ELIMINATE_STACK = (_CURRENT_FILE, _LOGGING_FILE)


class AddStackFilter(logging.Filter):
    def __init__(self, levels=None):
        self.levels = levels or set()

    def get_stack(self):
        # Iterator over file names
        filenames = iter(_ELIMINATE_STACK)
        filename = next(filenames, "")
        frames = traceback.walk_stack(None)

        # Walk up the frames
        for frame, lineno in frames:

            # If frame is not from file, continue on to the next file
            while os.path.normcase(frame.f_code.co_filename) != filename:
                filename = next(filenames, None)
                if filename is None:
                    break
            else:
                # It's from the given file, go up a frame
                continue

            # Finished iterating over all files
            break

        # No frames left
        else:
            return None

        info = traceback.format_stack(frame)
        info.insert(0, 'Stack (most recent call last):\n')

        # Remove last newline
        info[-1] = info[-1].rstrip()

        return "".join(info)

    def filter(self, record):
        if record.levelno in self.levels:
            sinfo = self.get_stack()
            if sinfo is not None:
                record.stack_info = sinfo

        return True

Этот фильтр имеет множество преимуществ:

  1. Удаляет кадры стека из локального файла и файла журнала.
  2. Оставляет кадры стека на случай, если мы вернемся к локальному файлу после прохождения регистрации. Важно, если мы хотим использовать тот же модуль для других вещей.
  3. Вы можете прикрепить его к любому обработчику или регистратору, не привязывая вас к StreamHandler или любому другому обработчику.
  4. Вы можете воздействовать на несколько обработчиков, используя один и тот же фильтр или один обработчик по вашему выбору.
  5. Уровни задаются в виде переменной __init__, что позволяет добавлять дополнительные уровни по мере необходимости.
  6. Позволяет добавлять трассировку стека в журнал, а не просто печатать.
  7. Хорошо играет с модулем регистрации, помещая стек в правильное место, ничего неожиданного.

Использование:

>>> import stackfilter
>>> import logging
>>> sfilter = stackfilter.AddStackFilter(levels={logging.WARNING})
>>> logging.basicConfig()
>>> logging.getLogger().addFilter(sfilter)
>>> def testy():
...     logging.warning("asdasd")
...
>>> testy()
WARNING:root:asdasd
Stack (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in testy
0 голосов
/ 04 июля 2019

Ответ, который я искал, дал @Martijn Pieters ♦ в комментариях

В питоне 3.x

logger.warning(f'{error_message}', stack_info=True)

делает именно то, что вам нужно.

Спасибо @Martijn Pieters ♦

0 голосов
/ 07 сентября 2018

тривиально, если вы согласитесь добавить обработчик журнала:

import logging
import traceback


class WarnWithStackHandler(logging.StreamHandler):
    def emit(self, record):
        if record.levelno == logging.WARNING:
            stack = traceback.extract_stack()
            # skip logging internal stacks
            stack = stack[:-7]
            for line in traceback.format_list(stack):
                print(line, end='')
        super().emit(record)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...