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.
Antworten
Zu viele Anzeigen?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>
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:
- Mozilla Developer Network :: window.onerror
- MSDN :: Behandlung und Vermeidung von Webseitenfehlern Teil 2: Laufzeitfehler
- Zurück zu den Grundlagen - JavaScript onerror Ereignis
- DEV.OPERA :: Bessere Fehlerbehandlung mit window.onerror
- Fenster onError Ereignis
- Verwendung des Ereignisses onerror zur Unterdrückung von JavaScript-Fehlern
- SO :: window.onerror wird in Firefox nicht ausgelöst
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:
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.
Es scheint, dass window.onerror
bietet keinen Zugang zu allen möglichen Fehlern. Insbesondere ignoriert es:
<img>
Ladefehler (Antwort >= 400).<script>
Ladefehler (Antwort >= 400).- globale Fehler, wenn Sie viele andere Bibliotheken in Ihrer Anwendung haben, die ebenfalls
window.onerror
auf eine unbekannte Art und Weise (Jquery, Angular usw.). - 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.
Versuchen Sie Atatus das fortschrittliches Error Tracking und Real User Monitoring für moderne Webanwendungen bietet.
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/ .
- See previous answers
- Weitere Antworten anzeigen