it-swarm.cn

如何编写好的异常消息

我目前正在进行代码审查,并且我要注意的事情之一是异常消息似乎在不断重申where发生异常的异常数量。例如.

throw new Exception("BulletListControl: CreateChildControls failed.");

我可以从异常的其余部分中得出该消息中的所有三个项目。我从堆栈跟踪中知道该类和方法,并且我知道它失败了(因为我有一个异常)。

这让我开始思考应该在异常消息中放入什么消息。首先,出于一般原因(例如PropertyNotFoundException-why),我创建了一个异常类(如果尚不存在),然后当我抛出该异常时,该消息指示出了问题(例如,“在Node 1234)上找不到属性'IDontExist'”-what。)。StackTrace中的位置。 when可能会出现在日志中(如果适用)。how是供开发人员确定(并修复)的

您还有其他引发异常的技巧吗?特别是关于创建新类型和异常消息。

102
Colin Mackay

我将把答案更多地指向异常之后发生的事情:它有什么好处,软件应该如何运行,您的用户应该如何处理异常?我在职业生涯初期遇到的一项很棒的技术是始终按以下三个部分报告问题和错误:上下文,问题和解决方案。使用此准则将极大地改变错误处理,并使软件大大改善,供操作员使用。

这里有几个例子。

Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.

在这种情况下,操作员确切知道该怎么办以及必须影响哪个文件。他们还知道连接池更改没有发生,应该重复进行。

Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem.  The email will be sent later. You may want to tell '[email protected]' about this problem.

我编写服务器端系统,而我的操作员通常都是精通技术的第一线支持。对于具有不同受众但包含相同信息的台式机软件,我将编写不同的消息。

如果使用这种技术,将会发生一些奇妙的事情。软件开发人员通常最擅长了解如何解决自己的代码中的问题,因此,在编写代码时以这种方式编码解决方案对于处于劣势的最终用户来说是巨大的利益,因为他们经常缺少有关以下方面的信息该软件到底在做什么。曾经读过Oracle错误消息的任何人都会知道我的意思。

想到的第二件事是,当您发现自己试图描述异常中的解决方案时,您正在编写“检查X,如果A则B否则C”。这是一个非常明显的迹象,表明在错误的位置检查了您的异常。您的程序员有能力比较代码中的内容,因此“ if”语句应在代码中运行,为什么要让用户参与一些可以自动化的事情?很有可能是代码深处的原因,有人做了偷懒的事情,并从任意数量的方法中抛出了IOException,并在无法充分描述出什么问题,specific上下文及其修复方法。这鼓励您编写更细粒度的错误,在代码中的正确位置捕获并处理它们,以便您可以正确表达操作员应采取的步骤。

在一家公司中,我们拥有一流的运营商,他们非常了解该软件,并保留了自己的“运行手册”,从而丰富了我们的错误报告和建议的解决方案。为了认识到这一点,该软件开始在例外情况下包括指向运行手册的Wiki链接,以便提供基本的说明以及操作员随时间推移可以进行更高级的讨论和观察的链接。

如果您有尝试该技术的准则,那么在创建自己的代码时应在代码中命名异常的情况就变得更加明显。 NonRecoverableConfigurationReadFailedException成为您要向操作员更全面描述的内容的简写形式。我喜欢冗长,我认为这对于下一个接触我的代码以进行解释的开发人员来说会更容易。

72
Sir Wobin

这个最近的问题 中,我指出了异常根本不应该包含任何消息。我认为,他们这样做的事实是一个巨大的误解。我建议的是

异常的“消息”是异常的(完全限定)类名

异常应在其自己的成员变量中包含尽可能多的有关所发生事件的详细信息。例如,IndexOutOfRangeException应该包含被发现无效的索引值,以及引发异常时有效的上限值和下限值。这样,通过反射,您可以自动构造一条消息,其内容如下:IndexOutOfRangeException: index = -1; min=0; max=5,以及堆栈跟踪,应该是解决问题所需的所有客观信息。将其格式化为漂亮的消息(如“索引-1不在0到5之间”)不会添加任何值。

