获取 $http 调用的完整调用堆栈跟踪

Posted

技术标签:

【中文标题】获取 $http 调用的完整调用堆栈跟踪【英文标题】:Get the full call stack trace of $http calls 【发布时间】:2014-08-13 10:06:33 【问题描述】:

假设有人在名为 app.js 的文件中编写了这样的方法,试图针对不存在的 url 执行 XHR 请求:

app.controller('MainCtrl', function($scope,$http) 
  $scope.send = function() 
    $http.get('http://run.plnkr.co/thisIs404');
  ;
);

我可以在控制台和网络面板中看到有关 URL http://run.plnkr.co/thisis404 的错误:

为了调试它,我想快速找到这个 XHR 调用在源代码中的位置(即找到 app.js 文件):

所以我在 chrome 开发工具中启用:

调用堆栈中的异步调试 调试任何 XHR

调试器实际上在 XHR 请求时停止,但调用堆栈只显示对 angular.js “核心”文件的引用:没有引用 app.js 任何地方都可以找到 .

我用 chromium 36 和 chrome 35 尝试了这个。唯一的解决方案:在整个代码库中搜索错误的 URL(在某些情况下可能很难做到)。

异步调试模式不应该指向堆栈中的app.js somwhere 吗? 有没有办法从控制台错误中轻松追踪这个app.js 文件?

对于普通 XHR 请求(即没有角度),XHR 调试调用堆栈会在 app.js 中显示 XHR 调用(在这种情况下更容易调试):

此处为完整示例:http://plnkr.co/edit/lnCRpv?p=preview

[编辑] 正如我被问到的那样:Angular.js 在我的测试中没有被缩小。

【问题讨论】:

【参考方案1】:

所以,你看,这个问题主要是因为 Angular 的 $http 很糟糕。对此感到抱歉。

让我们尝试使用 bluebird 库,因为它提供了长堆栈跟踪。

Promise.longStackTraces();
Promise.resolve($http.get('...'));

您会得到以下堆栈跟踪:

Possibly unhandled Error: [object Object]
    at Promise$_rejectFromThenable (http://cdn.jsdelivr.net/bluebird/1.2.4/bluebird.js:4736:52)
    at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
    at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
    at wrappedErrback (https://code.angularjs.org/1.3.0-beta.5/angular.js:11334:78)
    at https://code.angularjs.org/1.3.0-beta.5/angular.js:11467:76
    at Scope.$eval (https://code.angularjs.org/1.3.0-beta.5/angular.js:12418:28)
    at Scope.$digest (https://code.angularjs.org/1.3.0-beta.5/angular.js:12230:31)
    at Scope.$apply (https://code.angularjs.org/1.3.0-beta.5/angular.js:12522:24)
    at done (https://code.angularjs.org/1.3.0-beta.5/angular.js:8207:45)
    at completeRequest (https://code.angularjs.org/1.3.0-beta.5/angular.js:8412:7) 

(Plunker here.)

最重要的一行是第一行:Possibly unhandled Error: [object Object]

是的。抛出一个对象,而不是真正的 Error 对象,并附加了 stack 属性。作为参考,这里是如何抛出错误并保持它的堆栈:https://github.com/Ralt/newerror/blob/master/index.js

那么,如何解决这个问题?这取决于几个决定:

您想添加一个适当的 Promise 库来启用长堆栈跟踪吗? 是否要使用另一个会引发正确错误的 xhr 库?

如果要添加真正的 Promise 库,请使用 bluebird。 AFAIK,它是提供长堆栈跟踪的少数几个之一,而且它是目前最快的。

对于一个会引发真正错误的正确 xhr 库,恐怕你在那里不走运。不过,编写一个支持您想要的浏览器的自定义版本并不难。在不支持 IE8 的情况下,这可以工作(使用 bluebird):

function xhr(url) 
    return new Promise(function(resolve, reject) 
        var xhr = new XMLHttpRequest();
        xhr.onload = function() 
            resolve(xhr.responseText);
        ;
        xhr.onerror = reject;
        xhr.open('GET', url);
        xhr.send();
    );

(Plunker here.)

如您所见,堆栈跟踪信息丰富:

【讨论】:

是的,这个答案是正确的——这是因为 Bluebird 会进行未处理的拒绝跟踪,而 Angular 不会——请参阅This question,了解如何将 bluebird 与 AngularJS 一起使用。 AngularJS Promise 的堆栈跟踪非常糟糕,这使得在现实世界场景中使用它们非常困难。 我们可以修改angular的http函数来使用bluebird,这样它就可以在不修改现有项目的情况下工作吗?以下内容呢:plnkr.co/edit/1boCEqUjSLx8zGX2uqSo【参考方案2】:

XHR 请求堆叠在$http.pendingRequests 数组中,稍后发送。这就是为什么在调用 $http 的位置和发出实际 XHR 请求的位置之间找不到直接联系的原因。

如果你想知道哪个函数调用了$http,你必须在$http函数中设置一个断点。

在我看来,这有点违背了整个“XHR 断点”的目的。

【讨论】:

【参考方案3】:

我想到的一个选项是创建一个用于 $http 调试的模块,您可以在需要调试 $http 调用时将其作为依赖项添加到主模块中。可以注册 $http 服务的装饰器,它将简单地记录调用的参数并将其转发到 $http 服务。也可以设置断点。

我创建了一个简单的工作示例here。我希望它会有所帮助。

$http 记录器装饰器实现示例:

var httpDebugging = angular.module('http-debugging', []);

httpDebugging.decorator('$http', function ($delegate) 
  var debugAware = function (fnCallback) 
    return function () 
        var result = fnCallback.apply(this, arguments);    
        console.log('$http decorator: ', arguments);
        return result;
    ;   
  ; 

  for(var prop in $delegate) 
    if (angular.isFunction($delegate[prop])) 
        $delegate[prop] = debugAware($delegate[prop]);
    
  

  return $delegate;
);

【讨论】:

嗨,这给出了 $http 被调用的信息,这很有帮助,但是在 $http 函数上放置一个断点可以达到同样的效果。是否可以提取调用者(最好是行号,它启动了 $http 请求? 是的,购买在 console.log() 语句上添加断点,您可以在 javascript 调用堆栈上看到调用的第一个函数。 太棒了。我将使用 stacktracejs.com 更新它以包含整个调用堆栈 404 ajax 请求呢?你能得到导致它的代码行吗? 可能是的,通过将 $http 装饰器与拦截器结合使用。在拦截器中,您将获得响应代码。

以上是关于获取 $http 调用的完整调用堆栈跟踪的主要内容,如果未能解决你的问题,请参考以下文章

xperf 调用堆栈跟踪,特定于 dll

如何获取 gdb 调用堆栈跟踪?

当方法被意外调用的次数超过指定次数时,有没有办法从 rspec 获取堆栈跟踪?

获取完整的字符串堆栈跟踪,包括内部异常

C++ 堆栈跟踪问题

如何获取异步执行的完整堆栈跟踪