acm-header
登录

ACM通信

实践

在生产环境中调试AJAX


计算机代码

iStockPhoto.com

JavaScript语言有一段奇特的历史。它最初只是一个简单的工具,让Web开发人员向静态的Web页面添加动态元素,现在已经演变为交付基于Web的应用程序的复杂平台的核心。在早期,这种语言能够默默地处理失败被视为一种优势。如果图像翻转失败,最好保持无缝的Web体验,而不是向用户呈现难看的错误对话框。

这种对失败的容忍度已经成为现代浏览器的核心设计原则,错误会被无声地记录到隐藏的错误控制台。即使用户知道控制台,他们也只能找到少量的信息,假设脚本很小,一个指示文件和行号的消息就足以确定问题的根源。

然而,随着复杂AJAX应用程序的激增,这种假设不再成立,它已经永久地改变了JavaScript环境的设计中心。

脚本又大又复杂,跨越大量文件,大量使用异步动态实例化的函数。现在,脚本执行失败最多只能导致尴尬的体验。在最坏的情况下,应用程序停止工作或破坏服务器端状态。默认地接受脚本错误不再合适,在复杂的AJAX应用程序中,仅用一行数字和消息也不足以识别故障。因此,缺乏健壮的错误消息和本机堆栈跟踪已经成为AJAX开发的主要困难之一。

问题的严重程度取决于调试环境的性质。在开发过程中,工程师拥有几乎无限的自由。他们可以随意重新创建问题,启动交互式调试器,或快速修改和部署测试代码,提供快速形成和测试假设的能力,以确定问题的根本原因。但是,一旦应用程序离开这个避风港进入生产环境,一切都会改变。问题可能不可能在用户环境之外重现,因此访问系统进行交互调试通常是不可能的。运行测试代码,即使不需要停机,也可能比问题本身更糟糕。对于这些环境,事后调试问题的能力是必要的。当在生产中遇到错误时,必须保留足够的信息,以便准确地确定根本原因,并且必须以一种可以方便地从用户传输到工程人员的形式提供这些信息。

根据浏览器的不同,JavaScript有一组丰富的工具,用于在开发阶段识别问题的根源。Firebug、Venkman和内置DOM(文档对象模型)检查器等工具非常有价值。然而,与大多数语言一样,在生产中事情变得更加困难。理想情况下,我们希望能够获得JavaScript执行上下文的完整转储,但没有任何浏览器能够以安全或实用的方式支持这样的功能。这使得错误消息成为我们唯一的希望。这些错误消息必须提供足够的上下文来确定问题的根本原因,并且必须将它们集成到应用程序体验中,以便用户能够管理错误流,并了解如何将所需的信息提供给开发人员进行进一步分析。

此过程的第一步是提供在应用程序中显示错误的方法。尽管这很容易让人依赖alert ()它简单的弹出信息,与之相关的视觉体验是相当不和谐的。大量的文本不能很好地扩展到弹出窗口中,大量这样的错误可能需要不断地快速连续地取消对话框,有时使前进不可能。许多框架为此目的提供了内置控制台,但是一个非常简单的隐藏DOM元素可以很好地完成这项工作,它允许我们展开、折叠、清除和隐藏控制台。通过这个集成控制台,我们可以捕获和显示通常会丢失到浏览器错误控制台的错误。在大多数浏览器上,错误可以由顶层捕获window.onerror ()提供特定于浏览器的消息、文件和行号的处理程序。

简单地将这些消息转储到用户可见的控制台是向前迈出的重要一步,但在调试AJAX应用程序中的问题时,即使是准确的消息、文件和行号也可能毫无价值。除非错误是一个简单的排版错误,否则我们需要更好地理解遇到错误的上下文。

面对意想不到的错误,下一个问题几乎总是:“我们是如何以及为什么在这里?”如果幸运的话,我们可以只查看源代码并进行一些有根据的猜测。改进此过程的最常见方法是通过堆栈跟踪。生成堆栈跟踪的能力是健壮编程环境的标志,但不幸的是,这也是一个经常被忽视的特性。栈跟踪通常被认为难以构建,成本太高,无法在生产中使用,或者根本不值得努力实现。因为堆栈跟踪通常被视为只有在特殊情况下才需要的东西,所以堆栈跟踪的计算成本通常很高。然而,随着系统复杂性的增加和异步的使用范围的扩大,这种观点就变得不那么站得住脚了。例如,在消息传递系统中,一旦消息被移出队列,原始消息进入队列的上下文通常比失败的上下文更重要。在AJAX环境中(其中异步在这个缩略语中值得占有一席之地),对闭包的需要常常使实例化它们的上下文比闭包本身更有用。


当在生产中遇到错误时,必须保留足够的信息,以便准确地确定根本原因,并且必须以一种可以方便地从用户传输到工程人员的形式提供这些信息。


遗憾的是,JavaScript非常缺乏对堆栈跟踪的支持。支持堆栈跟踪的浏览器只通过抛出的异常提供堆栈跟踪,大多数浏览器根本不提供堆栈跟踪。栈跟踪在全局处理程序中永远不可用window.onerror (),因为参数是由一个针对最小公分母进行优化的DOM定义的。一个window.onexception ()作为异常对象传递的处理程序将是一个受欢迎的添加。相反,我们被迫显式地捕获所有异常。从表面上看,这似乎是一项艰巨的任务——我们不想把每段代码都包装在一个try/catch块中。然而,在AJAX应用程序中,所有JavaScript代码都在以下四种上下文中之一执行:

  • 加载脚本时全局上下文;
  • 从响应用户交互的事件处理程序;
  • 从超时或间隔;或
  • 从处理XMLHTTPRequest时的回调。

