Überwachen von JavaScript-Fehlern mit window.onerror

Das Material, dessen Übersetzung wir heute veröffentlichen, ist der Behandlung von JS-Fehlern mit window.onerror . Dies ist ein spezielles Browserereignis, das ausgelöst wird, wenn nicht erfasste Fehler auftreten. Hier onerror , wie Sie Fehler mit dem onerror Ereignishandler abfangen und Informationen dazu an den Server des Website-Entwicklers senden. Dieser Handler kann als Grundlage für Ihr eigenes System zum Sammeln und Analysieren von Fehlerinformationen verwendet werden. Darüber hinaus ist es einer der wichtigsten Mechanismen, die in fehlerorientierten Bibliotheken wie Raven-js verwendet werden .

Bild

Auf das window.onerror-Ereignis warten


Sie können auf das Ereignis onerror window.onerror indem Sie window.onerror Funktion window.onerror , die die Rolle eines Fehlerbehandlers spielt:

 window.onerror = function(msg, url, lineNo, columnNo, error) { // ...   ... return false; } 

Diese Funktion wird aufgerufen, wenn ein Fehler auftritt. Die folgenden Argumente werden an sie übergeben:

  • msg - Fehlermeldung. Zum Beispiel ist Uncaught ReferenceError: foo is not defined .
  • url - die Adresse des Skripts oder Dokuments, in dem der Fehler aufgetreten ist. Zum Beispiel /dist/app.js .
  • lineNo - Zeilennummer, in der der Fehler aufgetreten ist (falls unterstützt).
  • columnNo - die Spaltennummer der Zeile (falls unterstützt).
  • error - das error (falls unterstützt).

Die ersten vier Argumente teilen dem Entwickler mit, in welchem ​​Skript, in welcher Zeile und in welcher Spalte dieser Zeile ein Fehler aufgetreten ist. Das letzte Argument, ein Objekt vom Typ Error , ist vielleicht das wichtigste aller Argumente. Reden wir darüber.

Fehlerobjekt und Error.prototype.stack-Eigenschaft


Auf den ersten Blick ist das Error nichts Besonderes. Es enthält drei ganz normale Eigenschaften - message , fileName und lineNumber . Diese Daten können angesichts der an den Ereignishandler window.onerror übergebenen Informationen als redundant betrachtet werden.

Der tatsächliche Wert ist in diesem Fall die nicht standardmäßige Eigenschaft Error.prototype.stack . Diese Eigenschaft ermöglicht den Zugriff auf den Aufrufstapel (Fehlerstapel) und ermöglicht es Ihnen, herauszufinden, was zum Zeitpunkt des Fehlers im Programm passiert ist. Welcher Funktionsaufruf ging seinem Auftreten voraus. Die Ablaufverfolgung von Aufrufstapeln kann ein kritischer Bestandteil des Debugging-Prozesses sein. Und obwohl die stack Eigenschaft nicht Standard ist, ist sie in allen modernen Browsern verfügbar.

So sieht die stack Eigenschaft des Fehlerobjekts in Chrome 46 aus.

 "Error: foobar\n    at new bar (<anonymous>:241:11)\n    at foo (<anonymous>:245:5)\n at <anonymous>:250:5\n    at <anonymous>:251:3\n at <anonymous>:267:4\n at callFunction (<anonymous>:229:33)\n    at <anonymous>:239:23\n at <anonymous>:240:3\n at Object.InjectedScript.\_evaluateOn (<anonymous>:875:140)\n    at Object.InjectedScript.\_evaluateAndWrap (<anonymous>:808:34)" 

Vor uns liegt eine unformatierte Zeichenfolge. Wenn der Inhalt dieser Eigenschaft in dieser Form dargestellt wird, ist es unpraktisch, damit zu arbeiten. So sieht das gleiche nach der Formatierung aus.

 Error: foobar at new bar (<anonymous>:241:11) at foo (<anonymous>:245:5) at callFunction (<anonymous>:229:33) at Object.InjectedScript._evaluateOn (<anonymous>:875:140) at Object.InjectedScript._evaluateAndWrap (<anonymous>:808:34) 

Nach der Formatierung sieht der Fehlerstapel jetzt viel klarer aus. Es wird sofort klar, warum die stack beim Debuggen von Fehlern sehr wichtig ist.

Hier läuft jedoch nicht alles reibungslos. Die stack Eigenschaft ist nicht standardisiert, sondern wird in verschiedenen Browsern unterschiedlich implementiert. Hier sehen Sie beispielsweise, wie der Fehlerstapel in Internet Explorer 11 aussieht.

 Error: foobar at bar (Unknown script code:2:5) at foo (Unknown script code:6:5) at Anonymous function (Unknown script code:11:5) at Anonymous function (Unknown script code:10:2) at Anonymous function (Unknown script code:1:73) 

Sie können im Vergleich zum vorherigen Beispiel sehen, dass hier nicht nur ein anderes Format zur Darstellung von Stapelrahmen verwendet wird, sondern auch, dass für jeden Rahmen weniger Daten vorhanden sind. Chrome identifiziert beispielsweise Instanzen der Verwendung des new Schlüsselworts und bietet detailliertere Informationen zu anderen Ereignissen (insbesondere zu Funktionsaufrufen. _evaluateOn und. _evaluateAndWrap ). Gleichzeitig haben wir hier nur verglichen, was IE und Chrome herausgeben. Andere Browser verwenden ihre eigenen Ansätze, um Daten über den Stapel anzuzeigen und die in diesen Daten enthaltenen Informationen auszuwählen.

Um all dies zu einem einheitlichen Erscheinungsbild zu bringen, können Sie Tools von Drittanbietern verwenden. Beispielsweise verwendet raven-js hierfür TraceKit. Stacktrace.js und einige andere Projekte dienen demselben Zweck.

Funktionen der window.onerror-Unterstützung durch verschiedene Browser


Das Ereignis windows.onerror existiert seit einiger Zeit in Browsern. Insbesondere ist es in IE6 und Firefox 2 zu finden. Das Problem hierbei ist, dass alle Browser windows.onerror auf unterschiedliche Weise implementieren. Dies betrifft beispielsweise die Anzahl und Struktur der Argumente, die an die Handler dieses Ereignisses übergeben werden.

In der folgenden Tabelle sind onerror Argumente aufgeführt, die in den wichtigsten Browsern an den onerror Handler übergeben wurden.
Browser
Nachricht
URL
lineNo
colNo
errorObj
Firefox
Es gibt
Es gibt
Es gibt
Es gibt
Es gibt
Chrome
Es gibt
Es gibt
Es gibt
Es gibt
Es gibt
Rand
Es gibt
Es gibt
Es gibt
Es gibt
Es gibt
IE 11
Es gibt
Es gibt
Es gibt
Es gibt
Es gibt
IE10
Es gibt
Es gibt
Es gibt
Es gibt
Nein
IE 9.8
Es gibt
Es gibt
Es gibt
Nein
Nein
Safari 10 und höher
Es gibt
Es gibt
Es gibt
Es gibt
Es gibt
Safari 9
Es gibt
Es gibt
Es gibt
Es gibt
Nein
Android Browser 4.4
Es gibt
Es gibt
Es gibt
Es gibt
Nein

Es ist wahrscheinlich nicht überraschend, dass Internet Explorer 8, 9 und 10 onerror nur eingeschränkt unterstützen. Es mag jedoch ungewöhnlich erscheinen, dass die Unterstützung für das Fehlerobjekt im Safari-Browser nur in der 10. Version erschien, die 2016 veröffentlicht wurde. Darüber hinaus gibt es ältere mobile Geräte, die den Standard-Android-Browser verwenden, der das Fehlerobjekt ebenfalls nicht unterstützt. In modernen Android-Versionen wurde dieser Browser durch Chrome Mobile ersetzt.

Wenn uns kein Fehlerobjekt zur Verfügung steht, liegen keine Daten zur Stapelverfolgung vor. Dies bedeutet, dass Browser, die das Objekt des Fehlers nicht unterstützen, im Standardskript keine onerror für die Verwendung des onerror Handlers bereitstellen. Und das ist, wie gesagt, sehr wichtig.

Polyfill-Entwicklung für window.onerror mit dem try / catch-Konstrukt


Um Informationen über den Stapel in Browsern abzurufen, die die Übergabe eines onerror an den onerror Handler nicht unterstützen, können Sie den folgenden Trick verwenden. Sie können den Code in ein try/catch Konstrukt einschließen und die Fehler selbst abfangen. Das resultierende Fehlerobjekt enthält in allen modernen Browsern die stack Eigenschaft.
Schauen Sie sich den invoke() , der die angegebene Methode des Objekts aufruft und ihm ein Array von Argumenten übergibt.

 function invoke(obj, method, args) { return obj[method].apply(this,args); } 

Hier erfahren Sie, wie Sie es verwenden.

 invoke(Math, 'max', [1,2]); //  2 

Hier ist das gleiche invoke() , aber jetzt ist der Methodenaufruf in try/catch , wodurch Sie mögliche Fehler abfangen können.

 function invoke(obj, method, args) { try {   return obj[method].apply(this,args); } catch(e) {   captureError(e);//      throw e;//      } } invoke(Math,'highest',[1,2]); //  ,     Math.highest 

Natürlich ist es sehr teuer, solche Strukturen manuell an allen Stellen hinzuzufügen, an denen sie möglicherweise benötigt werden. Diese Aufgabe kann durch die Erstellung einer universellen Hilfsfunktion vereinfacht werden.

 function wrapErrors(fn) { //      if(!fn.__wrapped__) {   fn.__wrapped__ = function() {     try{       return fn.apply(this,arguments);     }catch(e){       captureError(e);//          throw e;//          }   }; } return fn.__wrapped__; } var invoke = wrapErrors(function(obj, method, args) { returnobj[method].apply(this,args); }); invoke(Math,'highest',[1,2]);//,   Math.highest 

Da JavaScript ein Single-Threaded-Code-Ausführungsmodell verwendet, sollte dieser Wrapper nur mit den Funktionsaufrufen verwendet werden, die am Anfang neuer Stapel stehen. Es ist nicht erforderlich, alle Funktionsaufrufe darin zu verpacken.

Infolgedessen stellt sich heraus, dass diese Funktion an folgenden Stellen verwendet werden muss:

  • Wo die Anwendung startet (z. B. bei Verwendung von jQuery in der Funktion $(document).ready )
  • In Ereignishandlern (z. B. in addEventListener oder in Konstruktionen der Form $.fn.click )
  • In Rückrufen, die von Timer-Ereignissen aufgerufen werden (z. B. setTimeout oder requestAnimationFrame ).

Hier ist ein Beispiel mit der Funktion wrapErrors .

 $(wrapErrors(function () {//    doSynchronousStuff1();//     setTimeout(wrapErrors(function () {   doSynchronousStuff2();//      })); $('.foo').click(wrapErrors(function () {   doSynchronousStuff3();//     })); })); 

Solche Konstruktionen können dem Code selbst hinzugefügt werden, dies ist jedoch eine zu zeitaufwändige Aufgabe. Als praktische Alternative in solchen Situationen können Sie Bibliotheken für die Arbeit mit Fehlern in Betracht ziehen, die beispielsweise über Mechanismen verfügen, addEventListener und setTimeout Tools zum addEventListener Fehlern addEventListener .

Fehlerübertragung auf den Server


Jetzt haben wir also die Möglichkeit, Fehlerinformationen entweder mit windows.onerror oder mithilfe von Hilfsfunktionen basierend auf try/catch abzufangen. Diese Fehler treten auf der Client-Seite auf, und nach ihrem Abfangen möchten wir sie beheben und Maßnahmen ergreifen, um sie zu beseitigen. Dazu müssen sie auf unseren Server übertragen werden. Dazu müssen Sie einen Webdienst vorbereiten, der Informationen über Fehler über HTTP empfängt und diese dann zur weiteren Verarbeitung speichert, z. B. in eine Protokolldatei oder Datenbank schreiben.

Befindet sich dieser Webdienst in derselben Domäne wie die Webanwendung, reicht XMLHttpRequest aus. Das folgende Beispiel zeigt, wie Sie mit einer Funktion AJAX-Abfragen von jQuery ausführen, um Daten auf einen Server zu übertragen.

 function captureError(ex){ var errorData = {   name:ex.name,// : ReferenceError   message:ex.line,// : x is undefined   url:document.location.href,   stack:ex.stack//   ; ,     ! }; $.post('/logger/js/',{   data:errorData }); } 

Beachten Sie, dass Sie sich um die Unterstützung solcher Anforderungen kümmern müssen, wenn Sie domänenübergreifende Anforderungen senden müssen, um Informationen zu Fehlern an den Server zu senden.

Zusammenfassung


Sie haben die Grundlagen zum Erstellen eines Dienstes zum Abfangen von Fehlern und zum Senden von Informationen über diese an den Server gelernt. Insbesondere haben wir hier folgende Themen untersucht:

  • Funktionen des onerror Ereignisses und dessen Unterstützung in verschiedenen Browsern.
  • Verwenden des try/catch Mechanismus zum Abrufen von Informationen zum Aufrufstapel in Fällen, in denen onerror die Arbeit mit dem onerror nicht unterstützt.
  • Übertragen Sie Fehlerdaten auf den Server des Entwicklers.

Nachdem Sie die Funktionsweise der oben genannten Mechanismen kennengelernt haben, haben Sie Grundkenntnisse erworben, mit denen Sie Ihr eigenes System für die Arbeit mit Fehlern erstellen und zusätzliche Details während der Arbeit klären können. Möglicherweise ist dieses Szenario besonders relevant für Fälle, in denen es sich um eine bestimmte Anwendung handelt, in der beispielsweise aus Sicherheitsgründen die Verwendung von Bibliotheken von Drittanbietern nicht geplant ist. Wenn Ihre Anwendung die Verwendung von Code von Drittanbietern zulässt, finden Sie sehr gut das richtige Tool zur Überwachung von JS-Fehlern. Zu diesen Tools gehören Sentry , Rollbar , TrackJS und andere ähnliche Projekte.

Liebe Leser! Welche Tools zur Überwachung von JS-Fehlern verwenden Sie?

Source: https://habr.com/ru/post/de413173/


All Articles