Le matériel, dont nous publions la traduction aujourd'hui, est dédié au traitement des erreurs JS à l'aide de
window.onerror
. Il s'agit d'un événement spécial du navigateur qui se déclenche lorsque des erreurs non détectées se produisent. Ici, nous expliquerons comment détecter les erreurs à l'aide du
onerror
événements
onerror
et comment envoyer des informations à leur sujet au serveur du développeur du site Web. Ce gestionnaire peut être utilisé comme base de votre propre système de collecte et d'analyse des informations d'erreur. De plus, il s'agit de l'un des mécanismes les plus importants utilisés dans les bibliothèques orientées erreur, telles que
raven-js .

Écoute de l'événement window.onerror
Vous pouvez écouter l'événement
onerror
en affectant
window.onerror
fonction qui agit comme un gestionnaire d'erreur:
window.onerror = function(msg, url, lineNo, columnNo, error) { // ... ... return false; }
Cette fonction est appelée lorsqu'une erreur se produit; les arguments suivants lui sont transmis:
msg
- message d'erreur. Par exemple, Uncaught ReferenceError: foo is not defined
.url
- l'adresse du script ou du document dans lequel l'erreur s'est produite. Par exemple, /dist/app.js
.lineNo
- numéro de ligne où l'erreur s'est produite (si prise en charge).columnNo
- le numéro de colonne de la ligne (si pris en charge).error
- l'objet d' error
(s'il est pris en charge).
Les quatre premiers arguments indiquent au développeur quel script, dans quelle ligne et dans quelle colonne de cette ligne une erreur s'est produite. Le dernier argument, un objet de type
Error
, est peut-être le plus important de tous les arguments. Parlons-en.
Objet d'erreur et propriété Error.prototype.stack
À première vue, l'objet
Error
n'a rien de spécial. Il contient trois propriétés assez standard -
message
,
fileName
et
lineNumber
. Ces données, compte tenu des informations transmises au gestionnaire d'événements
window.onerror
, peuvent être considérées comme redondantes.
La valeur réelle dans ce cas est la propriété non standard
Error.prototype.stack
. Cette propriété donne accès à la pile des appels (pile des erreurs), vous permet de savoir ce qui se passait dans le programme au moment de l'erreur, quel appel de fonction a précédé son apparition. Le suivi de la pile d'appels peut être un élément essentiel du processus de débogage. Et, malgré le fait que la propriété de
stack
n'est pas standard, elle est disponible dans tous les navigateurs modernes.
Voici à quoi ressemble la propriété de
stack
de l'objet d'erreur dans Chrome 46.
"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)"
Devant nous se trouve une chaîne non formatée. Lorsque le contenu de cette propriété est présenté sous cette forme, il n'est pas pratique de travailler avec elle. Voici à quoi cela ressemblera après le formatage.
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)
Maintenant, après le formatage, la pile des erreurs semble beaucoup plus claire, il devient immédiatement clair pourquoi la propriété de la
stack
est très importante lors du débogage des erreurs.
Cependant, ici, tout ne se passe pas bien. La propriété de
stack
n'est pas normalisée; elle est implémentée différemment dans différents navigateurs. Voici, par exemple, à quoi ressemble la pile d'erreurs dans Internet Explorer 11.
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)
Vous pouvez voir, par rapport à l'exemple précédent, qu'ici non seulement un format différent pour représenter les trames de pile est utilisé, mais aussi qu'il y a moins de données pour chaque trame. Par exemple, Chrome identifie les instances d'utilisation du
new
mot clé et fournit des informations plus détaillées sur d'autres événements (en particulier, sur les appels de fonction.
_evaluateOn
et.
_evaluateAndWrap
). Dans le même temps, nous n'avons comparé ici que ce que IE et Chrome donnent. D'autres navigateurs utilisent leurs propres approches pour afficher des données sur la pile et pour sélectionner les informations incluses dans ces données.
Afin de rendre tout cela uniforme, vous pouvez utiliser des outils tiers. Par exemple, raven-js utilise TraceKit pour cela. Stacktrace.js et certains autres projets ont le même objectif.
Caractéristiques de la prise en charge de window.onerror par divers navigateurs
L'événement
windows.onerror
existe dans les navigateurs depuis un certain temps. En particulier, il peut être trouvé dans IE6 et Firefox 2. Le problème ici est que tous les navigateurs implémentent
windows.onerror
de différentes manières. Par exemple, cela concerne le nombre et la structure des arguments passés aux gestionnaires de cet événement.
Voici un tableau
onerror
arguments passés au gestionnaire
onerror
dans les principaux navigateurs.
Navigateur
| message
| url
| ligneNon
| colNo
| errorObj
|
Firefox
| Il y a
| Il y a
| Il y a
| Il y a
| Il y a
|
Chrome
| Il y a
| Il y a
| Il y a
| Il y a
| Il y a
|
Bord
| Il y a
| Il y a
| Il y a
| Il y a
| Il y a
|
IE 11
| Il y a
| Il y a
| Il y a
| Il y a
| Il y a
|
IE10
| Il y a
| Il y a
| Il y a
| Il y a
| Non
|
IE 9.8
| Il y a
| Il y a
| Il y a
| Non
| Non
|
Safari 10 et supérieur
| Il y a
| Il y a
| Il y a
| Il y a
| Il y a
|
Safari 9
| Il y a
| Il y a
| Il y a
| Il y a
| Non
|
Navigateur Android 4.4
| Il y a
| Il y a
| Il y a
| Il y a
| Non
|
Sans surprise, Internet Explorer 8, 9 et 10 ont un support limité pour
onerror
. Cependant, il peut sembler inhabituel que dans le navigateur Safari, la prise en charge de l'objet d'erreur n'apparaisse que dans la 10e version, publiée en 2016. En outre, certains appareils mobiles hérités utilisent le navigateur Android standard, qui ne prend pas en charge l'objet d'erreur. Dans les versions modernes d'Android, ce navigateur a été remplacé par Chrome Mobile.
S'il n'y a pas d'objet d'erreur à notre disposition, alors il n'y a pas de données sur la trace de la pile. Cela signifie que les navigateurs qui ne prennent pas en charge l'objet de l'erreur ne fournissent pas d'informations de pile dans le script standard pour utiliser le gestionnaire
onerror
. Et cela, comme nous l'avons dit, est très important.
Développement Polyfill pour window.onerror utilisant la construction try / catch
Afin d'obtenir des informations sur la pile dans les navigateurs qui ne prennent pas en charge la transmission d'un objet d'
onerror
au gestionnaire
onerror
, vous pouvez utiliser l'astuce suivante. Vous pouvez encapsuler le code dans une construction
try/catch
et intercepter les erreurs vous-même. L'objet d'erreur résultant contiendra, dans tous les navigateurs modernes, ce dont nous avons besoin, c'est la propriété
stack
.
Jetez un œil au code de la méthode d'assistance
invoke()
, qui appelle la méthode donnée de l'objet, en lui passant un tableau d'arguments.
function invoke(obj, method, args) { return obj[method].apply(this,args); }
Voici comment l'utiliser.
invoke(Math, 'max', [1,2])
Voici la même
invoke()
, mais maintenant l'appel de méthode est encapsulé dans
try/catch
, ce qui vous permet d'attraper les erreurs possibles.
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
Bien sûr, il est très coûteux d'ajouter manuellement de telles structures à tous les endroits où elles peuvent être nécessaires. Cette tâche peut être simplifiée en créant une fonction d'assistance universelle.
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
Étant donné que JavaScript utilise un modèle d'exécution de code à thread unique, ce wrapper ne doit être utilisé qu'avec les appels de fonction qui se trouvent au début de nouvelles piles. Il n'est pas nécessaire d'envelopper tous les appels de fonction.
En conséquence, il s'avère que cette fonction doit être utilisée aux endroits suivants:
- Où l'application démarre (par exemple, lors de l'utilisation de jQuery, dans la fonction
$(document).ready
) - Dans les gestionnaires d'événements (par exemple, dans
addEventListener
ou dans les constructions de la forme $.fn.click
) - Dans les rappels appelés par des événements de temporisation (par exemple, il s'agit de
setTimeout
ou requestAnimationFrame
)
Voici un exemple d'utilisation de la fonction
wrapErrors
.
$(wrapErrors(function () {// doSynchronousStuff1();// setTimeout(wrapErrors(function () { doSynchronousStuff2();// })); $('.foo').click(wrapErrors(function () { doSynchronousStuff3();// })); }));
De telles constructions peuvent être ajoutées au code vous-même, mais cette tâche prend trop de temps. Comme alternative pratique dans de telles situations, vous pouvez envisager des bibliothèques pour travailler avec les erreurs, qui, par exemple, ont des mécanismes qui
addEventListener
et
setTimeout
outils pour détecter les erreurs.
Erreur de transfert vers le serveur
Donc, nous avons maintenant à notre disposition des moyens pour intercepter les informations sur les erreurs en utilisant
windows.onerror
ou en utilisant des fonctions auxiliaires basées sur
try/catch
. Ces erreurs se produisent du côté client et, après leur interception, nous aimerions les traiter et prendre des mesures pour les éliminer. Pour ce faire, ils doivent être transférés sur notre serveur. Pour ce faire, vous devez préparer un service Web qui recevrait des informations sur les erreurs via HTTP, puis les enregistrer d'une manière ou d'une autre pour un traitement ultérieur, par exemple - écrire dans un fichier journal ou une base de données.
Si ce service Web se trouve sur le même domaine que l'application Web,
XMLHttpRequest
suffira. L'exemple suivant montre comment utiliser une fonction pour exécuter des requêtes AJAX à partir de jQuery pour transférer des données vers un serveur.
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 }); }
Gardez à l'esprit que si vous devez envoyer des demandes interdomaines pour envoyer des informations sur les erreurs au serveur, vous devrez prendre en charge ces demandes.
Résumé
Vous avez appris les bases de la création d'un service pour détecter les erreurs et envoyer des informations à leur sujet au serveur. En particulier, nous avons examiné ici les questions suivantes:
- Caractéristiques de l'événement
onerror
et son support dans divers navigateurs. - Utilisation du mécanisme
try/catch
pour obtenir des informations sur la pile d'appels dans les cas où onerror
ne prend pas en charge l'utilisation de l'objet d'erreur. - Transférez les données d'erreur sur le serveur du développeur.
Après avoir appris comment fonctionnent les mécanismes ci-dessus, vous avez acquis des connaissances de base qui vous permettront de commencer à créer votre propre système pour traiter les erreurs, en clarifiant des détails supplémentaires pendant le travail. Ce scénario est peut-être particulièrement pertinent dans les cas où il s'agit d'une certaine application dans laquelle, par exemple, pour des raisons de sécurité, il n'est pas prévu d'utiliser des bibliothèques tierces. Si votre application autorise l'utilisation de code tiers, vous pouvez très bien trouver le bon outil pour surveiller les erreurs JS. Parmi ces outils figurent
Sentry ,
Rollbar ,
TrackJS et d'autres projets similaires.
Chers lecteurs! Quels outils de surveillance des erreurs JS utilisez-vous?
