Red de conocimiento informático - Material del sitio web - Cómo detectar y analizar errores de JavaScript

Cómo detectar y analizar errores de JavaScript

Todos los ingenieros de front-end saben que JavaScript tiene capacidades básicas de manejo de excepciones. Podemos lanzar un nuevo Error() y el navegador también lanzará una excepción cuando nuestra llamada a la API salga mal. Pero la mayoría de los ingenieros de front-end no piensan en recopilar estas excepciones. Siempre que el error de JavaScript no vuelva a aparecer en las actualizaciones, el usuario puede actualizar para solucionar el problema, el navegador no fallará y no se producirá el error. Esta suposición era cierta antes de que las aplicaciones de una sola página se hicieran populares. Hoy en día, las aplicaciones de una sola página se ejecutan durante un tiempo y tienen estados muy complejos. Es posible que el usuario haya ingresado varias veces antes de venir aquí, entonces, ¿puede simplemente actualizar? ¿No es necesario rehacer todas las operaciones anteriores? Por lo tanto, aún necesitamos detectar y analizar estas excepciones y luego modificar el código para evitar afectar la experiencia del usuario.

Métodos para detectar excepciones

Escribimos throw new Error() nosotros mismos. Si queremos detectarlo, por supuesto que podemos, porque sabemos claramente dónde está escrito throw. Pero las excepciones que ocurren al llamar a las API del navegador no siempre son fáciles de detectar. Algunas interfaces de programación de aplicaciones han escrito excepciones en el estándar, mientras que algunas interfaces de programación de aplicaciones solo arrojan excepciones en navegadores individuales debido a diferencias o errores de implementación. Para el primero, aún podemos detectarlas usando try-catch, mientras que para el segundo, tenemos que escuchar las excepciones globales y detectarlas.

try-catch

Si se sabe que la API del navegador genera una excepción, entonces debemos realizar la llamada en un try-catch para evitar que todo el programa entre en un estado ilegal. debido a errores. Por ejemplo, window.localStorage es una API que genera una excepción cuando los datos escritos exceden su límite de capacidad, como es el caso en el modo de navegación privada de Safari.

try { localStorage.setItem('date', Date.now());} catch (error) { reportError(error);}

Otro uso común de Try-catch El escenario de la aplicación es la devolución de llamada. Dado que el código de la función de devolución de llamada está fuera de nuestro control, no sabemos la calidad del código o si llamará a otras API que generarán excepciones. Para evitar un error al llamar a la devolución de llamada y provocar que el resto del código llamado por la devolución de llamada no se ejecute, es necesario volver a llamar dentro del try-catch.

listeners.forEach(function(oyente) { intentar { escucha(); } catch (error) { reportError(error); }}); >

Para lugares que no están cubiertos por try-catch, si la devolución de llamada no está cubierta por try-catch, es mejor volver a colocar la llamada en try-catch. catch no está cubierto. Si ocurre una excepción, solo se puede detectar a través de window.onerror.

window.onerror = function(errorMessage, scriptURI, lineNumber) { reportError({ message: errorMessage, script: scriptURI, line. lineNumber });}

Tenga cuidado de no utilizar Es demasiado inteligente. Utilice window.addEventListener o window.attachEvent para escuchar window.onerror.

Muchos navegadores solo implementan window.onerror, o solo window.onerror es estándar. Teniendo en cuenta que el borrador del estándar también define window.onerror, solo usaremos window.onerror.

Propiedades faltantes

Suponiendo que tenemos una función reportError que recopila excepciones detectadas y las envía en lotes al almacenamiento del lado del servidor para consultas y análisis, entonces queremos recopilar ¿Qué información? La información útil incluye: tipo de error (nombre), mensaje de error (mensaje), dirección del archivo de script (script), número de línea (línea), número de columna (columna), seguimiento de pila (pila). Si try-catch detecta la excepción, toda esta información está en el objeto Error (compatible con todos los navegadores principales), por lo que reportError también recopilará esta información. Pero si window.onerror detecta la excepción, todos sabemos que la función de evento solo toma 3 parámetros, por lo que se perderá cualquier información más allá de estos 3 parámetros.

Mensaje serializado

Si el objeto Error lo creamos nosotros, entonces error.message será controlado por nosotros. Básicamente, lo que ingresamos en error.message es el primer parámetro (mensaje) de window.onerror. (Los navegadores en realidad modifican esto ligeramente, como anteponer "Error no detectado:"). Entonces podemos serializar (por ejemplo, JSON.Stringify) la propiedad que nos interesa, almacenarla en error.message y luego deserializarla cuando window.onerror la lea. Por supuesto, esto se limita a los objetos de Error que creamos nosotros mismos.

El quinto parámetro

Los proveedores de navegadores se han dado cuenta de las limitaciones del uso de window.onerror, por lo que han comenzado a agregar nuevos parámetros a window.onerror. Como tener filas y no columnas no parecía muy simétrico, IE comenzó a agregar columnas como cuarto parámetro. Sin embargo, la gente está más preocupada por obtener la pila completa, por lo que Firefox dice que es mejor poner la pila en el quinto parámetro. Pero Chrome cree que es mejor colocar el objeto Error completo en el quinto parámetro para que todos puedan leer las propiedades que quieran, incluidas las propiedades personalizadas. Como resultado, Chrome implementó rápidamente la nueva firma window.onerror en Chrome 30, y el borrador del estándar hizo lo mismo.

