前端异常监控 - BadJS

前端页面的问题我们是怎么发现的?

  • 测试验证
  • 同事提出
  • 用户投诉

上报组件

前端页面错误怎么捕获?





        // onerror
        window.onerror = function(msg, url, row, col, error) {
            // todo report
            report({
                msg: msg,
                url: url,
                row: row,
                col: col
            });
        };
        // onerror
        window.onerror = function(msg, url, row, col, error) {
            // get error stack
            // Firefox and chrome support stack trace
            var newMsg = error && error.stack ? _processStackMsg(error) : msg;
            // todo report
            report({
                msg: newMsg,
                url: url,
                row: row,
                col: col
            });
        };

        // process error stack
        var _processStackMsg = function(error) {
            // error stack
            var stack = error.stack.replace(/\n/gi, "")
                .split(/\bat\b/).slice(0, 5).join("@").replace(/\?[^:]+/gi, "");
            // error message
            var msg = error.toString();
            return stack.indexOf(msg) < 0 ? (msg + "@" + stack) : stack;
        };

什么是 Script error ?

出现 “Script error” 的场景和条件:

1、通过 window.onerror 注册监听脚本错误事件

2、浏览器:Firefox、Chrome、Safari、IE7+(*)

3、页面内使用了 script 标签引入,非同域的资源, 且发生脚本错误。

 举例:

          http://ke.qq.com/index.html 里引用了

          http://7.url.cn/index.js

本质上是出于安全考虑。因为 script 标签引入文件内容的时候是忽略文件本身的MIME声明,且是允许跨域请求的。这里如果不屏蔽掉跨域情况下的错误信息,很可能会给黑客提供一个攻击通道。

http://www.webryan.net/2012/12/something-about-window-onerror-script-error/

        // http://trac.webkit.org/browser/trunk/Source/WebCore/dom/ScriptExecutionContext.cpp#L347
        bool ScriptExecutionContext::sanitizeScriptError(
            String& errorMessage,
            int& lineNumber,
            int& columnNumber,
            String& sourceURL,
            CachedScript* cachedScript
        )
        {
            URL targetURL = completeURL(sourceURL);
            if (securityOrigin()->canRequest(targetURL) ||
                (cachedScript && 
                    cachedScript->passesAccessControlCheck(*securityOrigin())
                )
            )
                return false;
            errorMessage = "Script error.";
            sourceURL = String();
            lineNumber = 0;
            columnNumber = 0;
            return true;
        }

怎么解决 Script error ?

在脚本文件的 HTTP response header 中设置 CORS

比如: Access-Control-Allow-Origin: http://ke.qq.com

在页面的 script 标签中设置 crossorigin 属性

比如: <script src="http://7.url.cn/index.js" crossorigin></script>

CORS header 和 crossorigin 取值问题

anonymous(默认)

CORS 不等于 origin, 不能带 cookie

CORS header 和 crossorigin 取值问题

use-credentials

CORS 不能设置为 *,能带 cookie

CORS 不等于 origin,js 直接不加载

Access-Control-Allow-Credentials: true

通过 XHR 获取脚本资源, 通过 script 标签 innerText 插入到页面

会丢失错误的行号列号和文件名等信息

资源加载切换到主域

会失去使用 CDN 的优势, 适合在排查问题的时候灰度一部分用户使用

使用 try catch

  1. 每个方法都包裹上 try catch
  2. 对主要入口方法进行try catch

只需要对异步模块和业务主入口添加 try catch

  1. setTimeout 和 setInterval 
  2. 事件绑定 
  3. ajax callback
  4. define 和 require
  5. 业务主入口

组件内捕获到异常怎么处理

  • 直接吃掉
  • 向上抛

还有

  • 合并上报(延迟)
  • 日志级别(1-debug 2-info 4-error 8-fail)
  • 抽样上报
  • new Image VS XHR
  • 200 VS 204
  • ignore

项目中怎么接入

        <!-- 2.56 KB -->
        <script src="/dist/bj-report.min.js"></script>
        <!-- 或者 4.48 KB -->
        <script src="/dist/bj-report-tryjs.min.js"></script>


            BJ_REPORT.init({
                id: 1 // 填写在管理后台申请的上报 id
            });
            BJ_REPORT.spyAll();

上报后台

  • nodejs
  • ZeroMQ
  • mongodb
  • mysql

管理后台

邮件报表

最佳实践

  1. 上报组件在业务代码前加载执行
  2. 监控页面所有请求
  3. 监控业务关键点

接下来要做的

  1. sourcemap 支持
  2. 项目文档完善
  3. https 支持
  4. 简化部署
  5. ...

Q&A

Thanks