我们必须遵从第一种情况window.onerror (),但由于它发生在脚本加载时,这样的错误很难逃脱开发。对于其余的情况,我们可以通过我们自己的注册函数将回调自动包装在try/catch块中,如中所示图1一个

这里的表描述了从全局上下文中以及在为不同浏览器捕获特定类型的异常时可用的信息。该表演示了集成浏览器支持的限制。如果对每个异常都没有可靠的堆栈跟踪,我们就不得不生成编程的堆栈跟踪以获得更好的覆盖率。值得庆幸的是,语义参数对象允许我们编写函数以生成如中所描述的编程堆栈跟踪图2

完整的实现将提供一种跳过不感兴趣的帧的方法,包括本机堆栈跟踪(通过try/catch块),并提供一个toString ()转换结果的方法。我们没有文件号和行号,但有函数名和参数。不幸的是,JavaScript中匿名函数的泛滥使得获取函数的规范名称变得困难。的toString ()方法可以为我们提供特定函数的源代码,但在打印堆栈跟踪时,我们需要一个名称。实现这一点的唯一有效方法是搜索所有对象的全局名称空间,同时为函数构造一个人类可读的名称。这看起来很昂贵,但是只有在出现错误的情况下,我们才需要打印堆栈跟踪。大多数函数要么在全局名称空间中,在一个特定对象的原型中有一层深,要么在两层深。要获得函数的名称,我们只需要搜索窗口对象的成员、它们的所有子对象以及它们的原型对象的所有子对象。如果找到匹配项,则可以使用此沿袭构造名称。

通过函数名和参数,我们可以显示堆栈跟踪的合理副本,甚至在不支持堆栈跟踪的浏览器上也是如此。不过,需要注意的是,在Internet Explorer 7中无法获取函数名。由于不太容易理解的原因,在迭代窗口对象的成员时不包括全局函数。

仔细构造全局异常处理程序允许我们处理本机浏览器和动态生成的异常。尽管将堆栈跟踪附加到自定义异常是有用的,但在复杂环境中处理异步闭包(特别是异步XMLHTTPRequest对象)时,这种机制的真正威力是显而易见的。在复杂的AJAX应用程序中,所有服务器活动都必须异步发生;否则,浏览器将在等待响应时挂起。典型的服务模型如下所示图3一

中发生异常过程()函数,那么嵌入在服务实现中的包装器将捕获结果并将其传递给异常处理程序。但是堆栈跟踪将在过程(),当我们真正想要的是在点的堆栈跟踪dosomething ()被称为。因为我们的堆栈跟踪是按需生成的,组装起来成本很低,所以我们可以通过在调度每个异步调用之前记录堆栈跟踪,然后将其链接到任何捕获的异常来实现这一点。全局异常处理程序将打印异常的所有成员,显示进程中的两个堆栈跟踪。我们的核心调度程序看起来像这样图3 b.这允许使用相同的异常处理程序透明地处理服务器端故障。如果异步闭包生成了一个未预料到的异常,我们可以包含生成原始XMLHTTPRequest的上下文。

通过仔细地遵循这些设计原则,我们可以构建一个环境,通过使用户向开发人员提供更丰富的信息,允许进一步分析,从而极大地提高我们调试问题的能力。不幸的是,需要这个环境来克服当前JavaScript运行时环境的不足。如果没有单个点来处理所有未捕获的异常,我们就不得不将所有回调封装在一个try/catch块中;如果没有可靠的堆栈跟踪,我们就不得不生成自己的调试基础设施。显然,实现这两个特性的浏览器将很快成为AJAX应用程序的首选开发环境。在此之前,AJAX环境的精心设计仍然可以为应用程序的用户带来可调试性和可服务性方面的显著改进。

相关文章见queue.acm.org

转向AJAX(案例研究)
杰夫·诺沃克
http://queue.acm.org/detail.cfm?id=1515744

在异步世界中调试
迈克尔Donat说
http://queue.acm.org/detail.cfm?id=945134

调试设备
(Kode恶性列)
http://queue.acm.org/detail.cfm?id=1483103

回到顶部

作者

埃里克·施洛克自2003年起担任太阳微系统公司的工程师。schrock从Solaris内核组开始工作,在那里他从事ZFS工作,在过去的几年里,他作为公司Fishworks工程团队的一员,帮助开发Sun Storage 7000系列设备。

回到顶部

脚注

这里包含的示例的完整源代码以及浏览器支持表的最新版本,可以在http://blogs.sun.com/eschrock/resource/ajax/index.html上找到

文章开发由acmqueue
queue.acm.org

DOI: http://doi.acm.org/10.1145/1506409.1506423

回到顶部

数据

F1图1。自动处理异常。

F2图2。生成堆栈。

F3图3。包装异步请求。

回到顶部

UT1表格浏览器支持

回到顶部


©2009 acm 0001-0782/09/0500 $5.00

允许为个人或课堂使用本作品的全部或部分制作数字或硬拷贝,但不得为盈利或商业利益而复制或分发,且副本在首页上附有本通知和完整的引用。以其他方式复制、重新发布、在服务器上发布或重新分发到列表,需要事先获得特定的许可和/或付费。

数字图书馆是由计算机协会出版的。版权所有©2009 ACM, Inc.


没有发现记录

登录为完全访问
»忘记密码? »创建ACM Web帐号
文章内容:
Baidu
map