Журнал Python: отключить трассировку стека

Есть ли простой способ отключить запись трассировки стека исключений в Python 3, либо в Handler или же Formatter?

Мне нужна трассировка стека в другом Handlerтак что настройка exc_info=Falseв призыве к Logger это не вариант. Есть ли более простой способ, чем просто определить мой собственный Formatter?

3 ответа

Самый простой способ отключить вывод трассировки для каждого обработчика - это добавить пользовательский logging.Filter подкласс, который изменяет объект записи (а не отфильтровывает записи).

Фильтр просто должен установить exc_text на записи в строку, заменяя None дефолт:

class SetTracebackCacheFilter(logging.Filter):
    """Set the exception cache text on log records to a specific value"""
    def __init__(self, exc_text):
        self.exc_text = exc_text
    def filter(self, record):
        record.exc_text = self.exc_text
        return True

и добавьте этот фильтр в ваш обработчик, установив кешированный текст в пустую строку:

# do not display tracebacks in messages handled with this handler,
# by setting the traceback cache to an empty string:
handler_with_no_tracebacks.addFilter(SetTracebackCacheFilter(''))

Это работает, потому что Formatter.format() метод явно документы LogRecord.exc_text в качестве атрибута, в котором кэшируется отформатированная трассировка:

Обратите внимание, что отформатированная информация об исключении кэшируется в атрибуте exc_text, Это полезно, потому что информация об исключении может быть выбрана и отправлена ​​по проводам, но вы должны быть осторожны, если у вас более одного Formatter подкласс, который настраивает форматирование информации об исключении. В этом случае вам придется очистить кэшированное значение после того, как форматировщик выполнил свое форматирование, чтобы следующий форматировщик, обрабатывающий событие, не использовал кэшированное значение, а пересчитал его заново.

Приведенный выше фильтр использует это для предотвращения генерации текста трассировки. Каждый раз, когда сообщение передается обработчику, вышеупомянутый фильтр вызывается, чтобы увидеть, захочет ли обработчик обработать экземпляр записи, и мы "кешируем" пустой текст исключения.

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

idx = logger.handlers.index(handler_with_no_tracebacks)
if len(logger.handlers) >= idx:
    # clear the traceback text cache again for further handlers
    logger.handlers[idx + 1].addFilter(SetTracebackCacheFilter(None))

Если вы хотите отключить все выходные данные трассировки везде, то, возможно, добавление настраиваемого фильтра ко всем обработчикам или регистраторам становится утомительным. В этом случае другой вариант заключается в регистрации пользовательской фабрики записей с logging.setLogRecordFactory() функция; просто установите exc_text приписать записи к пустой строке:

record_factory = logging.getLogRecordFactory()

def clear_exc_text(*args, **kwargs):
    record = record_factory(*args, **kwargs)
    record.exc_text = ''
    return record

logging.setLogRecordFactory(clear_exc_text)

Обратите внимание, что фабрика по умолчанию это просто logging.LogRecord класс, но вышеупомянутая функция делает все возможное для работы с любой уже установленной фабрикой.

Ни фильтр, ни фабрика пользовательских записей не очищают exc_info кортеж, так что вы можете получить доступ к этому где-то еще.

Конечно, вы также можете создать свой собственный Handler подкласс, где Handler.handle() устанавливает и очищает exc_text атрибут:

class NoTracebackHandler(logging.Handler):
    def handle(self, record):
        old, record.exc_text = record.exc_text, ''
        try:
            super().handle(record)
        finally:
            record.exc_text = old

Это можно сделать даже проще, чем ответ Дигои :

      class NoTracebackFormatter(logging.Formatter):
    def formatException(self, ei):
        return ""
    def formatStack(self, stack_info):
        return ""

(Проверено на Python 3.9)

Поздний ответ, но это может быть полезно для кого-то еще. Вам не нужно беспокоиться о Handlerни Filter. Вам просто нужно унаследовать свой собственный Formatterи пропустите часть, где вы форматируете исключение. Вот фрагмент, который я использую:

      class EnhancedFormatter(logging.Formatter):
    def __init__(self, fmt=None, datefmt=None, style='%', validate=True, skip_traceback=False):
        self.skip_traceback = skip_traceback
        super(EnhancedFormatter, self).__init__(fmt, datefmt, style, validate)

    def format(self, record) -> str:
        record.message = record.getMessage()
        if self.usesTime():
            record.asctime = self.formatTime(record, self.datefmt)
        s = self.formatMessage(record)
        if not self.skip_traceback: # check here do you need to format traceback
            if record.exc_info:
                if not record.exc_text:
                    record.exc_text = self.formatException(record.exc_info)
            if record.exc_text:
                if s[-1:] != "\n":
                    s = s + "\n"
                s = s + record.exc_text
            if record.stack_info:
                if s[-1:] != "\n":
                    s = s + "\n"
                s = s + self.formatStack(record.stack_info)
        return s

Просто установите skip_tracebackаргумент при создании экземпляра класса форматирования, а затем используйте его, чтобы определить, нужно ли вам форматировать трассировку или нет.

Другие вопросы по тегам