Python:logging.warn()添加stacktrace

时间:2018-09-04 08:14:04

标签: python python-2.7 logging

我正在处理的旧代码库中有多个logging.warn('....')调用。

我想更好地了解日志输出。到目前为止,logging.warn()确实发出了一行。但是,仅此一行不足以理解上下文。

我想查看解释器的堆栈跟踪。

由于我的代码中有很多logging.warn('....')行,因此我想保留它们,只修改日志记录的配置

如何将解释器堆栈跟踪自动添加到每个warn()error()调用中?

我知道logging.exception("message")显示了堆栈跟踪,但是我想保留logging.warn()行。

3 个答案:

答案 0 :(得分:2)

如果您接受添加日志处理程序,那就太简单了:

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)

答案 1 :(得分:1)

我不认为处理程序是您的解决方案。进行过滤:

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

答案 2 :(得分:1)

@Martijn Pieters♦在评论中给出了我一直在寻找的答案

在python 3.x中

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

完全满足您的需求。

感谢@Martijn Pieters♦