当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror(),ErrorEvent接口的属性和方法可以参考下面截图。
当一项资源(如<img>或<script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过在部分浏览器如Firefox中能被window.addEventListener捕获一次。
加载一个全局的error事件处理函数可用于自动收集错误报告。
由于历史原因,window.onerror和element.onerror接受不同的参数。
window.onerror = function(message, source, lineno, colno, error) { ... }
element.onerror = function(event) { ... }
JavaScript因为手机浏览器兼容性的问题,或者用户数据和状态问题导致异常,客户端运行时报错,服务器端有时没有任何错误信息,对bug查找和修复非常不方便,所以将客户端的JavaScript运行异常捕获并上传到后台服务器日志中,并且将错误和用户的会话关联上,可以明确知道哪个用户的手机上抛出了什么错误,便于问题排查。
测试 window.onerror 捕获异常
客户端javascript的异常可以统一由window.onerror进行捕获,并且标准草案定义的也是window.onerror,一般不用window.addEventListener或window.attachEvent的形式去监听window.onerror。
var fn = window.onerror = function () { console.log(arguments); // 函数中返回 return true,错误便不会暴露到控制台中。};// 多次注册 error 事件,不会重复执行多个回调// window.onerror 和 addEventListener 都执行了,并只执行了一次window.addEventListener("error", fn);window.addEventListener("error", fn);window.onerror = function (errorMessage, scriptURI, lineNumber, columnNumber, error) { console.log(arguments); if (error) { // 标准草案实现 console.log(error); } else { console.log({message: errorMessage, script: scriptURI, line: lineNumber, column: columnNumber}); }} |
如上代码注释说明,多次注册error事件,不会重复执行多个回调,window.onerror和window.addEventListener都执行了,并只执行了一次。可以访问此测试链接查看错误信息,源码截图如下:
错误截图如下:
后台应用接收异常并记录日志
需要注意上传的error对象需要经过encodeURIComponent()方法进行转义,因为url中包括了一些特殊字符如{}会导致tomcat抛出如下异常而不能正常完成请求并记录日志:
Note: further occurrences of HTTP header parsing errors will be logged at DEBUG level.
java.lang.IllegalArgumentException: Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
客户端JavaScript代码
如下代码一般放在一个全局通用的JavaScript文件中,页面中引入这个JavaScript文件。
window.onerror = function (errorMessage, scriptURI, lineNumber, columnNumber, error) { new Image().src = "/js/error/record?error=" + encodeURIComponent(JSON.stringify({ message: errorMessage, script: scriptURI, line: lineNumber, column: columnNumber }));} |
后台应用示例
下面后台应用以springmvc为例,客户端JavaScript通过/js/error/record?error=链接上报异常信息,服务器中记录异常信息到日志文件中,并重定向到一张图片地址即可。
("/js/error")public class JavascriptErrorRecordController { private final Logger logger = LoggerFactory.getLogger(getClass()); ("/record") public String record(String error) { // 服务器端日志中记录异常信息的json,并且关联到发生异常的用户上 logger.warn("javascript error for user : {}, and content : {}", session.getAttribute("userId"), error); // should be 1px png return "http://www.baidu.com/img/bd_logo1.png"; }} |
CORS问题
如果当前网页引用另一个域名的JavaScript文件中有脚本错误,当前域下只能看到Script error.这个提示信息,却并不能像访问本域的JavaScript脚本那样,会抛出错误的详细信息。
CORS问题处理
- 在脚本引用标签中必须设置
crossorigin属性。 - 同时服务器端需要设置
Access-Control-Allow-Origin的响应头。
以上二者需要同时满足,不然都会扔出Script error.这个提示信息,关于CORS问题更详细的说明可以参考下面几个链接。