El material, cuya traducción publicamos hoy, está dedicado a manejar los errores de JS usando
window.onerror
. Este es un evento especial del navegador que se activa cuando se producen errores no detectados. Aquí hablaremos sobre cómo detectar errores utilizando el
onerror
eventos
onerror
y cómo enviar información sobre ellos al servidor del desarrollador del sitio web. Este controlador se puede utilizar como base de su propio sistema para recopilar y analizar información de errores. Además, es uno de los mecanismos más importantes utilizados en bibliotecas orientadas a errores, como
raven-js .

Escuchando el evento window.onerror
Puede escuchar el evento
onerror
asignando a
window.onerror
función que desempeña el papel de un controlador de errores:
window.onerror = function(msg, url, lineNo, columnNo, error) { // ... ... return false; }
Se llama a esta función cuando se produce un error; se le pasan los siguientes argumentos:
msg
: mensaje de error. Por ejemplo, Uncaught ReferenceError: foo is not defined
.url
: la dirección del script o documento en el que se produjo el error. Por ejemplo, /dist/app.js
.lineNo
: número de línea donde se produjo el error (si se admite).columnNo
: el número de columna de la fila (si se admite).error
: el objeto de error
(si es compatible).
Los primeros cuatro argumentos le dicen al desarrollador qué script, en qué fila y en qué columna de esta fila se produjo un error. El argumento final, un objeto de tipo
Error
, es quizás el más importante de todos los argumentos. Hablemos de eso.
Objeto de error y propiedad Error.prototype.stack
A primera vista, el objeto
Error
no es nada especial. Contiene tres propiedades bastante estándar:
message
, nombre de
lineNumber
y número de
lineNumber
. Estos datos, dada la información que se pasa al controlador de eventos
window.onerror
, pueden considerarse redundantes.
El valor real en este caso es la propiedad no estándar
Error.prototype.stack
. Esta propiedad da acceso a la pila de llamadas (pila de errores), le permite averiguar qué estaba sucediendo en el programa en el momento en que ocurrió el error, qué llamada de función precedió a su aparición. El seguimiento de la pila de llamadas puede ser una parte crítica del proceso de depuración. Y, a pesar de que la propiedad de
stack
no es estándar, está disponible en todos los navegadores modernos.
Así es como se ve la propiedad de
stack
del objeto de error en 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)"
Ante nosotros hay una cadena sin formato. Cuando el contenido de esta propiedad se presenta en este formulario, no es conveniente trabajar con él. Así es como se verá después del formateo.
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)
Ahora, después de formatear, la pila de errores se ve mucho más clara, inmediatamente queda claro por qué la propiedad de la
stack
es muy importante al depurar errores.
Sin embargo, aquí no todo va bien. La propiedad de la
stack
no está estandarizada; se implementa de manera diferente en diferentes navegadores. Aquí, por ejemplo, es cómo se ve la pila de errores en 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)
Puede ver, en comparación con el ejemplo anterior, que aquí no solo se usa un formato diferente para representar cuadros de pila, sino que también hay menos datos para cada cuadro. Por ejemplo, Chrome identifica instancias de uso de la
new
palabra clave y proporciona información más detallada sobre otros eventos (en particular, sobre llamadas a funciones.
_evaluateOn
y.
_evaluateAndWrap
). Al mismo tiempo, aquí solo comparamos lo que dan IE y Chrome. Otros navegadores utilizan sus propios enfoques para mostrar datos sobre la pila y para seleccionar la información incluida en estos datos.
Para que todo esto tenga un aspecto uniforme, puede utilizar herramientas de terceros. Por ejemplo, raven-js usa TraceKit para esto. Stacktrace.js y algunos otros proyectos tienen el mismo propósito.
Características del soporte de window.onerror por varios navegadores
El evento
windows.onerror
ha existido en los navegadores durante bastante tiempo. En particular, se puede encontrar en IE6 y Firefox 2. El problema aquí es que todos los navegadores implementan
windows.onerror
de diferentes maneras. Por ejemplo, esto se refiere al número y la estructura de los argumentos pasados a los controladores de este evento.
Aquí hay una tabla que
onerror
argumentos pasados al controlador
onerror
en los principales navegadores.
Navegador
| mensaje
| url
| líneaNo
| colNo
| errorObj
|
Firefox
| Hay
| Hay
| Hay
| Hay
| Hay
|
Cromo
| Hay
| Hay
| Hay
| Hay
| Hay
|
Borde
| Hay
| Hay
| Hay
| Hay
| Hay
|
IE 11
| Hay
| Hay
| Hay
| Hay
| Hay
|
IE10
| Hay
| Hay
| Hay
| Hay
| No
|
IE 9.8
| Hay
| Hay
| Hay
| No
| No
|
Safari 10 y superior
| Hay
| Hay
| Hay
| Hay
| Hay
|
Safari 9
| Hay
| Hay
| Hay
| Hay
| No
|
Navegador de Android 4.4
| Hay
| Hay
| Hay
| Hay
| No
|
Probablemente no sea sorprendente, Internet Explorer 8, 9 y 10 tienen soporte limitado para
onerror
. Sin embargo, puede parecer inusual que en el navegador Safari el soporte para el objeto de error apareció solo en la décima versión, lanzada en 2016. Además, hay dispositivos móviles heredados que usan el navegador estándar de Android, que tampoco admite el objeto de error. En las versiones modernas de Android, este navegador ha sido reemplazado por Chrome Mobile.
Si no hay ningún objeto de error a nuestra disposición, entonces no hay datos sobre el seguimiento de la pila. Esto significa que los navegadores que no admiten el objeto del error no proporcionan información de la pila en el script estándar para usar el controlador
onerror
. Y esto, como hemos dicho, es muy importante.
Desarrollo de Polyfill para window.onerror usando try / catch construct
Para obtener información sobre la pila en los navegadores que no admiten pasar un objeto de
onerror
al controlador
onerror
, puede usar el siguiente truco. Puede ajustar el código en una construcción
try/catch
y detectar los errores usted mismo. El objeto de error resultante contendrá, en todos los navegadores modernos, lo que necesitamos es la propiedad de
stack
.
Eche un vistazo al código del método auxiliar
invoke()
, que llama al método dado del objeto, pasándole una matriz de argumentos.
function invoke(obj, method, args) { return obj[method].apply(this,args); }
Aquí se explica cómo usarlo.
invoke(Math, 'max', [1,2])
Aquí está el mismo
invoke()
, pero ahora la llamada al método está envuelta en
try/catch
, que le permite detectar posibles errores.
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
Por supuesto, es muy costoso agregar manualmente tales estructuras a todos los lugares donde puedan ser necesarias. Esta tarea puede simplificarse creando una función auxiliar universal.
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
Dado que JavaScript usa un modelo de ejecución de código de subproceso único, este contenedor solo debe usarse con esas llamadas de función que están al comienzo de nuevas pilas. No es necesario envolver todas las llamadas a funciones en él.
Como resultado, resulta que esta función debe usarse en los siguientes lugares:
- Dónde se inicia la aplicación (por ejemplo, cuando se usa jQuery, en la función
$(document).ready
) - En controladores de eventos (por ejemplo, en
addEventListener
o en construcciones de la forma $.fn.click
) - En devoluciones de llamada llamadas por eventos de temporizador (por ejemplo, es
setTimeout
o requestAnimationFrame
)
Aquí hay un ejemplo usando la función
wrapErrors
.
$(wrapErrors(function () {// doSynchronousStuff1();// setTimeout(wrapErrors(function () { doSynchronousStuff2();// })); $('.foo').click(wrapErrors(function () { doSynchronousStuff3();// })); }));
Tales construcciones se pueden agregar al código usted mismo, pero esta tarea lleva demasiado tiempo. Como una alternativa conveniente en tales situaciones, puede considerar las bibliotecas para trabajar con errores, que, por ejemplo, tienen mecanismos que
addEventListener
y
setTimeout
herramientas para
addEventListener
errores.
Error de transferencia al servidor
Entonces, ahora tenemos a nuestra disposición medios para interceptar información de error usando
windows.onerror
o usando funciones auxiliares basadas en
try/catch
. Estos errores ocurren en el lado del cliente y, después de su intercepción, nos gustaría tratar con ellos y tomar medidas para eliminarlos. Para hacer esto, necesitan ser transferidos a nuestro servidor. Para hacer esto, debe preparar un servicio web que acepte información de error a través de HTTP, y luego de alguna manera la guarde para su posterior procesamiento, por ejemplo, escribiría en un archivo de registro o base de datos.
Si este servicio web se encuentra en el mismo dominio que la aplicación web,
XMLHttpRequest
será suficiente. El siguiente ejemplo muestra cómo usar una función para ejecutar consultas AJAX desde jQuery para transferir datos a un servidor.
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 }); }
Tenga en cuenta que si necesita enviar solicitudes de dominio cruzado para enviar información sobre errores al servidor, deberá encargarse de respaldar dichas solicitudes.
Resumen
Ha aprendido los conceptos básicos de la creación de un servicio para detectar errores y enviar información sobre ellos al servidor. En particular, aquí examinamos los siguientes problemas:
- Características del evento
onerror
y su soporte en varios navegadores. - Usar el mecanismo
try/catch
para obtener información sobre la pila de llamadas en casos donde onerror
no admite trabajar con el objeto de error. - Transfiera datos de error al servidor del desarrollador.
Después de aprender cómo funcionan los mecanismos anteriores, ha adquirido conocimientos básicos que le permitirán comenzar a crear su propio sistema para trabajar con errores, aclarando detalles adicionales durante el trabajo. Quizás este escenario sea especialmente relevante para esos casos cuando se trata de una determinada aplicación en la que, por razones de seguridad, por ejemplo, no está previsto utilizar bibliotecas de terceros. Si su aplicación permite el uso de código de terceros, puede encontrar la herramienta adecuada para monitorear los errores de JS. Entre tales herramientas se encuentran
Sentry ,
Rollbar ,
TrackJS y otros proyectos similares.
Estimados lectores! ¿Qué herramientas utiliza para monitorear los errores de JS?
