用于python警告的日志堆栈跟踪

时间:2015-01-29 07:15:15

标签: python logging warnings stack-trace

我在我的python程序中使用的软件包正在发出一个警告,表示我想了解其确切原因。我已设置logging.captureWarning(True)并在我的日志记录中捕获警告,但仍不知道它来自何处。我如何记录堆栈跟踪,以便我可以看到警告来自哪里?我使用traceback吗?

6 个答案:

答案 0 :(得分:6)

虽然有点hackish,但你可以将warnings.warn方法用于monkeypatch:

import traceback
import warnings

def g():
    warnings.warn("foo", Warning)

def f():
    g()
    warnings.warn("bar", Warning)

_old_warn = warnings.warn
def warn(*args, **kwargs):
    tb = traceback.extract_stack()
    _old_warn(*args, **kwargs)
    print("".join(traceback.format_list(tb)[:-1]))
warnings.warn = warn

f()
print("DONE")

这是输出:

/tmp/test.py:14: Warning: foo
  _old_warn(*args, **kwargs)
  File "/tmp/test.py", line 17, in <module>
    f()
  File "/tmp/test.py", line 8, in f
    g()
  File "/tmp/test.py", line 5, in g
    warnings.warn("foo", Warning)

/tmp/test.py:14: Warning: bar
  _old_warn(*args, **kwargs)
  File "/tmp/test.py", line 17, in <module>
    f()
  File "/tmp/test.py", line 9, in f
    warnings.warn("bar", Warning)

DONE

请注意,调用原始warnings.warn函数不会报告您想要的行,但堆栈跟踪确实是正确的(您可以自己打印警告消息)。

答案 1 :(得分:4)

我最终选择了以下内容:

import warnings
import traceback

_formatwarning = warnings.formatwarning

def formatwarning_tb(*args, **kwargs):
    s = _formatwarning(*args, **kwargs)
    tb = traceback.format_stack()
    s += ''.join(tb[:-1])
    return s

warnings.formatwarning = formatwarning_tb
logging.captureWarnings(True)

答案 2 :(得分:1)

如果您不知道导致警告投掷的数据/指令,您可以使用标准Python Debugger等工具。

文档非常好且详细,但是一些可能有用的快速示例应该是:

  • 不用修改源代码:调用debbugger作为脚本:

      

    $ python -m pdb myscript.py

  • 修改源代码:您可以使用对pdb.set_trace()的调用,这些调用就像断点一样;例如,请考虑我有以下示例代码:

    x = 2
    x = x * 10 * 100
    y = x + 3 + y
    return y

    我想知道 x y 在返回之前有什么价值,或者堆栈包含什么,我会在这些语句之间添加以下行:

    pdb.set_trace()

    我将接受(Pdb)提示,这将允许您逐行浏览代码。 (Pdb)提示符的有用命令是:

    • n :执行下一个语句。
    • q :退出整个计划。
    • c :退出(Pdb)提示并停止调试。
    • p varname :打印 varname
    • 的值

由于您没有提供更多信息,我不知道这是否足够,但我认为至少,这可能是一个良好的开端。

BONUS EDIT

基于this answer,我发现有一个漂亮而友好的GUI调试工具,您可以通过以下方式安装:

  

$ pip 安装pudb

使用脚本运行调试器:

  

$ python -m pudb.run myscript.py

编辑:添加事后调试

如果我们甚至不知道代码是否会崩溃,我们可以在发生崩溃时输入 postmortem 调试。来自Pbd文档:

  

检查崩溃程序的典型用法是:

>>> import pdb
>>> import mymodule
>>> mymodule.test()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "./mymodule.py", line 4, in test
    test2()
  File "./mymodule.py", line 3, in test2
    print spam
NameError: spam
>>> pdb.pm()
> ./mymodule.py(3)test2()
-> print spam
(Pdb)

postmortem 查看sys.last_traceback时,只有在有回溯时才会输入(等等,警告或崩溃):

if sys.last_traceback:
     pdb.pm()

答案 3 :(得分:1)

您可以将警告变为异常,这意味着您将自动获得堆栈跟踪:

warnings.filterwarnings("error")

请参阅https://docs.python.org/3.4/library/warnings.html#the-warnings-filter

答案 4 :(得分:0)

如果是我,我会选择@LluísVilanova的快速&amp;肮脏的黑客,只是为了找到一些东西。但如果那不是一个选择......

如果你真的想要一个“日志记录”解决方案,你可以试试像this这样的东西(完全工作的来源)。

基本步骤是:

  • 创建一个自定义logging.Formatter子类,其中包含格式化日志记录的当前堆栈
  • 在警告类
  • 上使用该格式化程序

代码的核心是自定义格式化程序:

class Formatter(logging.Formatter):
    def format(self, record):
        record.stack_info = ''.join(traceback.format_stack())
        return super().format(record)

根据docs

New in version 3.2: The stack_info parameter was added.

答案 5 :(得分:0)

对于python 3.2及更高版本,使用可选的stack_info关键字参数是获取堆栈跟踪信息和日志消息的最简单方法。 在下面的示例中,“ Server.py”使用“ lib2.py”,而“ lib2.py”使用“ lib.py”。 启用stack_info参数后,完整的追溯记录将与每个logging.log()调用一起记录。这与logging.info()和其他便利方法相同。

用法:-

logging.log(DEBUG, "RWL [{}] : acquire_read()".format(self._ownerName), stack_info=True)

输出:-

2018-10-06 10:59:55,726|DEBUG|MainThread|lib.py|acquire_read|RWL [Cache] : acquire_read()
Stack (most recent call last):
  File "./Server.py", line 41, in <module>
    logging.info("Found {} requests for simulation".format(simdata.count()))
  File "<Path>\lib2.py", line 199, in count
    with basics.ReadRWLock(self.cacheLock):
  File "<Path>\lib.py", line 89, in __enter__
    self.rwLock.acquire_read()
  File "<Path>\lib.py", line 34, in acquire_read
    logging.log(DEBUG, "RWL [{}] : acquire_read()".format(self._ownerName), stack_info=True)