Manejo de excepciones de JavaScript


82

¿Cuál es la mejor técnica para detectar TODAS las excepciones lanzadas dentro de JavaScript?

Obviamente, la mejor técnica es usar try ... catch. Pero con devoluciones de llamada ansincrónicas y demás, eso puede ser complicado.

Sé que los navegadores IE y Gecko son compatibles con window.onerror, pero ¿qué pasa con Opera y Safari?

Aquí hay un montón de casos de prueba para los que me gustaría tener una solución central de manejo de excepciones:

// ErrorHandler-Test1
var test = null;
test.arg = 5;
// ErrorHandler-Test2
throw (new Error("Hello"));
// ErrorHandler-Test3
throw "Hello again";
// ErrorHandler-Test4
throw {
    myMessage: "stuff",
    customProperty: 5,
    anArray: [1, 2, 3]
};
// ErrorHandler-Test5
try {
    var test2 = null;
    test2.arg = 5;
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test6
try {
    throw (new Error("Goodbye"));
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test7
try {
    throw "Goodbye again";
} catch(e) {
    ErrorHandler.handleError(e);
}
// ErrorHandler-Test8
try {
    throw {
        myMessage: "stuff",
        customProperty: 5,
        anArray: [1, 2, 3]
    };
} catch(e) {
    ErrorHandler.handleError(e);
}

Si piensa en otros casos de prueba, menciónelos. Varios de estos casos mencionan un método ErrorHandler.handleError. Esta es solo una guía sugerida al usar try ... catch.


Solo porque está claro que no notó los comentarios en mi respuesta: parece que me votó en contra de "declarado en la pregunta" cuando se lee como una pregunta: "pero ¿qué pasa con Opera y Safari? Respondí específicamente a una pregunta planteada aquí. WTF?
párpados

1
En WebKit, la window.onerrorfalta ha sido un problema desde 2003 , pero parece que finalmente se está resolviendo . Por supuesto, Opera seguirá siendo obstinada.
josh3736

FYI window.onerror funciona bien en - Firefox 7.0.1 - IE 8 - Chrome 16 - Opera 11.60
Vitaliy Kaplich

11
No estoy seguro de por qué se cerró esto. Votado para reabierto.
ripper234

1
De acuerdo, esta es una pregunta útil. Lo único en lo que puedo pensar es en casperOne ¿temía que esto se convirtiera en algún tipo de discusión sobre la guerra del navegador?
Jordan Reiter

Respuestas:


23

Si usa una biblioteca como jQuery para asignar todos sus controladores de eventos, puede usar una combinación de window.onerrory envolver el código del controlador de eventos jQuery y una función lista con una función de manejo de errores (consulte: Seguimiento de errores de JavaScript: por qué window.onerror no es suficiente ).

  • window.onerror: detecta todos los errores en IE (y la mayoría de los errores en Firefox), pero no hace nada en Safari y Opera.
  • Controladores de eventos de jQuery: detecta errores de eventos de jQuery en todos los navegadores.
  • Función jQuery ready: detecta errores de inicialización en todos los navegadores.

Ya había leído el artículo que no ofrece ninguna solución. No uso jQuery, pero me preocupa lo que insinúas. ¿Estás diciendo que jQuery está comiendo excepciones? ¿O simplemente proporcionan una funcionalidad de registro (que solo es útil después de que se responda mi pregunta original)?
harley.333

jQuery no hace nada especial con Excepciones. Pero si usa jQuery para agregar todos sus eventos, le brinda un solo lugar para agregar su declaración try catch y código de manejo de errores como se explica en el segundo enlace en mi respuesta.
Karl

2
El enlace proporcionado se movió: blogs.cozi.com/tech/2008/04/…
natacado

1
¿Sigue siendo cierto esto? Acabo de probar el onerrorenfoque de manejo normal en Safari + Chrome y parece funcionar correctamente.
Juri

El enlace que le dio a por qué "no es suficiente" es información bastante antigua y no es el caso ahora.
vsync

21

WebKit (Safari, Chrome, etc.) ahora parece ser compatible onerror.

Publicación original: Hasta donde yo sé, WebKit / Safari no es compatible con el onerrorevento. Lo cual es una maldita vergüenza.


19
Uh, no, no lo es. Preguntado en la pregunta: "pero ¿qué pasa con Opera y Safari?"
párpados

Al igual que en, respondí específicamente a una pregunta planteada en la publicación de preguntas.
párpados

8
Creo que quiso decir "¿qué [hago] acerca de Opera y Safari [que no tienen window.onerror]?"
nickf

10
@nickf, tal vez, pero si es así, está muy mal redactado, y mi interpretación es, en el peor de los casos, comprensible y, en el mejor de los casos, la más probable. Es una razón bastante pobre para que la gente rechace mi respuesta, especialmente dado que la pregunta no la establece , y otros lectores pueden beneficiarse de una declaración afirmativa o negativa.
párpados

Chrome ahora parece admitirlo.
Daniel X Moore

7

En realidad, el enfoque de jquery no es tan malo. Ver:

http://docs.jquery.com/Events/error#fn

y:

$(window).error(function(msg, url, line){
  $.post("js_error_log.php", { msg: msg, url: url, line: line });
});

11
para cualquiera que encuentre esto ahora, jquery realmente recomienda no vincular un evento al evento window.error, consulte el enlace de documentación anterior. Extraer: No se debe adjuntar un controlador de eventos de error jQuery al objeto de la ventana. [...] Utilice window.onerror en su lugar.
zcrar70

ofc no se supone que esté enlazado de esta manera, ¡es ilógico! pueden ocurrir muchos errores en el momento en que se hizo esta oferta (si se hizo con éxito ...)
vsync

6

Capture todas las excepciones con su propio controlador de excepciones y use instanceof.

$("inuput").live({
    click : function (event) {
        try {
            if (somethingGoesWrong) {
                throw new MyException();
            }
        } catch (Exception) {
            new MyExceptionHandler(Exception);
        }

    }
});

function MyExceptionHandler(Exception) {
    if (Exception instanceof TypeError || 
        Exception instanceof ReferenceError || 
        Exception instanceof RangeError ||  
        Exception instanceof SyntaxError ||     
        Exception instanceof URIError ) {
        throw Exception; // native error
     } else {
         // handle exception
     }
}

MyExcetpionHandler arrojará un error nativo ya que no hay un bloque try-catch.

Visite http://www.nczonline.net/blog/2009/03/10/the-art-of-throwing-javascript-errors-part-2/


4

try-catchno siempre es la mejor solución. Por ejemplo, en Chrome 7.0 pierde el buen seguimiento de la pila en la ventana de la consola. Relanzar la excepción no ayuda. No conozco ninguna solución que conserve los rastros de pila y le permita reaccionar en caso de excepción.


2

Con un poco de trabajo, es posible obtener trazas de pila que estén razonablemente completas en todos los navegadores.

Chrome y Opera modernos (es decir, cualquier cosa basada en el motor de renderizado Blink) son totalmente compatibles con la especificación de borrador HTML 5 para ErrorEvent y window.onerror. En ambos navegadores puede utilizar window.onerroro (¡sorprendentemente!) Enlazar correctamente con el evento 'error':

// Only Chrome & Opera pass the error object.
window.onerror = function (message, file, line, col, error) {
    console.log(message, "from", error.stack);
    // You can send data to your server
    // sendData(data);
};
// Only Chrome & Opera have an error attribute on the event.
window.addEventListener("error", function (e) {
    console.log(e.error.message, "from", e.error.stack);
    // You can send data to your server
    // sendData(data);
})

Desafortunadamente, Firefox, Safari e IE todavía existen y también tenemos que admitirlos. Como el stacktrace no está disponible window.onerror, tenemos que trabajar un poco más.

Resulta que lo único que podemos hacer para obtener trazos de pila de errores es envolver todo nuestro código en un try{ }catch(e){ }bloque y luego mirar e.stack. Podemos hacer el proceso algo más fácil con una función llamada wrap que toma una función y devuelve una nueva función con un buen manejo de errores.

function wrap(func) {
    // Ensure we only wrap the function once.
    if (!func._wrapped) {
        func._wrapped = function () {
            try{
                func.apply(this, arguments);
            } catch(e) {
                console.log(e.message, "from", e.stack);
                // You can send data to your server
                // sendData(data);
                throw e;
            }
        }
    }
    return func._wrapped;
};

Esto funciona. Cualquier función que ajuste manualmente tendrá un buen manejo de errores.

Puede enviar datos usando la etiqueta de imagen de la siguiente manera

function sendData(data) {
    var img = newImage(),
        src = http://yourserver.com/jserror + '&data=' + encodeURIComponent(JSON.stringify(data));

    img.crossOrigin = 'anonymous';
    img.onload = function success() {
        console.log('success', data);
    };
    img.onerror = img.onabort = function failure() {
        console.error('failure', data);
    };
    img.src = src;
}

Sin embargo, debe hacer backend para recopilar los datos y front-end para visualizar los datos.

En Atatus , estamos trabajando para solucionar este problema. Más que seguimiento de errores, Atatus proporciona un monitoreo real del usuario.

Prueba https://www.atatus.com/

Descargo de responsabilidad: soy desarrollador web en Atatus.


1

Es cierto que con los navegadores modernos, el enganche de window.onerror para errores que burbujean hasta la parte superior junto con la adición de controladores de eventos jQuery para errores Ajax capturará prácticamente todos los objetos Error arrojados en su código de cliente. Si está configurando manualmente un controlador para window.onerror, en los navegadores modernos esto se hace window.addEventListener('error', callback), mientras que en IE8 / 9 debe llamar window.attachEvent('onerror', callback).

Tenga en cuenta que, a continuación, debe considerar el entorno en el que se manejan estos errores y el motivo para hacerlo. Una cosa es detectar tantos errores como sea posible con sus trazas de pila, pero el advenimiento de las modernas herramientas de desarrollo F12 resuelve este caso de uso al implementar y depurar localmente. Los puntos de interrupción, etc., le proporcionarán más datos de los que están disponibles en los controladores, especialmente para los errores lanzados por bibliotecas de terceros que se cargaron desde solicitudes CORS. Debe realizar pasos adicionales para indicar al navegador que proporcione estos datos.

El problema clave es proporcionar estos datos en producción, ya que sus usuarios tienen la garantía de ejecutar una gama de navegadores y versiones mucho más amplia de la que posiblemente pueda probar, y su sitio / aplicación se romperá de formas inesperadas, sin importar cuánto control de calidad arroje. eso.

Para manejar esto, necesita un rastreador de errores de producción que capte todos los errores lanzados en los navegadores de sus usuarios, a medida que usan su código, y los envía a un punto final donde usted puede ver los datos y usarlos para corregir los errores a medida que ocurren. . En Raygun (descargo de responsabilidad: trabajo en Raygun) hemos hecho un gran esfuerzo para brindar una gran experiencia para esto, ya que hay muchas trampas y problemas a considerar que una implementación ingenua no se realizará.

Por ejemplo, es probable que agrupe y minimice sus activos JS, lo que significa que los errores lanzados desde el código minificado tendrán trazas de pila basura con nombres de variables alterados. Para esto, necesita su herramienta de compilación para generar mapas de origen (recomendamos UglifyJS2 para esta parte de la canalización), y su rastreador de errores para aceptarlos y procesarlos, convirtiendo los rastros de pila destrozados en otros legibles por humanos. Raygun hace todo esto de inmediato e incluye un punto final de API para aceptar mapas de origen a medida que los genera su proceso de compilación. Esto es clave, ya que deben mantenerse como no públicos, de lo contrario, cualquiera podría desminificar su código, negando su propósito.

La biblioteca cliente raygun4js también viene con window.onerrornavegadores modernos y heredados, así como con ganchos de jQuery listos para usar, por lo que para configurar esto solo necesita agregar:

<script type="text/javascript" src="//cdn.raygun.io/raygun4js/raygun.min.js" </script> <script> Raygun.init('yourApiKey').attach(); </script>

También hay un montón de funcionalidades integradas, incluida la capacidad de mutar la carga útil del error antes de que se envíe, agregando etiquetas y datos personalizados, metadatos sobre el usuario que vio el error. También elimina la molestia de obtener buenos seguimientos de pila de los scripts CORS de terceros mencionados anteriormente, lo que resuelve el temido 'Error de script' (que no contiene ningún mensaje de error ni seguimiento de pila).

Un tema más crucial es que debido a la gran audiencia en la web, su sitio generará miles de instancias duplicadas de cada error. Un servicio de seguimiento de errores como Raygun tiene la inteligencia de agruparlos en grupos de errores para que no se ahogue en una avalancha de notificaciones y le permite ver cada error real listo para ser reparado.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.