博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Capture and report JavaScript errors with window.onerror
阅读量:7059 次
发布时间:2019-06-28

本文共 8383 字,大约阅读时间需要 27 分钟。

原文:

onerror is a special browser event that fires whenever an uncaught JavaScript error has been thrown. It’s one of the easiest ways to log client-side errors and report them to your servers. It’s also one of the major mechanisms by which  (raven-js) works.

You listen to the onerror event by assigning a function to window.onerror:

window.onerror = function (msg, url, lineNo, columnNo, error) { // ... handle error ... return false; }

When an error is thrown, the following arguments are passed to the function:

  • msg – The message associated with the error, e.g. “Uncaught ReferenceError: foo is not defined”
  • url – The URL of the script or document associated with the error, e.g. “/dist/app.js”
  • lineNo – The line number (if available)
  • columnNo – The column number (if available)
  • error – The  associated with this error (if available)

The first four arguments tell you in which script, line, and column the error occurred. The final argument, Error object, is perhaps the most valuable. Let’s learn why.

The Error object and error.stack

At first glance the  isn’t very special. It contains 3 standardized properties: messagefileName, and lineNumber. Redundant values that already provided to you via window.onerror.

The valuable part is a non-standard property: Error.prototype.stack. This stack property tells you at what source location each frame of the program was when the error occurred. The stack trace can be a critical part of debugging an error. And despite being non-standard, this property is available in every modern browser.

Here’s an example of the Error object’s stack property in Chrome 46:

"Error: foobar\n    at new bar (
:241:11)\n at foo (
:245:5)\n at
:250:5\n at
:251:3\n at
:267:4\n at callFunction (
:229:33)\n at
:239:23\n at
:240:3\n at Object.InjectedScript._evaluateOn (
:875:140)\n at Object.InjectedScript._evaluateAndWrap (
:808:34)"

Hard to read, right? The stack property is actually just an unformatted string.

Here’s what it looks like formatted:

Error: foobar    at new bar (
:241:11) at foo (
:245:5) at callFunction (
:229:33) at Object.InjectedScript._evaluateOn (
:875:140) at Object.InjectedScript._evaluateAndWrap (
:808:34)

Once it’s been formatted, it’s easy to see how the stack property can be critical in helping to debug an error.

There’s just one snag: , and its implementation differs among browsers. For example, here’s the same stack trace from Internet Explorer 11:

Error: foobar   at bar (Unknown script code:2:5)   at foo (Unknown script code:6:5)   at Anonymous function (Unknown script code:11:5)   at Anonymous function (Unknown script code:10:2)   at Anonymous function (Unknown script code:1:73)

Not only is the format of each frame different, the frames also have less detail. For example, Chrome identifies that the new keyword has been used, and has greater insight into eval invocations. And this is just IE 11 vs Chrome – other browsers similar have varying formats and detail.

Luckily, there are tools out there that normalize stack properties so that it is consistent across browsers. For example, raven-js uses  to normalize error strings. There’s also  and a few other projects.

Browser compatibility

window.onerror has been available in browsers for some time – you’ll find it in browsers as old as IE6 and Firefox 2.

The problem is that every browser implements window.onerror differently. Particularly, in how many arguments are sent to to the onerror listener, and the structure of those arguments.

Here’s a table of which arguments are passed to onerror in most browsers:

Browser Message URL lineNo colNo errorObj
Firefox 42
Chrome 46
Android Browser 4.4  
Edge  
IE 11
IE 10  
IE 9, 8    
Safari 9  
iOS 9  

You’ll notice that the latest Apple browsers – Safari and iOS – don’t support a 5th error object argument. And while the final version of Internet Explorer (11) supports the error object, Microsoft’s latest browser, Edge, .

Without the error object, there is no stack trace property. This means that these browsers cannot retrieve valuable stack information from errors caught by onerror.

Polyfilling window.onerror with try/catch

But there is a workaround – you can wrap code in your application inside a try/catch and catch the error yourself. This error object will contain our coveted stack property in every modern browser.

Consider the following helper method, invoke, which calls a function on an object with an array of arguments:

function invoke(obj, method, args) { return obj[method].apply(this, args); } invoke(Math, 'max', [1, 2]); // returns 2

