Meccanismo di eventi globali JavaScript


350

Vorrei rilevare ogni errore di funzione indefinito generato. Esiste una funzione di gestione degli errori globale in JavaScript? Il caso d'uso sta rilevando dal flash chiamate di funzione non definite.


Cosa vuoi fare con un errore dopo averlo rilevato? Devi solo registrarlo in modo da poter creare la funzione mancante o stai cercando di fermare le eccezioni dalla violazione del codice?
Dan Herbert,

2
Vorrei ottenere il nome della funzione mancante chiamata e in base alla presenza di una stringa chiamare la mia funzione. Qualsiasi chiamata a una funzione con la stringa 'close' chiamerebbe my close () per esempio. Vorrei anche intercettare l'errore a quel punto.

2
exceptionsjs.com fornisce questa funzionalità e può essere personalizzato per rilevare solo errori relativi a funzionalità indefinita con la sua funzionalità "guard".
Steven Wexler,

Risposte:


193

Questo ti aiuta:

<script type="text/javascript">
window.onerror = function() {
    alert("Error caught");
};

xxx();
</script>

Non sono sicuro di come gestisca gli errori Flash però ...

Aggiornamento: non funziona in Opera, ma sto hackerando Dragonfly in questo momento per vedere cosa ottiene. Il suggerimento sull'hacking di Dragonfly è venuto da questa domanda:

Finestra mimica. onerror in Opera usando javascript


3
Con l'aggiunta di msg, file_loc, line_no params questo dovrebbe farlo per me. Grazie!

1
Comprendo correttamente che questo codice ignora eventuali gestori di errori esistenti?
Mars Robertson,

@MarsRobertson No.
atilkan,

@atilkan window.onerror = function() { alert(42) };ora il codice nella risposta: window.onerror = function() { alert("Error caught"); };non ignorato, non sono ancora sicuro ..
Mars Robertson

@MarsRobertson Ho capito. Probabilmente sovrascrive. Sì, lo fa, appena testato. L'uso di addEventListener sarebbe meglio.
Atilkan,

646

Come rilevare errori Javascript non gestiti

Assegna l' window.onerrorevento a un gestore eventi come:

<script type="text/javascript">
window.onerror = function(msg, url, line, col, error) {
   // Note that col & error are new to the HTML 5 spec and may not be 
   // supported in every browser.  It worked for me in Chrome.
   var extra = !col ? '' : '\ncolumn: ' + col;
   extra += !error ? '' : '\nerror: ' + error;

   // You can view the information in an alert to see things working like this:
   alert("Error: " + msg + "\nurl: " + url + "\nline: " + line + extra);

   // TODO: Report this error via ajax so you can keep track
   //       of what pages have JS issues

   var suppressErrorAlert = true;
   // If you return true, then error alerts (like in older versions of 
   // Internet Explorer) will be suppressed.
   return suppressErrorAlert;
};
</script>

Come commentato nel codice, se il valore restituito window.onerrorè truequindi il browser dovrebbe sopprimere la visualizzazione di una finestra di avviso.

Quando si attiva l'evento window.onerror?

In breve, l'evento viene generato quando 1.) si verifica un'eccezione non rilevata o 2.) si verifica un errore di tempo di compilazione.

eccezioni non rilevate

  • lancia "alcuni messaggi"
  • call_something_undefined ();
  • cross_origin_iframe.contentWindow.document ;, un'eccezione di sicurezza

errore di compilazione

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);, tenterà di compilare il primo argomento come script

Browser che supportano window.onerror

  • Chrome 13+
  • Firefox 6.0+
  • Internet Explorer 5.5+
  • Opera 11.60+
  • Safari 5.1+

Immagine dello schermo:

Esempio del codice onerror sopra in azione dopo averlo aggiunto a una pagina di test:

<script type="text/javascript">
call_something_undefined();
</script>

Avviso JavaScript che mostra le informazioni di errore dettagliate dall'evento window.onerror

Esempio per la segnalazione degli errori AJAX

var error_data = {
    url: document.location.href,
};

if(error != null) {
    error_data['name'] = error.name; // e.g. ReferenceError
    error_data['message'] = error.line;
    error_data['stack'] = error.stack;
} else {
    error_data['msg'] = msg;
    error_data['filename'] = filename;
    error_data['line'] = line;
    error_data['col'] = col;
}

