JavaScript全局事件机制


350

我想捕获所有引发的未定义函数错误。JavaScript中是否有全局错误处理工具?用例是捕获来自未定义的Flash的函数调用。


一旦发现错误,您想如何处理?您是否只需要记录它以便可以创建缺少的函数,还是希望阻止异常破坏代码?
丹·赫伯特

2
我想获取被调用的缺少函数的名称,并基于一些字符串调用我自己的函数。例如,对带有字符串“ close”的函数的任何调用都会调用我的close()。我也想在那时捕获错误。

2
exceptionsjs.com提供了此功能,可以利用其“防护”功能来仅捕获与未定义功能相关的错误。
史蒂芬·韦克斯勒

Answers:


192

这对您有帮助吗?

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

xxx();
</script>

我不确定它如何处理Flash错误...

更新:它在Opera中不起作用,但是我现在正在黑客蜻蜓,以查看它能得到什么。关于黑客蜻蜓的建议来自以下问题:

模拟窗口。使用JavaScript在Opera中出错


3
加上msg,file_loc和line_no参数,这应该对我有用。谢谢!

1
我是否正确理解此代码将覆盖任何现有的错误处理程序?
火星罗伯逊

@MarsRobertson号
-atilkan,

@atilkan window.onerror = function() { alert(42) };现在的答案代码:window.onerror = function() { alert("Error caught"); };不重写,我仍然不确定..
火星罗伯逊

@MarsRobertson我明白。它可能会覆盖。是的,确实如此,只是经过测试。使用addEventListener会更好。
atilkan

646

如何捕获未处理的Javascript错误

window.onerror事件分配给事件处理程序,例如:

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

正如评论中的代码,如果返回值window.onerrortrue那么浏览器应该抑制显示一个警告对话框。

window.onerror事件何时触发?

简而言之,当1.)存在未捕获的异常或2.)发生编译时错误时,将引发事件。

未捕获的异常

  • 抛出“一些消息”
  • call_something_undefined();
  • cross_origin_iframe.contentWindow.document ;,一个安全异常

编译错误

  • <script>{</script>
  • <script>for(;)</script>
  • <script>"oops</script>
  • setTimeout("{", 10);,它将尝试将第一个参数编译为脚本

支持window.onerror的浏览器

  • 铬13+
  • Firefox 6.0以上
  • Internet Explorer 5.5以上
  • Opera 11.60+
  • Safari 5.1以上版本

屏幕截图:

将上面的onerror代码添加到测试页后的示例:

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

Javascript警报显示window.onerror事件详细说明的错误信息

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/

参考文献:


3
值得一提的是,firefox throw手动制作时不会返回错误消息。stackoverflow.com/questions/15036165/…–
Sebas

2
好答案。您可以使用像JSNLog这样的包来实现“通过ajax报告此错误”,该包将为您执行ajax和服务器端日志记录。
user1147862 2013年

4
除了此答案外,我还添加了err对象,以便获得堆栈跟踪。stackoverflow.com/a/20972210/511438。现在,我可以获得更多反馈,因为我的开发错误在页面顶部显示为方框(如创建时一样)。
Valamas 2014年

将您的onerror实现打包在try-catch(ignore)块中,防止您的onerror()抛出更多错误不是更好吗?(不是这种情况,但可以肯定)
Pieter De Bie

1
@ super1ha1您能提供一个jsfiddle吗?我认为浏览器不会在onerror事件处理程序停止触发事件的地方引入重大变化。您可以尝试使用此jsfiddle来查看onerror处理程序的工作情况: jsfiddle.net/nzfvm44d 在Chrome版本62.0.3202.94(官方内部版本)(64位)中,这仍然对我有用。
山姆

38

复杂的错误处理

如果您的错误处理非常复杂,因此可能会引发错误,则添加标记以指示您是否已经处于“ errorHandling-Mode”是很有用的。像这样:

var appIsHandlingError = false;

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

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

否则,您可能会陷入无限循环。


29
或更安全的方法是在该handleError方法周围使用try-catch 。
Aidiakapi'4

8
如果错误处理中有异步调用,则不能使用try catch。所以他的解决方案仍然是一个很好的解决方案
Emrys Myrooin

@EmrysMyrooin仍然会通过错误处理导致同步或异步循环,并且可能在没有可用堆栈信息的情况下崩溃。
inf3rno

1
我认为应该在某个时候重置标志,对吗?我相信我们需要在if如果很大的情况下进行尝试/捕获,并确保在离开onerror方法之前就将标志重置。否则,将仅处理一个错误。
勒布

23

试试Atatus,它为现代Web应用程序提供了高级错误跟踪和真实用户监视。

https://www.atatus.com/

让我解释一下如何获取所有浏览器中相当完整的堆栈跟踪。

JavaScript中的错误处理

现代Chrome和Opera完全支持ErrorEvent和的HTML 5规范草案window.onerror。在这两种浏览器中,您都可以使用window.onerror或正确绑定到'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
    // 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);
})

