2013-10-17 45 views
30

我写了一个AngularJS应用程序,但它证明了一点噩梦调试。我正在使用Grunt + uglify来连接和缩小我的应用程序代码。它还会在缩小的JS文件旁边创建源地图。AngularJS - 堆栈跟踪忽略源图

当文件中存在JS错误,但在AngularJS应用程序之外时,源映射似乎正常工作。例如如果我在其中一个文件的顶部写入console.log('a.b');,则Chrome调试器中记录的错误会显示原始文件的行+文件信息,而不是缩小的文件。

当Angular自身运行的代码出现问题时(例如在Controller代码中),就会出现问题。我从Angular获得了一个很好的堆栈跟踪,但它仅仅详细说明了缩小文件而非原始文件。

有什么我可以做,让Angular承认源地图?

举例如下错误:

TypeError: Cannot call method 'getElement' of undefined 
at Object.addMapControls (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:2848) 
at Object.g [as init] (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:344) 
at new a (http://my-site/wp-content/plugins/my-maps/assets/js/app.min.js:1:591) 
at d (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.min.js:29:495) 
at Object.instantiate (http://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.2/angular.min.js:30:123) 

回答

6

我有同样的问题,并已狩猎周围的解决方案 - 显然这是一个Chrome的问题与一般堆栈跟踪和发生,因为它使用堆栈适用于角错误报告中的痕迹。请参阅:

Will the source mapping in Google Chrome push to Error.stack

+1

而现在的错误是固定的铬42 :) Cf https://code.google.com/p/chromium/issues/detail?id=357958。 –

9

我能找到的唯一解决办法是硬着头皮分析源映射自己。这里有一些代码可以做到这一点。首先,您需要将source-map添加到您的网页。然后添加以下代码:

angular.module('Shared').factory('$exceptionHandler', 
function($log, $window, $injector) { 
    var getSourceMappedStackTrace = function(exception) { 
    var $q = $injector.get('$q'), 
     $http = $injector.get('$http'), 
     SMConsumer = window.sourceMap.SourceMapConsumer, 
     cache = {}; 

    // Retrieve a SourceMap object for a minified script URL 
    var getMapForScript = function(url) { 
     if (cache[url]) { 
     return cache[url]; 
     } else { 
     var promise = $http.get(url).then(function(response) { 
      var m = response.data.match(/\/\/# sourceMappingURL=(.+\.map)/); 
      if (m) { 
      var path = url.match(/^(.+)\/[^/]+$/); 
      path = path && path[1]; 
      return $http.get(path + '/' + m[1]).then(function(response) { 
       return new SMConsumer(response.data); 
      }); 
      } else { 
      return $q.reject(); 
      } 
     }); 
     cache[url] = promise; 
     return promise; 
     } 
    }; 

    if (exception.stack) { // not all browsers support stack traces 
     return $q.all(_.map(exception.stack.split(/\n/), function(stackLine) { 
     var match = stackLine.match(/^(.+)(http.+):(\d+):(\d+)/); 
     if (match) { 
      var prefix = match[1], url = match[2], line = match[3], col = match[4]; 
      return getMapForScript(url).then(function(map) { 
      var pos = map.originalPositionFor({ 
       line: parseInt(line, 10), 
       column: parseInt(col, 10) 
      }); 
      var mangledName = prefix.match(/\s*(at)?\s*(.*?)\s*(\(|@)/); 
      mangledName = (mangledName && mangledName[2]) || ''; 
      return ' at ' + (pos.name ? pos.name : mangledName) + ' ' + 
       $window.location.origin + pos.source + ':' + pos.line + ':' + 
       pos.column; 
      }, function() { 
      return stackLine; 
      }); 
     } else { 
      return $q.when(stackLine); 
     } 
     })).then(function (lines) { 
     return lines.join('\n'); 
     }); 
    } else { 
     return $q.when(''); 
    } 
    }; 

    return function(exception) { 
    getSourceMappedStackTrace(exception).then($log.error); 
    }; 
}); 

该代码会下载源,然后下载sourcemaps,解析它们,最后试图替换堆栈中的位置追踪映射位置。这在Chrome中完美工作,在Firefox中非常可接受。缺点是您要为代码库添加相当大的依赖关系,并且您需要从非常快速的同步错误报告转换为相当慢的异步错误报告。

+2

这适用于我,在Chrome中。我将'''_.map''改为''。$ .map''',以便它使用jQuery而不是underscore.js。我已经有了一个jQuery依赖,并且不想添加underscore.js。 – jcoffland

+0

在Firefox中适合我,奇妙的是,角度堆栈的痕迹已经让我烦恼了很多年。 – jazmit

+0

如果您不介意在控制台中记录两次异常,则可以包含[更小,更轻的代码片段](http://stackoverflow.com/a/33991279/1836776)。 –

0

根据this issue看来,Angular的$logProvider打破了源映射。像这样的解决办法,建议在该问题:

var module = angular.module('source-map-exception-handler', []) 

module.config(function($provide) { 
    $provide.decorator('$exceptionHandler', function($delegate) { 
    return function(exception, cause) { 
     $delegate(exception, cause); 
     throw exception; 
    }; 
    }); 
}); 
+2

这似乎并不适用于我。它对任何人有帮助吗? – pschuegr

+1

这对我也不起作用。 –

+0

似乎没有工作(使用Angular 1.2) – maxdec

9

Larrifax的answer是好的,但有功能的改进版本记录in the same issue report

.config(function($provide) { 

    // Fix sourcemaps 
    // @url https://github.com/angular/angular.js/issues/5217#issuecomment-50993513 
    $provide.decorator('$exceptionHandler', function($delegate) { 
    return function(exception, cause) { 
     $delegate(exception, cause); 
     setTimeout(function() { 
     throw exception; 
     }); 
    }; 
    }); 
}) 

这将产生两个堆栈跟踪,如安德鲁·马吉noted:一个由Angular格式化,另一个由浏览器格式化。第二个跟踪将应用源地图。这可能不是一个好主意,禁用重复,因为你可能有其他的Angular模块,也可以处理异常,可以在此之后通过委派调用。

+0

setTimeout在这里非常重要。我正在尝试Larrifax的原始黑客攻击,但遇到了其他无关的错误(因为抛出错误中断角度,大概)。 – tandrewnichols

+0

不错!这对我来说很有效,超时会让它超出Angular的控制范围,而Chrome会处理剩下的事情。 –

0

由于错误has been fixed在Chrome(但问题仍然存在角),不打印出堆栈跟踪解决方法两次会是这样:

app.factory('$exceptionHandler', function() { 
    return function(exception, cause) { 
     console.error(exception.stack); 
    }; 
});