393 Stimmen

Globaler JavaScript-Ereignis-Mechanismus

Ich möchte jeden undefinierten Funktionsfehler abfangen, der ausgelöst wird. Gibt es in JavaScript eine Möglichkeit zur globalen Fehlerbehandlung? Der Anwendungsfall ist das Abfangen von Funktionsaufrufen aus Flash, die nicht definiert sind.

678voto

Sam Punkte 26229

Wie man unbehandelte Javascript-Fehler abfängt

Weisen Sie den window.onerror Ereignis an einen Ereignishandler wie:

<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>

Wie im Code kommentiert, wird, wenn der Rückgabewert von window.onerror es true dann sollte der Browser die Anzeige eines Warndialogs unterdrücken.

Wann wird das Ereignis window.onerror ausgelöst?

Kurz gesagt, das Ereignis wird ausgelöst, wenn entweder 1.) eine nicht abgefangene Ausnahme auftritt oder 2.) ein Kompilierzeitfehler.

nicht gefangene Ausnahmen

  • werfen "einige Nachrichten"
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document;, eine Sicherheitsausnahme

Kompilierfehler

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10); zu kompilieren, wird es versuchen, die ein Skript

Browser, die window.onerror unterstützen

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

Screenshot:

Beispiel für den obigen onerror-Code in Aktion nach dem Hinzufügen zu einer Testseite:

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

Javascript alert showing error information detailed by the window.onerror event

Beispiel für AJAX-Fehlerberichte

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/

Referenzen:

205voto

Ionuț G. Stan Punkte 168218

Hilft Ihnen das?

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

xxx();
</script>

Ich bin mir allerdings nicht sicher, wie es mit Flash-Fehlern umgeht...

Update: Es funktioniert nicht in Opera, aber ich hacke gerade Dragonfly, um zu sehen, was es bringt. Der Vorschlag, Dragonfly zu hacken, kam von dieser Frage:

Nachahmung von Window. onerror in Opera mit javascript

41voto

SunnyRed Punkte 3386

Ausgefeilte Fehlerbehandlung

Wenn Ihre Fehlerbehandlung sehr anspruchsvoll ist und daher selbst einen Fehler auslösen könnte, ist es sinnvoll, ein Flag hinzuzufügen, das angibt, ob Sie sich bereits im "errorHandling-Modus" befinden. Zum Beispiel so:

var appIsHandlingError = false;

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

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

Andernfalls könnten Sie sich in einer Endlosschleife wiederfinden.

28voto

Lance Punkte 69299

Es scheint, dass window.onerror bietet keinen Zugang zu allen möglichen Fehlern. Insbesondere ignoriert es:

  1. <img> Ladefehler (Antwort >= 400).
  2. <script> Ladefehler (Antwort >= 400).
  3. globale Fehler, wenn Sie viele andere Bibliotheken in Ihrer Anwendung haben, die ebenfalls window.onerror auf eine unbekannte Art und Weise (Jquery, Angular usw.).
  4. Wahrscheinlich gibt es viele Fälle, auf die ich nicht gestoßen bin, nachdem ich das jetzt untersucht habe (iframes, Stack Overflow usw.).

Hier ist der Anfang eines Skripts, das viele dieser Fehler abfängt, so dass Sie Ihre Anwendung während der Entwicklung mit einem robusteren Debugging versehen können.

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

})();

Es könnte so verwendet werden:

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

Das vollständige Skript hat eine Standard-Implementierung, die versucht, eine halbwegs lesbare "Display"-Version der Entität/des Fehlers, die es erhält, auszudrucken. Kann als Inspiration für einen app-spezifischen Error-Handler verwendet werden. Die Standard-Implementierung behält auch einen Verweis auf die letzten 100 Fehler-Entitäten, so dass Sie sie in der Web-Konsole inspizieren können, nachdem sie auftreten, wie:

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

Hinweis: Dies funktioniert durch das Überschreiben von Methoden mehrerer Browser/nativer Konstruktoren. Dies kann unbeabsichtigte Nebeneffekte haben. Es hat sich jedoch als nützlich erwiesen, es während der Entwicklung zu verwenden, um herauszufinden, wo Fehler auftreten, um während der Entwicklung Protokolle an Dienste wie NewRelic oder Sentry zu senden, damit wir Fehler während der Entwicklung messen können, und im Staging, damit wir debuggen können, was auf einer tieferen Ebene vor sich geht. In der Produktion kann es dann abgeschaltet werden.

Ich hoffe, das hilft.

26voto

Fizer Khan Punkte 79079

Versuchen Sie Atatus das fortschrittliches Error Tracking und Real User Monitoring für moderne Webanwendungen bietet.

https://www.atatus.com/

Ich möchte Ihnen erklären, wie Sie Stacktraces erhalten, die in allen Browsern einigermaßen vollständig sind.

Fehlerbehandlung in JavaScript

Modern Chrome und Opera unterstützen die HTML 5-Entwurfsspezifikation für ErrorEvent und window.onerror . In diesen beiden Browsern können Sie entweder window.onerror oder binden Sie das Ereignis "Fehler" richtig:

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

Leider gibt es Firefox, Safari und IE immer noch und wir müssen auch sie unterstützen. Da der Stacktrace nicht verfügbar ist in window.onerror müssen wir noch ein wenig mehr Arbeit leisten.

Es stellt sich heraus, dass das Einzige, was wir tun können, um Stacktraces von Fehlern zu erhalten, darin besteht, unseren gesamten Code in eine try{ }catch(e){ } Block und schauen Sie sich dann e.stack . Wir können den Prozess mit einer Funktion namens wrap, die eine Funktion annimmt und eine neue Funktion mit guter Fehlerbehandlung zurückgibt, etwas vereinfachen.

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;
};

Das funktioniert. Jede Funktion, die Sie manuell einpacken, wird eine gute Fehlerbehandlung haben, aber es stellt sich heraus, dass wir es in den meisten Fällen automatisch für Sie tun können.

Durch die Änderung der globalen Definition von addEventListener so dass es automatisch den Rückruf umhüllt, können wir automatisch einfügen try{ }catch(e){ } um den meisten Code. Dadurch kann der vorhandene Code weiter verwendet werden, aber es wird eine hochwertige Ausnahmeverfolgung hinzugefügt.

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

Wir müssen auch dafür sorgen, dass removeEventListener funktioniert weiter. Im Moment wird es nicht funktionieren, weil das Argument zu addEventListener geändert wird. Auch hier müssen wir dies nur auf der prototype Objekt:

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

Übermittlung von Fehlerdaten an Ihr Backend

Sie können Fehlerdaten mit dem Image-Tag wie folgt senden

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;
}

Haftungsausschluss: Ich bin ein Webentwickler bei https://www.atatus.com/ .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X