ventana.onerror = function(onerror = function( mensaje de error, scriptURI, número de línea, número de columna, error) { if (error) { reportError(error); } else { reportError({ mensaje: mensaje de error, script: scriptURI, línea: lineNumber, columna: columnNumber }); }}

Regularización de atributos

Los atributos y nombres del objeto Error que analizamos anteriormente se basan en el método de denominación de Chrome. navegador, pero diferentes navegadores nombran las propiedades del objeto Error de manera diferente. Por ejemplo, la dirección del archivo de secuencia de comandos se llama secuencia de comandos en el navegador Chrome, pero se llama nombre de archivo en Firefox, por lo que necesitamos una función especial para regularizar el objeto Error. mapear diferentes nombres de atributos a nombres de atributos unificados. Consulte este artículo para conocer métodos específicos. Aunque la implementación del navegador se actualizará, dicha tabla de mapeo no se mantendrá manualmente.

No es difícil. tiene un formato similar, por lo que también debe mantener manualmente una expresión regular para extraer la identidad de cada cuadro del texto sin formato, secuencias de comandos, filas y columnas.

Restricciones de seguridad

. Si alguna vez ha encontrado un error con un mensaje de "Error de secuencia de comandos", sabe de lo que estoy hablando; en realidad, esto es una restricción impuesta por los navegadores a archivos de secuencias de comandos de diferentes orígenes. El razonamiento detrás de esta restricción de seguridad es el siguiente: Supongamos que un banco en línea devuelve un HTML diferente al que ve un usuario anónimo después de iniciar sesión, y un sitio web de terceros puede. El URI del banco se coloca en el atributo script.src. El HTML no se puede analizar en JS, por lo que el navegador. generará una excepción y el sitio web de terceros puede determinar si el usuario ha iniciado sesión analizando la ubicación de la excepción para determinar si el usuario ha iniciado sesión. Para hacer esto, el navegador filtrará todas las excepciones lanzadas por scripts de origen diferente. archivos en un único mensaje sin cambios "Error de secuencia de comandos"

Para sitios de cierto tamaño, no es raro que las secuencias de comandos se alojen en CDN de diferentes orígenes. Incluso si su sitio es más pequeño, se utilizan marcos comunes como jQuery y. Backbone puede hacer referencia directamente a versiones en CDN públicas para acelerar la velocidad de descarga de los usuarios. Por lo tanto, esta limitación de seguridad causa algunos problemas, lo que genera excepciones inútiles de "errores de script" que recopilamos de Chrome y Firefox.

CORS

Para evitar esta limitación, solo necesita asegurarse de que el archivo de script esté en el mismo origen que la página misma. Sin embargo, ¿colocar archivos de script en un servidor que no esté acelerado por CDN no ralentizaría las velocidades de descarga de los usuarios? Una solución es colocar el archivo de secuencia de comandos en una CDN, usar XMLHttpRequest para descargar el contenido a través de CORS y luego crear una etiqueta para inyectarla en la página. Por supuesto, el código incrustado en la página es el mismo código fuente.

Suena simple, pero hay muchos detalles que implementar.

Un ejemplo sencillo:

lt;script src="/step1.js"gt;lt;/scriptgt;lt;scriptgt; (función paso2() {})();lt;/scriptgt ;lt ;script src="/step3.js");lt;/scriptgt;lt;script src="/step3.js"gt;lt;/scriptgt;

Todos sabemos que si el paso 1. Si existe una dependencia entre los pasos 2 y 3, deben ejecutarse en este orden; de lo contrario, se producirá un error. El navegador puede solicitar los archivos de los pasos 1 y 3 en paralelo, pero el orden de ejecución está garantizado. Si obtenemos el contenido del archivo de los pasos 1 y 3 nosotros mismos a través de XMLHttpRequest, debemos asegurarnos de que el orden sea correcto. Además, no te olvides del paso 2, que se puede ejecutar mientras el paso 1 se descarga sin bloqueo, por lo que también tenemos que intervenir para que el paso 2 espere a que se complete el paso 1 antes de ejecutar.

Si ya tenemos un conjunto de herramientas que generan etiquetas para diferentes páginas del sitio web, entonces necesitamos adaptar este conjunto de herramientas para poder cambiar las etiquetas p>

p>

lt;scriptgt; ScheduleRemoteScript('/step1.js');lt;/scriptgt;lt;scriptgt; ScheduleInlineScript(código de función() { (función paso2() {})() ; });lt ;/scriptgt;lt;scriptgt; ScheduleRemoteScript('/step3.js');lt;/scriptgt;

Necesitamos implementar las funciones ScheduleRemoteScript y ScheduleInlineScript y asegurarnos de que sean las mismas. el primero que hace referencia al archivo de script externo # scriptgt; Tenga en cuenta que la función paso 2 que se ejecutará inmediatamente ahora se coloca dentro de una función de código más grande que no se ejecutará pero que es solo un contenedor para que el código paso 2 original no se escape y se conserve, pero no se ejecute inmediatamente.

A continuación, necesitamos implementar un mecanismo completo para garantizar que el contenido del archivo descargado de la dirección mediante ScheduleRemoteScript y el código obtenido directamente mediante ScheduleInlineScript se ejecuten en el orden correcto. No te daré los detalles del código aquí, si estás interesado puedes implementarlo tú mismo.

Verificación inversa del número de línea

Inyectar código en la página después de recuperarlo a través de CORS romperá las restricciones de seguridad, pero también generará un nuevo problema: conflicto de número de línea. Inicialmente, puede encontrar archivos de script únicos mediante error.script y números de línea únicos mediante error.line. Ahora, debido a que el código está incrustado en la página, error.script no puede distinguir entre múltiples etiquetas