2013-10-17 40 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); 
    }; 
});