Here’s invoke again, this time wrapped in try/catch, in order to capture any thrown error:

function invoke(obj, method, args) { try { return obj[method].apply(this, args); } catch (e) { captureError(e); // report the error throw e; // re-throw the error } } invoke(Math, 'highest', [1, 2]); // throws error, no method Math.highest

Of course, doing this manually everywhere is pretty cumbersome. You can make it easier by creating a generic wrapper utility function:

function wrapErrors(fn) { // don't wrap function more than once if (!fn.__wrapped__) { fn.__wrapped__ = function () { try { return fn.apply(this, arguments); } catch (e) { captureError(e); // report the error throw e; // re-throw the error } }; } return fn.__wrapped__; } var invoke = wrapErrors(function(obj, method, args) { return obj[method].apply(this, args); }); invoke(Math, 'highest', [1, 2]); // no method Math.highest

Because JavaScript is single threaded, you don’t need to use wrap everywhere – just at the beginning of every new stack.

That means you’ll need to wrap function declarations:

  • At the start of your application (e.g. in $(document).ready if you use jQuery)
  • In event handlers, e.g. addEventListener or $.fn.click
  • Timer-based callbacks, e.g. setTimeout or requestAnimationFrame

For example:

$(wrapErrors(function () { // application start doSynchronousStuff1(); // doesn't need to be wrapped setTimeout(wrapErrors(function () { doSynchronousStuff2(); // doesn't need to be wrapped }); $('.foo').click(wrapErrors(function () { doSynchronousStuff3(); // doesn't need to be wrapped }); }));

If that seems like a heck of a lot of work, don’t worry! Most error reporting libraries have  for augmenting built-in functions like addEventListener and setTimeout so that you don’t have to call a wrapping utility every time yourself. And yes, raven-js does this too.

Transmitting the error to your servers

Okay, so you’ve done your job – you’ve plugged into window.onerror, and you’re additionally wrapping functions in try/catch in order to catch as much error information as possible.

There’s just one last step: transmitting the error information to your servers. In order for this to work, you’ll need to set up some kind of reporting web service that will accept your error data over HTTP, log it to a file and/or store it in a database.

If this web service is on the same domain as your web application, this is achieved easily by using XMLHttpRequest. In the example below, we use jQuery’s AJAX function to transmit the data to our servers:

function captureError(ex) { var errorData = { name: ex.name, // e.g. ReferenceError message: ex.line, // e.g. x is undefined url: document.location.href, stack: ex.stack // stacktrace string; remember, different per-browser! }; $.post('/logger/js/', { data: errorData }); }

Note that if you have to transmit your error across different origins, your reporting endpoint will need to support CORS (Cross Origin Resource Sharing).

Summary

If you’ve made it this far, you now have all the tools you need to roll your own basic error reporting library and integrate it with your application:

  • How window.onerror works, and what browsers it supports
  • How to use try/catch to capture stack traces where window.onerror is lacking
  • Transmitting error data to your servers

Of course, if you don’t want to bother with all of this, there are plenty of commercial and open source tools that do all the heavy-lifting of client-side reporting for you. (Psst, you might want to try .)

That’s it! Happy error hunting.

转载地址:http://ckfll.baihongyu.com/

你可能感兴趣的文章
文本数据处理之awk
查看>>
JavaScript基础之String对象和RegExp对象
查看>>
Zabbix监控之Zabbix proxy
查看>>
我的友情链接
查看>>
修炼真经
查看>>
跟小博老师一起学Servlet ——Servlet之属性操作2
查看>>
自建Saltstack的repo软件源仓库
查看>>
Domino和Java技术杂烩
查看>>
Ext.class源码
查看>>
EXCHANGE 备忘
查看>>
Windows Server 2003应用宝典
查看>>
DAM2加密狗克隆的具体解决方案
查看>>
教你深入系统的学习linux系统
查看>>
前台向后台隐藏传参数
查看>>
Oracle10g手工创建数据库
查看>>
JS下载文件
查看>>
Nginx 模块常用命令介绍
查看>>
thinkphp5.0框架swoole的使用
查看>>
继上一篇SQL练习题,给出答案
查看>>
慕课网-Java从零打造企业级电商项目实战_项目初始化_项目结构
查看>>