AngularJS - 忽略源映射的堆栈跟踪

时间:2013-10-17 07:24:50

标签: javascript angularjs source-maps

我已经编写了一个AngularJS应用程序,但这对于调试来说是一个噩梦。我正在使用Grunt + uglify来连接和缩小我的应用程序代码。它还会在缩小的JS文件旁边创建一个源映射。

当文件中存在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 个答案:

答案 0 :(得分: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);
  };
});

此代码将下载源代码,然后下载源映射,解析它们,最后尝试替换堆栈中的位置跟踪映射的位置。这在Chrome中完美运行,在Firefox中非常可接受。缺点是您要为代码库添加相当大的依赖项,并且从非常快速的同步错误报告转移到相当慢的异步错误报告。

答案 1 :(得分: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;
      });
    };
  });
})

这将生成两个堆栈跟踪,如Andrew Magee noted:一个由Angular格式化,另一个由浏览器格式化。第二个跟踪将应用源图。禁用重复项可能不是一个好主意,因为您可能还有其他Angular模块也可以处理可通过委派后调用的异常。

答案 2 :(得分:6)

我只是遇到了同样的问题并一直在寻找解决方案 - 显然这是一个一般的堆栈跟踪的Chrome问题,并且恰好适用于Angular,因为它在错误报告中使用堆栈跟踪。参见:

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

答案 3 :(得分:3)

我会看一下以下项目:https://github.com/novocaine/sourcemapped-stacktrace

它与@ jakub-hampl的答案基本相同,但可能有用。

答案 4 :(得分: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;
    };
  });
});

答案 5 :(得分:0)

正如Chrome中的错误has been fixed(但问题仍然存在于Angular中),不会打印出两次堆栈跟踪的解决方法就是:

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