在上共享推特
在上共享脸谱网
在上共享黑客新闻
在上共享LinkedIn链接

调试Python错误

Sentry的一个更强大的功能包括以下语言蟒蛇在Python、PHP和JVM中,我们能够更深入地反思运行时,并为您提供有关调用堆栈中每个帧的附加数据。在较高的层次上,这让您了解诸如调用函数参数之类的事情,从而更容易重现和理解错误。让我们深入了解它的外观,以及它在引擎盖下的工作原理。

我们将从什么开始Python错误通常可能与终端或标准日志记录系统中的类似:

TypeError:需要字符串或缓冲区文件“sentry/stacktraces.py”,第309行,在process_single_stacktrace中processable_frame,processing_task)process_frame中的文件“sentry/lang/native/plugin.py”第196行in_app=(in_app而不是self.sym.is_internal_function(raw_frame.get('function')))is_internal_function中的文件“sentry/lang/native/symbolar.py”,第278行return _internal_function_re.search(function)不为None

虽然这让我们了解了错误的类型和位置,但不幸的是,这并不能帮助我们理解真正导致错误的原因。它可能传递了一个整数或NoneType,但实际上,它可能是任意数量的东西。猜测只能让我们走到目前为止,我们真的需要知道什么功能实际上是。

要做到这一点,一个简单且通常非常容易的选择是添加一些测井。我们可以在几个不同的入口点放置日志记录,这样更容易实现。它还让我们能够确保我们明确地得到我们想要的答案。例如,我们想知道类型属于功能是:

进口测井# ...测井.调试(“函数的类型为%s”, 类型(功能))

像这样的伐木的另一个好处是可以进行生产。结果是,您通常不会在生产中记录DEBUG级别的日志语句,因为数量可能很大,而且用处不大。它还经常要求您提前计划,以确保记录可能发生的各种故障案例以及每个案例的适当上下文。

为了阅读本教程,让我们假设我们无法在生产中实现这一点,没有提前计划,而是尝试在开发中调试和再现这一点。

Python调试器

这个Python调试器(PDB)是一种工具,允许您使用断点单步执行调用堆栈。该工具本身受到GNU的调试器(GDB)的启发,虽然功能强大,但如果你不熟悉它,它通常会让你大吃一惊。这肯定是一种随着重复而变得更容易的体验,我们只会在我们的示例中介绍一些高级概念。

因此,我们要做的第一件事是插入代码以添加断点。在上面的示例中,我们实际上可以对符号.py我们自己。情况并非总是如此,因为有时第三方代码中会发生异常。无论你在哪里使用乐器,你都可以在堆栈中上下跳跃。让我们从更改代码开始:

定义 is_internal_function(内部函数)(自己,功能):
    #为PDB添加断点
    尝试:
        返回_内部函数.搜索(功能)   
    除了例外:
        进口个人数字广播个人数字广播.集合跟踪()
        提升

我们将此限制为例外情况,因为通常情况下,您的代码大部分时间都会成功运行,有时是在循环中运行,并且您不希望在每次迭代时都暂停执行。

一旦我们到达这个断点(这就是set_trace()正在注册),我们将进入一个特殊的类壳环境:

# ...(分贝)

这是PDB控制台,其工作方式与Python shell类似。除了能够运行大多数Python代码外,我们还可以在调用堆栈中的特定上下文中执行。该位置是入口点。相反,这是你打电话的地方set_trace()。在上面的示例中,我们正好处于需要的位置,因此可以轻松获取功能:

(Pdb)类型(函数)<类型“NoneType”>

当然,我们也可以使用Python的内置程序之一简单地获取所有本地范围的变量:

(数据库)本地(){…,'函数':无,…}

在某些情况下,我们可能需要导航向上的向下到达执行函数的帧的堆栈。例如,如果我们的set_trace()仪器将我们放在堆栈的更高位置,可能在顶部框架,我们将使用向下跳转到内部框架中,直到到达一个具有所需信息的位置:

(Pdb)下降->in_app=(in_app而不是self.sym.is_internal_function(raw_frame.get('function')))(Pdb)下降->return_internal_function_re.search(function)不是None(Pdb)类型(功能)<类型“NoneType”>

因此,我们确定了问题:功能是一个无类型虽然这并没有真正告诉我们为什么会这样,但它至少为我们提供了有价值的信息,以加快测试用例的速度。

生产中的Python调试

因此,PDB在开发方面很有效,但生产方面呢?让我们更深入地了解一下Python为我们提供了什么来回答这个问题。

