"我在测试中没有发现任何bug,这就意味着没有bug……对吗?"千万不要这样认为。由于大型软件的复杂程度很高,不管你做了多少测试,都不可能达到零bug的程度。因为你并不能揣测出用户的所有使用方式,因此,了解应用程序中错误和异常之间的差异,是非常重要的。为此,你要选择正确的方式来处理这些错误和异常,以积极的态度来确保应用的正常运行,对你的开发团队和最终用户负责。
即便你的测试是最彻底的,你依然只是在测试特定的情况,而且自己的偏好也在测试过程中发挥着作用,使得测试本身的客观性有失偏颇。
想象一下,数以千记的用户同时使用你的应用程序,同时也是以数以千计的方式来进行操作,这当中一定会遇到你没有测试过的状况。
简单的说,bug会导致错误和异常,这些错误和异常在不同的条件下有不同的意义,这要看你更看中哪一方面的问题。最主要的问题当然是如何更好的处理这些错误和异常,这样才不会带来消极的后果。
首先,让我们来看一些定义,以及为什么这些差异很重要。
一些编程语言对错误和异常有自己的定义,但我想定义这种差异。
先说错误。编程错误,通常无法继续和恢复,需要程序员进入程序并且修改代码来修复。有时候错误会转化为异常,以便它们可以在代码中被处理。错误可以通过简单的检查来避免,如果一些简单的检查不够,错误也可以转换为异常来处理,这样一来,应用程序就可以解决问题和保持起码的运行。
再说异常。当异常发生时,要考虑到不同编程语言的特性。异常可以被忽略或者捕获,所以代码可以恢复和处理这些情况,而不会令应用程序进入"错误"的状态。由于异常可以被忽略,所以应用程序在这种时候依然可以保持运行,未处理异常(这是错误的)也可以登录,所以,是否处理这些潜在异常就要看开发者了。来看看一些事例。
事例1:一个用户错误
当用户输入了错误的数据,可能暂时不需要太去处理,但这仍然可能导致程序出现错误和不可恢复的状态。毫无疑问,这时代码应该进行简单的检查来阻止错误状态的发生,你应该进行前端和后端的验证,抛出一个异常作为"最后的防御"。
事例2:文件打不开/下载异常
这是一种特殊情况,不至于破坏你的整个应用程序。你的应用程序应该能够处理这个问题。造成下载失败的原因很多,所以在程序设定的过程中,要做充足的预料和准备。Ok,以上就是我定义的错误与异常的区别了,这是易于遵循的过程,帮助你更好的处理错误。
"如果我捕获了每个异常,我的代码就是零差错了,对吗?"
就像我在前文中提到的,不是所有的错误都会导致异常。这个结论的主要问题是,你不知道什么是错的。你的代码可能有一些问题,通过捕捉异常而不做任何事情,您将丢失这些信息。不要只是查找异常,然后就一切如常。查找异常的目的是处理它们,并创造更适合运行的环境。
抛出和捕获异常是让应用程序自行恢复,并防止它运行到错误状态的一个好方法。如果你知道哪种异常可能被抛出,最好明确被捕获的各种异常都会导致哪种应用程序的停滞。(我们谈了一点关于软件构架错误报告的问题)
说到具体的异常类型,你可以向用户搜集反馈信息,这样你就知道具体导致程序出错的原因,就可以更好的处理这些情况了。
随着程序的运行,某些异常会损坏数据或以非正常的的方式运行。这会导致应用程序出错。如果你确切知道发生了哪些异常,您应该知道要遵循哪些步骤来恢复。或者,如果你无法恢复,你应该知道如何很好的处理这个情况。
那么,这能够恢复吗?很多时候,异常有足够的信息来知道出错了,在被捕获的异常中,有时可以从错误状态中恢复。你可以通过修复一些数据,数据重新获取,甚至要求用户再试一次来实现这一点。
你可以捕捉异常,但有时程序仍然无法运行,因为你依赖的数据已经以一种不可恢复的形式损坏,或者这些异常需要以不同的方式来解决。
比如,一个超出范围的数组异常,程序如何从中恢复?这是一个将错误转化为异常的示例。你的应用程序希望数据以某种方式存在,但这并没有发生。虽然恢复并不总是可能的,但现在有可能不进入错误状态和流畅的处理情况。如果在登录时出现异常,开发人员可以通过添加一些简单的检测来修复异常,在数组被访问或者改变它被访问之前。
有些异常是你不希望出现的,比如代码中的错误。你可以登录未被代码捕获的那些异常,许多语言都提供这种处理异常的方法。(例如.NET的application_error和javascripts全球的on_errorhandler)。任何未被处理的异常都会显示为错误,而错误是无法依靠代码自行修复的。所以,记录下这些错误,能便于你能找出其中的原因。这样一来,错误就不会被当做异常被忽略了。一旦这些异常出现,你就可以很快的解决掉它们。
错误日志可以帮助我们捕获错误。有了错误日志,你可以查看这些记录的错误和异常,这也是调试的关键,同时你还可以优先考虑什么时间修复哪些错误。你不必太依赖用户发来的截图和描述,更何况不是所有用户都会有兴趣来报错。错误日志可以让你的团队保持积极的行动力,一旦错误被发现,他们能够及时联系到用户,使其免受侵害。用户也会乐意接到你们的提醒,这还可以提升你的客户关系。当然,在用户使用到之前解决这些问题才是最关键的。
举例来说,一个导致计费有误的代码错误比不能显示特定详细页面这样的错误严重得多,即使不能显示详细页面这样的错误更容易出现。当你的应用程序出现异常时,你想要想办法去修复它,但是仅有1%的用户会主动报错,还有好多你不知道的错误存在潜在的隐患。
写些代码来保存异常和堆叠追踪,把它们存在文件里或者通过电子邮件发送,可以提示你这些错误的发生,这是一种可行的办法。举例来说,一个用户在运行中遇到许多异常,一百个用户可能遇到一些不太频繁出现的错误,哪一个比较重要呢?在不知道具体错误情况的条件下,影响更多用户的错误更为重要。
使用异常中的堆叠追踪可以帮助你找出错误所在的位置,并且你应该能够复制或读取代码以了解出错的原因。有时候这还不够,问题还需要进一步的追踪。如果发生这种情况,在登录之前向异常添加更多信息,包括上下文特定的详细信息(如帐户ID或特定对象状态),这些信息将允许你在本地复制错误。现在你应该能发现所有的错误和异常,并且记录未处理的了吧。
根据你应用程序的大小,错误提示的噪音也是个问题。你可以通过邮件过滤做些聪明的事情,比如帮助你把错误分组,这也只是部分的解决办法。几年前我这样做过,但很快意识到出现的问题太多,而这只是部分的解决办法。
问题在于,我依然不知道哪些错误对用户的影响最大。我专注于抛出最多的错误而不是用户体验中最麻烦的错误。正因如此,我从来没有真正摸清过哪些错误更为严重。我没有可视化的表示正在发生什么,但必须运行手动查询来计算出来,这是相当耗时的。
大型软件中的错误和异常非常常见,正确的处理错误将作为评判一个团队的依据,也是一个突破错误和异常,创造美好运行环境的过程。好的应用程序包含在可能时从异常中恢复的代码。处理和记录异常对您的软件的健康非常重要!
刘妮娜译
原文链接:https://dzone.com/articles/how-to-handle-errors-and-exceptions-in-large-scale