不幸的是,Firefox,Safari和IE仍然存在,我们也必须支持它们。由于stacktrace在window.onerror我们需要做更多的工作。

事实证明,从错误中获取堆栈跟踪的唯一方法是将所有代码包装在一个try{ }catch(e){ }块中,然后查看e.stack。我们可以使用称为wrap的函数使该过程更容易些,该函数接受一个函数并返回具有良好错误处理能力的新函数。

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

这可行。您手动包装的任何函数都将具有良好的错误处理能力,但事实证明,在大多数情况下,我们实际上可以为您自动完成此功能。

通过更改的全局定义addEventListener,使其自动包装回调,我们可以自动try{ }catch(e){ }在大多数代码中插入。这使现有代码可以继续工作,但是增加了高质量的异常跟踪。

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

我们还需要确保它removeEventListener继续工作。目前,这不是因为to的参数addEventListener已更改。同样,我们只需要在prototype对象上修复此问题:

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

将错误数据传输到您的后端

您可以使用以下图像标签发送错误数据:

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

免责声明:我是https://www.atatus.com/上的Web开发人员。


什么是yourserver.com/jserrorREST,Web服务,Wcf服务?关于后端简单吗?
Kiquenet

如果要将js错误从用户浏览器发送到服务器。您必须编写您的backend(http://yourserver.com)来接收和存储。如果您选择atatus.com,则无需执行任何操作。只需在页面中包含两行脚本即可。
费扎尔汗2015年

18

似乎window.onerror不能提供对所有可能错误的访问。具体来说,它将忽略:

  1. <img> 加载错误(响应> = 400)。
  2. <script> 加载错误(响应> = 400)。
  3. 如果您的应用程序中还有许多其他库,则会发生全局错误 window.onerror以未知方式(jQuery,Angular等)进行,则会。
  4. 在探究了这一点之后,可能很多情况我都没有遇到过(iframe,堆栈溢出等)。

这是捕获许多这些错误的脚本的开始,因此您可以在开发过程中为应用程序添加更强大的调试功能。

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

})();

可以这样使用:

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

完整脚本具有默认实现,该默认实现尝试打印出它接收到的实体/错误的半可读“显示”版本。可用于启发特定于应用程序的错误处理程序。默认实现还保留了对最近100个错误实体的引用,因此您可以在出现以下情况后在Web控制台中对其进行检查:

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

注意:这可以通过覆盖多个浏览器/本机构造函数上的方法来工作。这可能会产生意想不到的副作用。但是,在开发过程中使用它,找出错误发生的位置,在开发过程中将日志发送到诸如NewRelic或Sentry之类的服务非常有用,这样我们就可以在开发过程中评估错误,并在登台时可以调试发生的情况更深层次。然后可以在生产中将其关闭。

希望这可以帮助。


1
显然,图像会触发错误事件: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

一个人也应该保留以前关联的onerror回调

<script type="text/javascript">

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

</script>

3

如果您想要统一的方式来处理未捕获的错误和未处理的Promise拒绝,则可以查看未捕获的库

编辑

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

它监听窗口。除了window.onerror之外,还有unhandledrejection


2
兰斯·波拉德(Lance Pollard)在上述答案中提到了一些错误,即普通的onerror无法处理。图书馆会处理吗?如果其中有一些,请具体说明?
Michael Freidgeim

@ MiFreidgeimSO-stopbeingevil刚刚检查了源代码-不幸的是,它没有。Aleksandr Oleynikov:如果您能支持,那就
太好了

2

我建议尝试一下Trackjs

作为服务记录错误。

设置非常简单。只需在每一页上添加<script>行即可。这也意味着,如果您决定不喜欢它,则将其删除非常简单。

还有其他服务,例如Sentry(如果您可以托管自己的服务器,则是开源的),但它不能完成Trackjs的工作。Track.js记录用户在其浏览器和Web服务器之间的交互,以便您可以实际跟踪导致错误的用户步骤,而不仅仅是文件和行号引用(可能是堆栈跟踪)。


2
尽管有免费试用版,但TrackJS 似乎不再有免费层
2015年

这对于跟踪并警告错误是很好的,但并不能真正解决处理部分。理想情况下,我认为询问者正在寻找一种处理这些错误的方法,以便其余代码仍然可以运行。
wgp

如果您捕获到错误事件并使用堆栈跟踪和应用程序状态向记录器添加xhr调用怎么办?trackjs更好吗?
inf3rno

2
@ inf3rno不仅仅是堆栈跟踪,它只是跟踪错误的开始。TrackJS将跟踪整个用户会话,因此您可以查看导致该会话的原因。这是一个屏幕截图,例如,trackjs.com / assets / images / screenshot.png
kane,

1

通过将函数分配给window.onerror来监听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; 
  };
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.