2013-10-17 4 views
30

Ich habe eine AngularJS-App geschrieben, aber es ist ein bisschen wie ein Albtraum zu debuggen. Ich verwende Grunt + uglify, um meinen Anwendungscode zu verketten und zu minimieren. Es erstellt auch eine Quellkarte neben der verkleinerten JS-Datei.AngularJS - Stack-Trace ignorieren Quellkarte

Die Quellkarte scheint ordnungsgemäß zu funktionieren, wenn in der Datei ein JS-Fehler auftritt, jedoch außerhalb der AngularJS-Anwendung. z.B. Wenn ich console.log('a.b'); oben in einer der Dateien schreibe, zeigt der im Chrome-Debugger protokollierte Fehler die Zeile + Dateiinformationen für die Originaldatei an, nicht die minimierte.

Das Problem tritt auf, wenn ein Problem mit Code vorliegt, den Angular selbst ausführt (z. B. im Controller-Code). Ich bekomme eine nette Stapelspur von Angular, aber es gibt nur die verkleinerte Datei an, nicht das Original.

Gibt es etwas, was ich tun kann, damit Angular die Quellkarte erkennt?

Beispiel Fehler unter:

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) 

Antwort

6

Ich hatte gerade das gleiche Problem und haben nach einer Lösung, um gejagt - anscheinend ist es ein Chrome Problem mit Stack-Traces im Allgemeinen und geschieht auf Angular anzuwenden, da es Stapel verwendet Spuren in der Fehlerberichterstattung. Siehe:

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

+1

Und der Fehler behoben ist jetzt in Chrom 42 :) Cf https://code.google.com/p/chromium/issues/detail?id=357958. –

9

Die einzige Lösung, die ich finden konnte, ist die Kugel zu beißen und zu analysieren, die Quelle selbst abbildet. Hier ist ein Code, der dies tun wird. Zuerst müssen Sie source-map zu Ihrer Seite hinzufügen. Dann diesen Code hinzu:

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); 
    }; 
}); 

Dieser Code den Quellcode herunterladen, dann die sourcemaps herunterladen, analysiert sie, und schließlich versucht, die Positionen in dem Stapel ersetzen Sie die abgebildeten Stellen verfolgen. Dies funktioniert perfekt in Chrome und ziemlich akzeptabel in Firefox. Der Nachteil besteht darin, dass Sie Ihrer Codebasis eine relativ große Abhängigkeit hinzufügen und dass Sie von einer sehr schnellen synchronen Fehlerberichterstattung zu einer ziemlich langsamen asynchronen Fehlerberichterstattung wechseln.

+2

Das hat bei mir in Chrome funktioniert. Ich habe '' _.map''' in '' '.map''' geändert, so dass jQuery anstelle von underscore.js verwendet wird. Ich hatte bereits eine jQuery-Abhängigkeit und wollte keine Unterstreichung.js hinzufügen. – jcoffland

+0

Funktioniert für mich in Firefox, fantastisch, die eckigen Stapelspuren haben mich seit Ewigkeiten gestört. – jazmit

+0

Wenn es Ihnen nichts ausmacht, dass die Ausnahme zweimal in der Konsole protokolliert wird, können Sie ein [kleineres, leichteres Codefragment] einfügen (http://stackoverflow.com/a/33991279/1836776). –

3

würde ich einen Blick auf das folgende Projekt nehmen: https://github.com/novocaine/sourcemapped-stacktrace

Es ist im Wesentlichen dasselbe wie die Antwort von @ jakub-Hampl aber nützlich sein könnte.

+0

Haben Sie es versucht? Es scheint nicht für mich zu funktionieren .. Es funktioniert nicht von innen irgendein Winkel Modul/Direktive/etc Code – laggingreflex

0

Nach this issue scheint Angular $logProvider Quellmapping zu brechen. Eine Abhilfe wie dies in der Frage vorgeschlagen:

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

Dies scheint nicht für mich arbeiten. Hat es irgendjemandem geholfen? – pschuegr

+1

Das hat auch nicht für mich funktioniert. –

+0

Scheint nicht zu funktionieren (mit Angular 1.2) – maxdec

9

Larrifax des answer gut ist, aber es ist eine verbesserte Version der Funktion in the same issue report dokumentiert:

.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; 
     }); 
    }; 
    }); 
}) 

Diese zwei Stack-Traces erzeugen wird, als Andrew Magee noted: eine formatiert von Angular, dann eine zweite vom Browser formatiert. Die zweite Ablaufverfolgung wendet Quellkarten an. Es ist wahrscheinlich keine gute Idee, die Duplikate zu deaktivieren, weil Sie möglicherweise andere Angular-Module haben, die auch mit Ausnahmen funktionieren, die danach über die Delegierung aufgerufen werden können.

+0

Der setTimeout ist hier kritisch. Ich habe Larrifax's ursprünglichen Hack versucht, aber andere nicht zusammenhängende Fehler bekommen (weil das Werfen des Fehlers angular vermutlich abbricht). – tandrewnichols

+0

Schön! Das funktioniert für mich, die Auszeit bringt es außerhalb von Angulars Kontrolle und Chrome erledigt den Rest. –

0

Als Bug has been fixed in Chrome (aber das Problem weiterhin besteht in Angular), eine Abhilfe, die zweimal aus dem Stack-Trace wird nicht gedruckt würde dies:

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