var xhr = new XMLHttpRequest();

xhr.open('POST', '/ajax/log_javascript_error');
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
    if (xhr.status === 200) {
        console.log('JS error logged');
    } else if (xhr.status !== 200) {
        console.error('Failed to log JS error.');
        console.error(xhr);
        console.error(xhr.status);
        console.error(xhr.responseText);
    }
};
xhr.send(JSON.stringify(error_data));

JSFiddle:

https://jsfiddle.net/nzfvm44d/

Riferimenti:


3
vale la pena ricordare che Firefox non restituisce il messaggio di errore quando throwviene effettuato manualmente. stackoverflow.com/questions/15036165/…
Sebas

2
Bella risposta. È possibile implementare "Segnala questo errore tramite Ajax" con un pacchetto come JSNLog, che esegue la registrazione sul lato server e Ajax per te.
user1147862

4
Oltre a questa risposta, ho aggiunto l'oggetto err in modo da poter ottenere la traccia dello stack. stackoverflow.com/a/20972210/511438 . Ora posso sviluppare con più feedback perché i miei errori di sviluppo vengono visualizzati come una casella nella parte superiore della pagina (come l'ho creato).
Valamas,

Non sarebbe meglio racchiudere l'implementazione di onerror in un blocco try-catch (ignore), impedendo a onerror () di lanciare ancora più errori? (non che sia il caso qui, ma per essere sicuro)
Pieter De Bie,

1
@ super1ha1 Puoi fornire un jsfiddle? Non credo che i browser introdurrebbero un cambiamento radicale in cui il gestore di eventi onerror smette di generare eventi. Puoi provare questo jsfiddle per vedere il gestore onerror funzionare: jsfiddle.net/nzfvm44d Questo funziona ancora per me nella versione 62.0.3202.94 di Chrome (build ufficiale) (64-bit).
Sam,

38

sofisticata gestione degli errori

Se la tua gestione degli errori è molto sofisticata e quindi potrebbe generare un errore stesso, è utile aggiungere un flag che indica se sei già in "errorHandling-Mode". Così:

var appIsHandlingError = false;

window.onerror = function() {
    if (!appIsHandlingError) {
        appIsHandlingError = true;
        handleError();
    }
};

function handleError() {
    // graceful error handling
    // if successful: appIsHandlingError = false;
}

Altrimenti potresti ritrovarti in un ciclo infinito.


29
O un modo più sicuro sarebbe usare try-catch attorno al handleErrormetodo.
Aidiakapi,

8
Non puoi utilizzare try catch se hai una chiamata sincrona nella gestione degli errori. Quindi la sua soluzione rimane una buona soluzione
Emrys Myrooin,

@EmrysMyrooin Tuttavia causerà una sincronizzazione o un ciclo asincrono mediante la gestione degli errori e probabilmente si arresterà in modo anomalo senza informazioni sullo stack utilizzabili.
inf3rno,

1
Penso che la bandiera dovrebbe essere ripristinata ad un certo punto, giusto? Credo che abbiamo bisogno di un tentativo / catch around se grande se, e assicurarsi che il flag sia resettato subito prima di lasciare il metodo onerror. Altrimenti, verrà gestito solo un errore.
Seb

23

Prova Atatus che fornisce il monitoraggio avanzato degli errori e il monitoraggio degli utenti reali per le app Web moderne.

https://www.atatus.com/

Lascia che ti spieghi come ottenere stack stack ragionevolmente completi in tutti i browser.

Gestione degli errori in JavaScript

Chrome e Opera moderni supportano completamente le specifiche di bozza HTML 5 per ErrorEvent e window.onerror. In entrambi questi browser è possibile utilizzare window.onerroro associare correttamente l'evento "errore":

// 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
    // sendError(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
    // sendError(data);
})

Purtroppo Firefox, Safari e IE sono ancora in circolazione e anche noi dobbiamo supportarli. Dato che stacktrace non è disponibile in, window.onerrordobbiamo fare un po 'più di lavoro.

Si scopre che l'unica cosa che possiamo fare per ottenere stacktraces dagli errori è racchiudere tutto il nostro codice in un try{ }catch(e){ }blocco e quindi guardare e.stack. Possiamo semplificare un po 'il processo con una funzione chiamata wrap che accetta una funzione e restituisce una nuova funzione con una buona gestione degli errori.

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
                // sendError(data);
                throw e;
            }
        }
    }
    return func._wrapped;
};