CPython运行时是大多数人使用的标准运行时,它的优点是可以轻松访问当前的调用堆栈。虽然其他一些运行时(如PyPy)将提供类似的信息,但这并不保证。当遇到异常时,堆栈通过系统例外信息()。让我们看看这为一个典型的异常提供了什么:

>>> 尝试:
...   1 / 0
... 除了:
...   进口系统系统.exc信息()
...
(<类型 '异常。ZeroDivisionError'>,ZeroDivision错误('整数被零除或模',),
    <回溯对象0x105da1a28>)

我们将避免深入讨论这个问题,但我们有一个包含三条信息的元组:异常的类、异常的实际实例和回溯对象。这里我们关心的是回溯对象。注意,您还可以使用回溯模块。这里有一些文档关于使用这些结构,但让我们深入了解一下自己,试着理解它们。在回溯对象中,我们有一堆可用的信息,尽管需要一些工作和魔法才能访问:

>>>exc类型,exc值,结核=exc信息>>>结核.tb_帧<框架对象0x105dc0e10>

一旦我们得到了一个框架,CPython就公开了获取堆栈局部变量的方法——即所有作用域变量都指向该执行框架。例如,查看以下代码:

定义 foo公司(酒吧=):foo公司= “酒吧”
    1 / 0

让我们用该代码生成一个异常:

尝试:foo公司()
除了:exc类型,不包括值(_V),结核=系统.exc信息()

最后,让我们通过局部变量(_L)<帧>对象:

>>>从pprint导入pprint>>>打印(结核.tb_帧._本地人)
{'__builtins__': <模块 '__builtin__' (建造-在里面)>,
    '__doc__':,
    '__name__': '__main__',
    '__package__':,
    'exc_info': (<类型'异常。ZeroDivisionError'>,ZeroDivision错误('整数被零除或模',),
                <回溯对象位于0x105cd4fc8个>),
    “foo”(foo): <foo函数位于0x105立方英尺50厘米8>,
    “历史记录”: '/Users/dcramer/.pythonhist',
    “操作系统”: <模块 “操作系统”“lib/python2.7/os.py”>,
    '打印': <函数pprint位于0x105立方英尺52a8>,
    '打印函数':_功能((2, 6, 0, “阿尔法”, 2), (, 0, 0, “阿尔法”, 0), 65536),
    “readline”: <模块 '读取行''lib/python2.7/lib-dynload/readline.so'>,
    “堆栈”: [],
    “系统”: <模块 “系统” (建造-在里面)>,
    “tb”: <回溯对象位于0x105da1a28>,
    “tb_next”:,
    '写入历史': <函数write_history位于0x105cf2c80>}

我们在上面看到的实际上并没有那么有用。原因是我们的范围上升了一级,所以我们看到了foo公司定义了,但实际上与函数调用本身无关。这并不总是正确的,但在我们的简单示例中却是如此。要找到我们要查找的信息,我们需要更深入一层:

>>>内部帧(_F)=结核.tb_next(下一步).tb_帧>>>打印(内部帧(_F).局部变量(_L))
{“bar”: , “foo”(foo): “bar”}

当我们回到原始版本时,您可以很快理解这是如何有用的类型错误在这种情况下,通过上述反思,我们发现功能,它应该是一个字符串,实际设置为无类型我们知道这一点是因为哨兵已为我们捕获此错误,并自动提取每个帧的堆栈局部变量:

例外

这是我们在Sentry中构建的首批功能之一,直到今天,它仍然是我们能够提供的最有价值的调试组件之一。虽然我们无法始终为您提供重现异常所需的详细信息,但根据我们的经验,实际上我们很少需要手动工具来理解上下文并最终解决问题。

如果您对完整的实现感兴趣,它还利用了Python回溯结构中的其他各种组件,您可以随时查看我们的GitHub上的Python SDK源代码。在PHP和JVM中,由于运行时的不同,方法略有不同,如果您感兴趣,还可以在上找到这些存储库哨兵的GitHub。如果您正在使用Sentry,我们通常会自动为您插入指令,尽管JVM需要一些配置(文档很快就会发布)。

拇指蟒

博客

Python性能测试:综合指南

了解哪些测试可以评估Python应用程序的性能,有助于在开发过程中节省时间和精力。

阅读更多信息

更多来自哨兵博客

变更日志Codecov公司仪表盘发现狗粮编年史生态系统错误监视事件来宾帖子手机开放源代码性能监控发布运行状况资源SDK更新哨兵
©2024•Sentry是注册商标
Functional Software,Inc.的。