编程错误
也许软件开发曾经是不同的。 也许您花了数小时来设计算法,然后挤在白板上,直到标记用完,浑身都是汗水和干dried的咖啡斑点。
对于我们大多数人而言,情况已不再如此-大多数现代软件开发都包含使用正确的术语进行搜索。
![](https://i-blog.csdnimg.cn/blog_migrate/41393429d53d798731ad0629a8f653ca.png)
现代编程工作流程。 Google有问题,找到解决方案。 Google下一个问题。
令人惊讶的是,弄清楚“正确的术语”是什么可能非常困难。 特别是如果您不熟悉编程或正在使用的技术。
这是有关如何搜索错误消息的系列文章的第一篇。 但是,要找到正确的搜索词,我们首先需要了解如何阅读错误消息以及哪些部分相关。 我将尝试始终使用不同的编程语言,以便我们可以看到它们之间的差异和相似之处。 让我们潜入吧!
错误消息剖析
错误消息由两部分组成, 错误消息和堆栈跟踪 。
错误消息是“出了什么问题” —好的错误消息会有所帮助,并告诉您应该做什么,而错误的错误消息有时看起来像是可以召唤克苏鲁的东西。
![](https://i-blog.csdnimg.cn/blog_migrate/cffd6e795a6351477b231102c693b23e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/8a759c2ce3d46faea7501680747b73d8.png)
另一方面,堆栈跟踪是“哪里出错了”。
请注意,堆栈跟踪在不同的语言中具有不同的名称,Python将其称为回溯-但是核心概念是相同的。
![](https://i-blog.csdnimg.cn/blog_migrate/f312d50b3b630fe9ce083d1c7d3db9ac.png)
现在,当您搜索发生了什么问题时,您比堆栈跟踪更常使用错误消息。 堆栈跟踪很有用,因为它可以告诉我们各种上下文信息,例如,您在哪一行出错了,或者哪个库在抛出错误。
JavaScript错误消息剖析
让我们尝试看一个非常小JavaScript程序,看看如何使用错误消息和堆栈跟踪。
这是相当简单的程序。 它由三个文件组成。 第一个index.js
是应用程序的启动位置,下一个some-random-file.js
并没有做什么用-它只是用来延长堆栈跟踪的时间, server.js
启动了一个侦听端口3000的express
服务器。
![](https://i-blog.csdnimg.cn/blog_migrate/2454b325885afc8362906eb0ee182ece.png)
如果我们不小心在同一时间两次运行了该程序,则会收到错误消息-端口已被占用。 我们将获得以下错误消息和堆栈跟踪。
![](https://i-blog.csdnimg.cn/blog_migrate/0d1cee718903c1504bc21eb7cb8849c1.png)
堆栈跟踪(通常)从底部的最早条目到顶部的最新条目。 让我们看看是否可以将这一部分分成几部分。
启动噪声
这些通常在使用框架时发生。 它们的共同点是它们通常是运行时初始化逻辑,并且在大多数情况下,它们与我们的实际错误无关。
入门时可能很难分辨出启动噪声是什么,但是在看到一些错误之后,您通常会很快学会如何对其进行调试。 一条好的经验法则是,在代码的第一行和堆栈跟踪的开始之间的任何内容都可能具有启动噪声。
在此特定示例中,第一行是启动噪声。
您的密码
这是您编写的代码。 通常,您可以从文件名或路径中分辨出是什么代码。 在这种情况下,我们可以看到:
"/home/geewee/WebstormProjects/myproject/index.js:5:1"
,这是我们index.js
文件的路径。
我们的代码有两个部分比其他部分要特别。 代码的第一行很重要,因为它的前几行通常是启动噪声。
但是,最重要的部分是代码的最后一行。
代码的最后一行。
这通常是魔术发生的地方。 您要查找的是您编写的堆栈跟踪的最后一行。 这意味着最后一行不是来自您所使用的语言运行库或库。 通常这是最有趣的一行,因为这是您可能犯错误的最后一行。
在此特定示例中,我们代码的最后一行是: "module.exports.startServer (/home/geewee/WebstormProjects/myproject/server.js:8:9)"
这意味着server.js第8行是开始寻找我们做错了什么的好地方。
代码最后一行之后的行
如果存在,这行很重要,因为它通常会告诉我们错误发生在哪个库或第三方代码中。在这种情况下,该行是:
"Function.listen (/home/geewee/WebstormProjects/myproject/node_modules/express/lib/application.js:618:24)
”
这里值得一提的是,路径是从我们的项目目录到node_modules\express\
。
现在, node_modules/
是Javascript存储库的目录。 这意味着该错误在express
库中发生。 这很有用,因为我们在弄清楚以后应该搜索的内容时想知道这一点。
内部第三方通话
如果实际错误发生在第三方库中,则在抛出实际错误之前通常会进行一些内部调用。 内部第三方调用是代码的最后几行和实际错误消息之间的行。 他们有时会提供更多有关到底出了什么问题的背景信息,但通常它们并不那么重要。
实际的错误信息:
最后一个重要部分是实际的错误消息。 它不一定位于堆栈跟踪的最顶部,但通常非常接近。 在这种情况下,这部分是:
Error: listen EADDRINUSE :::3000
这是我们将在搜索查询中使用的部分。 在这种情况下,错误消息可以粗略地翻译为“正在使用的地址3000”-但是您可能会因为不知道而被原谅。 这不是一个很好的错误消息。
再次查看整个错误消息,并标注不同的部分,如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/896ec1d3bee0c0c11004d32ef483afed.png)
Python错误消息剖析
让我们尝试看看Python中的相同情况,以了解堆栈跟踪因语言而异的情况。 这和以前使用相同文件的程序基本相同。 唯一的区别是,它使用名为bottle
的Python框架而不是express
来监听端口3000。
![](https://i-blog.csdnimg.cn/blog_migrate/46d72053c0192b26ecac058fcba23e6b.png)
和以前一样,如果我们运行程序的多个实例,则会收到错误消息:
![](https://i-blog.csdnimg.cn/blog_migrate/02067184cb9d89bc37fdf5ef3c8759fc.png)
此堆栈跟踪有一些显着的区别(Python首选将其称为回溯。)最明显的是,它的顺序不同。
在Javascript示例中,最旧的调用位于底部,但是正如该python示例所指出的那样,此处的最新调用位于底部。
也没有启动噪声,堆栈跟踪从发生错误的代码行开始。
除此之外,其余部分都是相同的,让我们仔细看一下。
有“我们的代码”-第一行和最后一行。 第一行也是堆栈跟踪的第一行:
File "index.py", line 5, in <module> some_random_function_that_starts_a_server()
这是我们代码的最后一行:
File "/home/geewee/PycharmProjects/myproject/server.py", line 12, in start_server run(host='localhost', port=3000)
然后是第三方代码的第一行:
File "/home/geewee/PycharmProjects/myproject/venv/lib64/python3.7/site-packages/bottle.py", line 3127, in run server.run(app)
值得注意的是/site-packages/
是python存储第三方库的地方。 因此,基于该目录,我们知道下一个调用是第三方库。 读取目录名称bottle/
-我们可以猜测错误来自bottle
库。
然后在瓶子库中有内部第三方调用,最后是实际的错误消息:
"OSError: [Errno 98] Address already in use"
比来自express的要好一些,但仍然不如预期的好。
如果我们用不同的部分注释堆栈跟踪,则如下所示:
![](https://i-blog.csdnimg.cn/blog_migrate/d345cd8b1167e23d09dfe3c29c19dacc.png)
五十种痕迹
现在,我们在这里看到的两条堆栈跟踪相当相同-可能存在一些启动噪音,一些代码以及在第三方库中引发的错误。 现在,并非所有错误条件都像这样,所以也不是所有堆栈跟踪都一样。
不允许第三方
有时没有第三方调用,如以下Java示例所示:
![](https://i-blog.csdnimg.cn/blog_migrate/749fe91ef3c3277925d527c6a7a29c81.png)
该程序试图找出分配给null
的java String
的长度。 运行该程序为我们提供了非常小的堆栈跟踪:
Exception in thread "main" java.lang.NullPointerException at Main.randomFunction(Main.java:9) at Main.main(Main.java:4)
请注意,没有启动噪音,没有第三方代码。 只有我们的代码,以我们犯错的确切行结尾。 如果没有第三方代码,那么“代码的最后一行”就是检查错误原因的地方。
杜德,我的代码在哪里?
现在让我们来看一个使用名为Spring Boot的大型框架的示例。 Spring适用于Java和Kotlin。 这个例子在Kotlin。 现在,我们将导致错误而没有真正使我们的代码出现在堆栈跟踪中。 为此,我们可以定义一个非常小的应用程序,其中包含两个都希望侦听同一/hello
HTTP端点的类。 Spring无法确定哪个类应处理/hello
端点,因此在启动时崩溃。
![](https://i-blog.csdnimg.cn/blog_migrate/4dfeada584a109630e8c827996d32281.png)
我们将获得的堆栈跟踪信息就是这个庞然大物:
![](https://i-blog.csdnimg.cn/blog_migrate/292733e351da0a4f49fefa6baa79d105.png)
我们不会对此进行深入剖析,因为它很大。 我们的主要方法在其中某处,但是该方法实际上并没有做很多事情。 相反,堆栈跟踪主要由Spring框架的内部第三方调用控制。 如果我们剥离内部的第三方调用并删除最后一部分,因为那只是内部的Spring异常,而不是那么重要,那么我们将会更容易理解。
![](https://i-blog.csdnimg.cn/blog_migrate/6a6972f736605545f3b4e7be1470c44a.png)
在这里,更容易找到实际的错误消息,幸运的是,该消息提供了有意义的信息:
Ambiguous mapping. Cannot map 'restController' method public java.lang.String dk.gustavwengel.myproject.RestController.helloWorld() to {[/hello]}: There is already 'identicalRestController' bean method
如果您使用的是大型框架,则可能会发生很多情况,即堆栈跟踪中根本没有您的代码。 这通常意味着这是一个配置问题,但是这些错误很难调试。 通常,我们依靠框架作者提供的有用图像来帮助我们度过这些错误,因为堆栈跟踪几乎是无用的。
我们已经看到了如何获取错误消息并将其分为不同的部分。 我们已经讨论了哪些部分很重要,哪些部分通常可以忽略。 在下一部分中,我们将讨论如何在搜索时选择正确的术语,以及如何搜索错误消息实际上是一个随处学习的过程,而不仅仅是找到答案的过程。
您为下一部分感到兴奋吗? 您是否对此有任何想法,还是只想了解一下Elm错误消息的效果如何? 通过 @GeeWengel 在Twitter上与我 联系
翻译自: https://hackernoon.com/how-to-read-programming-error-messages-22795982c217
编程错误