由使用者端自动取得当机回报- Get Crash Reports From User Automatically!(原文)

作者:周思博(Joel Spolsky)
属于Joel on Software, http://www.joelonsoftware.com

我曾经受雇于在美国一间最大的互联网服务供应商撰写客户端应用程式,所开发的产品拥有数以百万的用户,即使一个最罕见的错误都可能影响数百甚至数千的用户。虽然如此,每次当我们决定按下按钮以释出最新版本的程式时我还是充满信心。我还记得我曾经告诉我爸「这次的试用版本看起来棒极了。昨天我们在北美洲只收到十二个错误报告。」

十二个,嗯?还是十三个?

不,是十二个。我们应用了一种日渐普及、由用户端自动报告错误的技术。所得的报告都记录到错误追踪资料库内汇总成摘要,以便开发人员寻找并修正只在用户端发生的错误。这类错误往往难以在我们的测试实验室内测得到,因为事实上我们无法全部设置或模拟成客户所有超乎寻常的电脑组合。

错误侦测及报告功能正日渐普及。现在互联网浏览器和视窗系统都已经内建这类功能,而且越来越多客户希望他们的桌面应用程式懂得自行回报所遇到的错误。

能够知道世界上每一个角落找到的每个错误,对于建立一份你所开发的产品能在这个疯狂的世界上安然无恙的信心而言非常重要。尤其对于像开发消费者应用程式领域的我们而言更为重要。很多时候你不能倚靠客户告诉你他们所遇到的错误,他们可能没有足够的技术背景,更重要的是,要是没有自动报告错误的机制,他们根本不会有兴趣在忙着干自己重要的工作时还抽空给你发一份完整而有用的错误报告。

现在我创立了自己的公司,更深深地相信自动收集用户端错误的重要。差不多所有我们在Fog Creek Software编写的程式都有好些方法能透过我们的FogBugz错误追踪资料库回报错误。我们的产品CityDesk之内的产品,以至FogBugz本身(一个安装在客户端的网页伺服器上的网页应用程式)都内建了这项功能。两者皆可以透过互联网汇报错误。就连我们为内部开发的应用程式,比如说营运Fog Creek网上商店的电子商务程式,都会用相同的方式通知开发团队程式运作期间所遇到的错误。

Contents

[hide]

  • 1 剖释错误
  • 2 数据收集
  • 3 致电回家
  • 4 办认重覆的错误

剖释错误

好吧,你的程式当掉了。在几乎每个程式执行环境下,总有些方法能让你由某个地方回复、重新开始作业。以往我们只会让程式在出问题的的地方乖乖挂掉,现在我们选择弹出一个对话框。

这么多年来我学到一件事,就是你所问的问题越多,想回答的人就越少。所以现在我们只会问最少的而又能帮助分析错误的问题。比如说「你正在做什么?」、「你的电邮地址?」我们强调提供电邮地址是自愿性质,以排解关于个人隐私的疑虑。令人意外的是有些盲目恐惧是如此深入民心而且根深蒂固。在消费者市场内,许多人都已经被十一点钟新闻报导教育成永不要交出他们的电邮地址以避免收到垃圾邮件。结果是,如果你需要人们汇报错误的时候填写电邮地址,他们就干脆不提交报告。

基本上所有你所需要的资料都可自动取得,例如作业系统的版本,系统拥有的记忆体大小等等。

数据收集

接下来的问题是,要收集什么样的数据才可以帮助我们的开发人员找到造成错误的地方?我们很难抗拒把所有抓得到的数据都抓起来的这种诱惑。抓下每个你可以抓到的位元资讯,抓下用户系统内所有DLL及COM控制项的版本,以至把所有记忆体内的资料都倒出来制作一个备份(有些会叫这做核心转储Core Dump)。

然而在做了开发人员几年后、以及在从不知道自己拿着份核心转储资料可以干些什么的情况下,我终于发现这些都是非必要的数据。以下都是我们实际上会收集的数据:

  • 产品版本
  • 作业系统版本及微软Internet Explorer版本(有相当多的视窗功能都是来自Internet Explorer和它的元件,以致这项资料对于了解包括图象介面应用程式在内的程式对视窗作业系统上的表现非常重要)
  • 发生错误的程式档以及程式码行数
  • 错误讯息文字
  • 独一无二的错误码
  • 来自用户关于他们正在进行中的作业的描述
  • 用户的电邮地址

有这些就足够了!这些年来我们发现单是知道发生错误的程式码行数就已经足够帮助修正大部份的问题。在少数以这项资讯不足以解决问题的个案上,你可以透过直接联络遇上问题的用户以取得更多的资讯帮忙解决问题。收集少量资讯的好处在于令回报错误的程序变得非常快速,进而减少用户对回报问题感到不耐烦的机会。单是检查所有DLL和COM控制项的版本就可以花上好一段时间,如果数据是由数据机上传的话就又得花更多的时间,而这些数据是可以提供帮助修正错误的有用资讯。即使你发现错误只会在程式应用一个微软系统DLL的某个版本时才会发生,你又可以做些什么?你仍然需要改写程式来避开错误。

致电回家