Questo funziona Qualsiasi funzione che avvolgi manualmente avrà una buona gestione degli errori, ma si scopre che possiamo effettivamente farlo automaticamente per te nella maggior parte dei casi.

Modificando la definizione globale di in addEventListenermodo da avvolgere automaticamente il callback, possiamo inserire automaticamente la try{ }catch(e){ }maggior parte del codice. Ciò consente al codice esistente di continuare a funzionare, ma aggiunge il monitoraggio delle eccezioni di alta qualità.

var addEventListener = window.EventTarget.prototype.addEventListener;
window.EventTarget.prototype.addEventListener = function (event, callback, bubble) {
    addEventListener.call(this, event, wrap(callback), bubble);
}

Dobbiamo anche assicurarci che removeEventListenercontinui a funzionare. Al momento non lo sarà perché l'argomento addEventListenerè cambiato. Ancora una volta dobbiamo solo risolvere questo problema prototypesull'oggetto:

var removeEventListener = window.EventTarget.prototype.removeEventListener;
window.EventTarget.prototype.removeEventListener = function (event, callback, bubble) {
    removeEventListener.call(this, event, callback._wrapped || callback, bubble);
}

Trasmetti i dati di errore al tuo back-end

È possibile inviare i dati di errore utilizzando il tag immagine come segue

function sendError(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;
}

Disclaimer: sono uno sviluppatore web su https://www.atatus.com/ .


Cosa è yourserver.com/jserror ? REST, servizio Web, servizio Wcf ? Qualche semplice sul backend?
Kiquenet,

Se si desidera inviare errori js dal browser dell'utente al server. devi scrivere il tuo backend ( http://yourserver.com) per ricevere e archiviare. Se scegli atatus.com , non devi fare nulla. Includi solo due righe di script nella tua pagina.
Fizer Khan,

18

Sembra che window.onerrornon fornisca l'accesso a tutti i possibili errori. In particolare ignora:

  1. <img> errori di caricamento (risposta> = 400).
  2. <script> errori di caricamento (risposta> = 400).
  3. errori globali se nella tua app ci sono molte altre librerie che manipolano anche window.onerrorin modo sconosciuto (jquery, angolare, ecc.).
  4. probabilmente molti casi in cui non mi sono imbattuto dopo aver esplorato questo ora (iframe, overflow dello stack, ecc.).

Ecco l'inizio di uno script che rileva molti di questi errori, in modo da poter aggiungere un debug più robusto all'app durante lo sviluppo.

