report javascript error to server

当JavaScript运行时错误(包括语法错误)发生时,window会触发一个ErrorEvent接口的error事件,并执行window.onerror()ErrorEvent接口的属性和方法可以参考下面截图。

当一项资源(如<img><script>)加载失败,加载资源的元素会触发一个Event接口的error事件,并执行该元素上的onerror()处理函数。这些error事件不会向上冒泡到window,不过在部分浏览器如Firefox中能被window.addEventListener捕获一次。

加载一个全局的error事件处理函数可用于自动收集错误报告。

由于历史原因,window.onerrorelement.onerror接受不同的参数。

window.onerror = function(message, source, lineno, colno, error) { ... }
element.onerror = function(event) { ... }

JavaScript因为手机浏览器兼容性的问题,或者用户数据和状态问题导致异常,客户端运行时报错,服务器端有时没有任何错误信息,对bug查找和修复非常不方便,所以将客户端的JavaScript运行异常捕获并上传到后台服务器日志中,并且将错误和用户的会话关联上,可以明确知道哪个用户的手机上抛出了什么错误,便于问题排查。

测试 window.onerror 捕获异常

客户端javascript的异常可以统一由window.onerror进行捕获,并且标准草案定义的也是window.onerror,一般不用window.addEventListenerwindow.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.onerrorwindow.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=链接上报异常信息,服务器中记录异常信息到日志文件中,并重定向到一张图片地址即可。

@Controller@RequestMapping("/js/error")public class JavascriptErrorRecordController {    private final Logger logger = LoggerFactory.getLogger(getClass());    @RequestMapping("/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问题处理

  1. 在脚本引用标签中必须设置crossorigin属性。
  2. 同时服务器端需要设置Access-Control-Allow-Origin的响应头。

以上二者需要同时满足,不然都会扔出Script error.这个提示信息,关于CORS问题更详细的说明可以参考下面几个链接。

References

  1. GlobalEventHandlers.onerror
  2. 前端代码异常日志收集与监控
  3. 如何捕获和分析 JavaScript Error
  4. JavaScript 中的异常处理