感谢上天互联网的无处不在,在大部份情况下透过网页传送资讯都是把资讯送回家的最佳方法。只要送出一个标准的HTTP需求,你的错误报告几乎可以穿越于客户环境内任何种类的防火墙。更妙的是,现在几乎每一种编程环境都有内建的程式库支援发送HTTP需求及读取回应。举例来说,在视窗平台上你就可以在WININET程式库内找到内建的函数式去利用Internet Explorer的网络传输程式码来传送HTTP需求和读取回应。利用这些函数式最好的是,如果用户曾经设定他的浏览器透过代理伺服器来在防火墙内传取数据,WININET呼叫程序会自动采用相同的设定操作,而过程中你无付出任何额外的努力更改或设定你的程式就能正常发挥功用。

我们的FogBugz在收到HTTP需求送来的错误报告后,会传回一个很短的XML文件表示已经收到错误报告,文件亦同时包含一个展示给用户的讯息。关于这点,稍后我将会谈得更多。

如果你的应用程式是一个网页浏览器应用程式,你可以采用一个更简单的做法:展示一个包含一张可递交资料到伺服器上的表格的网页。你只需要将表格的行动路径指向你的错误追踪资料库即可。请参考图象2。

在好些种类的应用程式当中,你或许会想将错误报告先写到档案或登录内,等到用户重新启动应用程式时才送出,而非在错误发生时直接送出。我称呼这种技巧为延迟传送。这种做法虽然会延后收到报告的时间,但好处是一旦发生的错误严重至令你的应用程式瘫痪到无法送出错误报告时,你仍然可在稍后取得报告。

现在所有要送交Fog Creek的错误报告都会送抵一个位于我们公众服务伺服器上的URL。而我们的错误追踪资料库则由透过这个URL接收错误报告。事实上这个URL是公众可以接触到错误追踪资料库的唯一路径。除了接收报告以外的所有功能都已被锁死,客户可以提交错误报告,但无法读取资料库内的任何资料。

图象3展现的是当一个错误报告抵达我们的资料库时的模样。我们可以设定将收到的报告自动送给开发团队内的其中一位工作人员,不过最近为免打扰同事,我们选择将报告都送到被建立的虚拟人"CityDesk New Bug"手上。每次我们想要过滤出收到的报告,只需找出那些被分派到虚拟同事手中的错误,然后再逐一决定是否需要跟进修正。所有需要修正的错误将会被派到真实存在的同事手上。

办认重覆的错误

应用自动错误报告收集时很重要的一点是,同一个错误可以在许多不同的人手上发生许多次,而你绝不会希望每次同一个错误被报告时都会在你的错误追踪资料库内制做一个新的错误记录。我们应付这个情况的方法是以错误资讯内的重要资料为错误报告编上独特的识别字串。

字串的编码方法十分谨慎,分别来自两个用户的同一错误必须获编相同的识别字串。在好些实验以后,我们发现最好的编法就是在字串内加入错误码、档案名称、函数式名称、错误行数和产品版本。图象3内展示了识别字串"Error 91 (global:IsRoot:0) V1.0.32"。字串说明档案global.bas于执行函数式IsRoot位于第0行的程式码时发生91号错误,而产品的版本为1.0 build 32。相当偶然地,我们总是将编译版本数目为双数的产品编为内部使用版本,而编译版本数目为单数的则是会送交到客户手上的版本,这样我就可以一眼知道某个错误报告到底发生在开发人员或客户身上。

FogBugz往后遇到识别字串相同的当机回报时,会自动附加到这个案例中,并不会重开一个新案例。这样能让程式员在同一个地方看到所有相同的当机状况。

识别字串的格式设计得花点心思。我们以前会把当机错误讯息整个含在识别字串里。不过很快就发现错误讯息会被译成不同的语言,于是每种错误都会分散成英德西法及其他几种我认不出的语言。我们解决的方法是把错误讯息放在当机报告内,但是把不随语言而变的错误识别码放在报告的标题中,这些重覆的回报就少多了。

另外标题也经过特别安排,可以方便搜寻特定问题。由于我们在标题中使用「档名:函数名:行数」的格式(注意其中的冒号),所以只要搜寻「:函数名:」就很容易找出某函数相关的问题。我们基于相同的原因在版本号码前加上字母V,这样就能搜寻V1或V1.0或V1.0.32。如果没有这个V字母,想找版本1时就会找出所有标题里刚好有1字元的错误报告了。

当错误被确认时,我们可以把某个旗标(FogBugz介面里的Scout Will)由「继续回报」改成「停止回报」,之后就会忽略识别字串相同的当机回报。我们甚至可以设定一串文字讯息(在FogBugz介面里是针对自动送出的案例中出现的Scout Msg),可以自动传给往后遇到相同当机状况的使用者。当我们要使用避开错误的作法(workaround)时也会用这个功能提出建议。就像是「嘿,下次你要存档前要记得先拍拍自己的头再摸摸自己的肚子!」

重复回报的常见原因之一,就是当在当机处理程式本身。这并不一定是说当机处理的程式有问题,或许只是因为原先的当机太严重,以致再也没有程式能正常运行。