(function(){

/**
 * Capture error data for debugging in web console.
 */

var captures = [];

/**
 * Wait until `window.onload`, so any external scripts
 * you might load have a chance to set their own error handlers,
 * which we don't want to override.
 */

window.addEventListener('load', onload);

/**
 * Custom global function to standardize 
 * window.onerror so it works like you'd think.
 *
 * @see http://www.quirksmode.org/dom/events/error.html
 */

window.onanyerror = window.onanyerror || onanyerrorx;

/**
 * Hook up all error handlers after window loads.
 */

function onload() {
  handleGlobal();
  handleXMLHttp();
  handleImage();
  handleScript();
  handleEvents();
}

/**
 * Handle global window events.
 */

function handleGlobal() {
  var onerrorx = window.onerror;
  window.addEventListener('error', onerror);

  function onerror(msg, url, line, col, error) {
    window.onanyerror.apply(this, arguments);
    if (onerrorx) return onerrorx.apply(null, arguments);
  }
}

/**
 * Handle ajax request errors.
 */

function handleXMLHttp() {
  var sendx = XMLHttpRequest.prototype.send;
  window.XMLHttpRequest.prototype.send = function(){
    handleAsync(this);
    return sendx.apply(this, arguments);
  };
}

/**
 * Handle image errors.
 */

function handleImage() {
  var ImageOriginal = window.Image;
  window.Image = ImageOverride;

  /**
   * New `Image` constructor. Might cause some problems,
   * but not sure yet. This is at least a start, and works on chrome.
   */

  function ImageOverride() {
    var img = new ImageOriginal;
    onnext(function(){ handleAsync(img); });
    return img;
  }
}

/**
 * Handle script errors.
 */

function handleScript() {
  var HTMLScriptElementOriginal = window.HTMLScriptElement;
  window.HTMLScriptElement = HTMLScriptElementOverride;

  /**
   * New `HTMLScriptElement` constructor.
   *
   * Allows us to globally override onload.
   * Not ideal to override stuff, but it helps with debugging.
   */

  function HTMLScriptElementOverride() {
    var script = new HTMLScriptElement;
    onnext(function(){ handleAsync(script); });
    return script;
  }
}

/**
 * Handle errors in events.
 *
 * @see http://stackoverflow.com/questions/951791/javascript-global-error-handling/31750604#31750604
 */

function handleEvents() {
  var addEventListenerx = window.EventTarget.prototype.addEventListener;
  window.EventTarget.prototype.addEventListener = addEventListener;
  var removeEventListenerx = window.EventTarget.prototype.removeEventListener;
  window.EventTarget.prototype.removeEventListener = removeEventListener;

  function addEventListener(event, handler, bubble) {
    var handlerx = wrap(handler);
    return addEventListenerx.call(this, event, handlerx, bubble);
  }

  function removeEventListener(event, handler, bubble) {
    handler = handler._witherror || handler;
    removeEventListenerx.call(this, event, handler, bubble);
  }

  function wrap(fn) {
    fn._witherror = witherror;

    function witherror() {
      try {
        fn.apply(this, arguments);
      } catch(e) {
        window.onanyerror.apply(this, e);
        throw e;
      }
    }
    return fn;
  }
}

/**
 * Handle image/ajax request errors generically.
 */

function handleAsync(obj) {
  var onerrorx = obj.onerror;
  obj.onerror = onerror;
  var onabortx = obj.onabort;
  obj.onabort = onabort;
  var onloadx = obj.onload;
  obj.onload = onload;

  /**
   * Handle `onerror`.
   */

  function onerror(error) {
    window.onanyerror.call(this, error);
    if (onerrorx) return onerrorx.apply(this, arguments);
  };

  /**
   * Handle `onabort`.
   */

  function onabort(error) {
    window.onanyerror.call(this, error);
    if (onabortx) return onabortx.apply(this, arguments);
  };

  /**
   * Handle `onload`.
   *
   * For images, you can get a 403 response error,
   * but this isn't triggered as a global on error.
   * This sort of standardizes it.
   *
   * "there is no way to get the HTTP status from a 
   * request made by an img tag in JavaScript."
   * @see http://stackoverflow.com/questions/8108636/how-to-get-http-status-code-of-img-tags/8108646#8108646
   */

  function onload(request) {
    if (request.status && request.status >= 400) {
      window.onanyerror.call(this, request);
    }
    if (onloadx) return onloadx.apply(this, arguments);
  }
}

/**
 * Generic error handler.
 *
 * This shows the basic implementation, 
 * which you could override in your app.
 */

function onanyerrorx(entity) {
  var display = entity;

  // ajax request
  if (entity instanceof XMLHttpRequest) {
    // 400: http://example.com/image.png
    display = entity.status + ' ' + entity.responseURL;
  } else if (entity instanceof Event) {
    // global window events, or image events
    var target = entity.currentTarget;
    display = target;
  } else {
    // not sure if there are others
  }

  capture(entity);
  console.log('[onanyerror]', display, entity);
}

/**
 * Capture stuff for debugging purposes.
 *
 * Keep them in memory so you can reference them
 * in the chrome debugger as `onanyerror0` up to `onanyerror99`.
 */

function capture(entity) {
  captures.push(entity);
  if (captures.length > 100) captures.unshift();

  // keep the last ones around
  var i = captures.length;
  while (--i) {
    var x = captures[i];
    window['onanyerror' + i] = x;
  }
}

/**
 * Wait til next code execution cycle as fast as possible.
 */

function onnext(fn) {
  setTimeout(fn, 0);
}

})();

Potrebbe essere usato in questo modo:

window.onanyerror = function(entity){
  console.log('some error', entity);
};

Lo script completo ha un'implementazione predefinita che tenta di stampare una versione "display" semi-leggibile dell'entità / errore che riceve. Può essere utilizzato come ispirazione per un gestore di errori specifico dell'app. L'implementazione predefinita mantiene anche un riferimento alle ultime 100 entità di errore, quindi è possibile ispezionarle nella console Web dopo che si verificano come:

