【问题标题】:logging.warn() add stacktracelogging.warn() 添加堆栈跟踪
【发布时间】:2019-02-09 05:14:16
【问题描述】:

我今天正在处理的遗留代码库中有几个 logging.warn('....') 调用。

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

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

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

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

我知道logging.exception("message") 显示堆栈跟踪,但我想保持logging.warn() 行不变。

【问题讨论】:

  • 我处理这个问题的方法是在except 子句中使用logging.exception("message")
  • @TD-Asker 我更新了答案:我知道 logging.exception("message") 显示堆栈跟踪,但我想保持 logging.warn() 行不变。
  • 我想你可能会遇到困难,因为.warn() 只会返回包含的字符串并且不知道堆栈跟踪。更改日志记录配置不会改变.warn() 包含的知识。祝你好运,有人可能知道黑客/解决方法!
  • @OzgurBagci: warnings != logging。虽然您可以同时对两者进行修补,但对于任何一个框架都有更好的选择。
  • 这适用于哪个 Python 版本? Python 3.x 具有 stack_info 标志,您可以轻松地强制使用它。

标签: python python-2.7 logging


【解决方案1】:

如果您接受添加日志处理程序,这很简单:

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)

【讨论】:

    【解决方案2】:

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

    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
    

    【讨论】:

    • 一个问题是在 python2 中,logging.Formatter 不格式化 record.stack_info 开箱即用,也许你可以将它搭载到 record.exc_info
    【解决方案3】:

    @Martijn Pieters♦ 在 cmets 中给出了我正在寻找的答案

    在 python 3.x 中

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

    完全满足您的需要。

    感谢@Martijn Pieters♦

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-25
      • 2011-05-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-11
      相关资源
      最近更新 更多