在您的特定示例中,NodePropertyNotFoundException类将包含未找到的属性的名称,以及对不包含该属性的节点的引用。这很重要:它应该not包含节点的名称;它应该包含对实际节点的引用。在您的特定情况下,这可能不是必需的,但这是一个原则问题,是一种首选的思维方式:构造异常时,主要的考虑是必须由可能捕获该异常的代码来使用它。人类的可用性是一个重要的问题,但仅是次要的问题。

这可以解决您在职业生涯中某个时候可能遇到的令人沮丧的情况,在这种情况下,您可能会捕获到一个异常,该异常包含有关消息文本中发生的事情的重要信息,而不是有关其成员变量中发生的事情,因此您为了找出发生了什么,必须对文本进行字符串解析,希望消息文本在基础层的将来版本中保持不变,并祈祷当您的程序被使用时,消息文本不会使用某种外语。在其他国家/地区运行。

当然,由于异常的类名是异常的消息(而异常的成员变量是特定的详细信息),这意味着您需要很多不同的异常来传达所有不同的消息,并且那样就好。

现在,有时候,当我们编写代码时,遇到一种错误的情况,我们只是想快速编写throw语句并继续编写代码,而不必中断我们正在做的事情来创建新的异常类,以便我们可以将其扔在那里。对于这些情况,我有一个GenericException类,它实际上接受字符串消息作为构造时参数,但是此异常类的构造函数装饰有很大的巨大的明亮的紫色FIXME XXX TODO注释,指出在释放软件系统之前(最好在提交代码之前),必须用一些更特殊的异常类的实例替换此类的每个实例。

23
Mike Nakis

通常,通过提供有用的信息(期望值,实际值,可能的原因/解决方案等),例外应有助于开发人员查明原因

当没有任何内置类型有意义时,应创建新的异常类型。特定类型使其他开发人员可以捕获特定异常并进行处理。如果开发人员知道如何处理您的异常,但类型为Exception,则他将无法正确处理该异常。

13
mbillard

在.NET中,永远不要throw new Exception("...")(就像问题的作者所显示的那样)。 Exception是Exception的根类型,并且不应直接抛出。而是抛出一种派生的.NET异常类型,或者创建自己的自定义异常(从异常(或另一种异常类型)派生)。

为什么不抛出异常?因为抛出Exception并不能描述您的异常,并且会迫使您的调用代码编写catch(Exception ex) { ... }之类的代码,这通常不是一件好事! :-)。

4
bytedev

您想要寻找的“添加”到异常的东西是那些不在异常或堆栈跟踪中的数据元素。那些是“消息”的一部分还是在登录时需要附加是一个有趣的问题。

正如您已经指出的,该异常可能会告诉您什么,stacktrace可能会告诉您在哪里,但是“为什么”可能会涉及更多(应该是,有人希望),而不仅仅是看一两行然后说“ h!当然”。当在生产代码中记录错误时,情况更是如此-我经常被错误的数据所困扰,这些错误的数据已进入测试系统中不存在的实时系统。只需知道导致错误的数据库中记录的ID是什么,就可以节省大量时间。

所以...列出,或者对于.NET,添加到记录的异常数据收集中(参见@Plip!):

  • 参数(这可能会有点有趣-如果它不会序列化,则有时不能将其添加到数据集合中,有时单个参数可能会非常复杂)
  • ADO.NET或Linq返回到SQL或类似的附加数据(这也可能会很有趣!)。
  • 还有什么可能不明显。

当然,有些事情直到您在初始错误报告/日志中没有它们时才知道您需要。在发现需要这些东西之前,您不会意识到自己可以获得的一些东西。

2
Murph

什么是例外for

(1)告诉用户出了什么问题?

这应该是万不得已的方法,因为您的代码应该进行干预,并向他们显示比“异常”更“有趣”的东西。

“错误”消息应清楚而简洁地指出what出错了,并且用户可以采取什么措施来从错误情况中进行recover

例如“请不要再按此按钮”

(2)告诉开发人员何时出错?

这是您登录文件以进行后续分析的一种方式。
Stack Trace会告诉开发人员where代码损坏了;该消息应再次表明what出错。

(3)告诉异常处理程序(即代码)出了什么问题?

Exception的Type将决定看哪个异常处理程序,而在Exception对象上定义的properties将允许处理程序对其进行处理。

异常的消息是完全不相关

0
Phill W.