window.onanyerror0
window.onanyerror1
...
window.onanyerror99

Nota: funziona sostituendo i metodi su diversi browser / costruttori nativi. Ciò può avere effetti collaterali indesiderati. Tuttavia, è stato utile utilizzare durante lo sviluppo, per capire dove si verificano errori, per inviare registri a servizi come NewRelic o Sentry durante lo sviluppo in modo da poter misurare gli errori durante lo sviluppo e la gestione temporanea in modo da poter eseguire il debug di ciò che sta succedendo a un livello più profondo. Può quindi essere spento in produzione.

Spero che sia di aiuto.


1
A quanto pare le immagini innescano l'evento di errore: stackoverflow.com/a/18152753/607033
inf3rno

6
// display error messages for a page, but never more than 3 errors
window.onerror = function(msg, url, line) {
if (onerror.num++ < onerror.max) {
alert("ERROR: " + msg + "\n" + url + ":" + line);
return true;
}
}
onerror.max = 3;
onerror.num = 0;

6

Si dovrebbe preservare anche il callback onerror precedentemente associato

<script type="text/javascript">

(function() {
    var errorCallback = window.onerror;
    window.onerror = function () {
        // handle error condition
        errorCallback && errorCallback.apply(this, arguments);
    };
})();

</script>

3

Se si desidera un modo unificato per gestire sia gli errori non rilevati sia i rifiuti non gestiti, è possibile dare un'occhiata alla libreria non rilevata .

MODIFICARE

<script type="text/javascript" src=".../uncaught/lib/index.js"></script>

<script type="text/javascript">
    uncaught.start();
    uncaught.addListener(function (error) {
        console.log('Uncaught error or rejection: ', error.message);
    });
</script>

Ascolta la finestra. rifiuto non gestito oltre a window.onerror.


2
Lance Pollard nella risposta sopra menzionava alcuni errori che il normale onerror non gestisce. La biblioteca li gestisce? Se alcuni di essi, si prega di specificare quale?
Michael Freidgeim,

@ MiFreidgeimSO-stopbeingevil Ho appena controllato il codice sorgente - no, sfortunatamente no. Aleksandr Oleynikov: Sarebbe fantastico se tu potessi supportare questo - poi trasformerò il mio
voto negativo

2

Consiglierei di provare Trackjs .

È la registrazione degli errori come servizio.

È incredibilmente semplice da configurare. Basta aggiungere una riga <script> a ciascuna pagina e il gioco è fatto. Questo significa anche che sarà incredibilmente semplice da rimuovere se decidi che non ti piace.

Ci sono altri servizi come Sentry (che è open-source se puoi ospitare il tuo server), ma non fa quello che fa Trackjs. Trackjs registra l'interazione dell'utente tra il proprio browser e il proprio server Web in modo da poter effettivamente tracciare i passaggi dell'utente che hanno portato all'errore, al contrario di un semplice riferimento a file e numero di riga (e forse stack trace).


2
TrackJS non sembra più avere un livello gratuito , sebbene ci sia una prova gratuita.
pkaeding,

Questo va bene per il monitoraggio e la segnalazione di errori ma non risolve davvero la parte relativa alla gestione. Idealmente penso che il richiedente sia alla ricerca di un modo per gestire questi errori in modo che il resto del codice sia ancora in esecuzione.
settimane

Cosa succede se si rileva l'evento di errore e si aggiunge una chiamata xhr al logger con la traccia dello stack e lo stato dell'applicazione? In che modo trackjs è migliore?
inf3rno,

2
@ inf3rno è più di una traccia dello stack, che traccia solo l'inizio dell'errore. TrackJS seguirà l'intera sessione dell'utente in modo da poter vedere cosa ha portato ad essa. ecco uno screenshot come esempio trackjs.com/assets/images/screenshot.png
kane,

1

Ascolti l'evento onerror assegnando una funzione a window.onerror:

 window.onerror = function (msg, url, lineNo, columnNo, error) {
        var string = msg.toLowerCase();
        var substring = "script error";
        if (string.indexOf(substring) > -1){
            alert('Script Error: See Browser Console for Detail');
        } else {
            alert(msg, url, lineNo, columnNo, error);
        }   
      return false; 
  };
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.