大型 Web 项 目可 用性提升 零脚本错误的实战 郭林林烁 2017.10
郭林林烁 (joeyguo) @ 腾讯 AlloyTeam
1 社区的相关提问
错误信息分析与优化 如何发现代码出了了问题? 开发测试与脚本错误 Web 安全与脚本错误 基础的监控体系组成
1 如何发现线上代码出了了问题?
1 不不可能有问题! 我的代码不不可能有问题!
2 不不可能不不可能不不可能
3 测试 / 用户反馈 有问题 有问题 有问题 有问题 有问题 有问题 这 里里有问题 有问题 有问题
4 遇 见问题 这 页 面怎么打不不开啊? 我是 老老板 前端? 后台? 能否复现? 前端 后台 测试
xx 手机, 能够复现了了 原来是前端兼容问题, 难怪浏览器器是正常的 老老板, 问题修复了了 我是 老老板 修复速度太慢了了!
及时发现 方便便解决总结沉淀 打造线上监控系统, 及时发现问题, 解决问题
2 监控系统 监控 上报 信息收集
1 偷听 ( 监控 ) 系统基本组成 偷听监控 报警上报 现场还原数据呈现 警局信息收集
2 监控系统基本组成 1 代码监控 2 数据上报 3 数据收集系统
3 监控 方式 监控的 方式主要有两种 try-catch 预料料之内的错误 window.onerror 预料料之外的错误 并 非 非此即彼, 可结合使 用 try-catch 异步错误 无法捕获
4 上报 方式 通过 Ajax 发送数据 动态创建 img 标签的形式
5 信息收集监控系统 提供上报接 口 存储上报数据 数据分析展示
3 错误信息分析与优化
1 错误信息分析 Script error.
2 产 生 Script error. 的原因 浏览器器安全策略略, 跨域报错信息 无权限获得 http://trac.webkit.org/browser/trunk/source/webcore/dom/scriptexecutioncontext.cpp
3 优化 Script error. 同源处理理 Inline 内联代码 外链同域名 外链灰度同域 ( 20% )
4 优化 Script error. 利利 用跨源资源共享机制 (CORS) script 标签添加 crossorigin 属性 响应头增加 Access-Control-Allow-Origin 响应头中需带上 Vary:Origin http://a.com http://a.com http://a.com/index.html <script src= //qq.com/main.js" crossorigin></script>
5 Vary:Origin 的作 用 Vary 为缓存服务器器提供缓存规则及缓存筛选的依据 Vary:Origin 表示在缓存筛选时, 将结合请求的 Origin 进 行行区分 www.qq.com/main.js http://a.com http://a.com www.qq.com/main.js http://b.com http://a.com
进 行行脚本错误分析
6 错误信息分析 代码压缩后, 定错出错代码困难
让脚本错误 一 目了了然
1 让脚本错误 一 目了了然 不不压缩代码 代码 大 小变 大很多 源代码泄露露
2 让脚本错误 一 目了了然 半压缩 分号换空格 / 保留留空格换 行行 通过特征值快速找到报错代码 代码 大 小相对压缩则仍有所变 大
3 让脚本错误 一 目了了然 使 用 SourceMap 快速定位 SourceMap 维护源 文件盒处理理后 文件的映射关系 使 用 VLQ 编码来存储映射 源 文件 生成 文件 SourceMap 文件
4 让脚本错误 一 目了了然 利利 用 SourceMap 结合 生成 文件的 行行列列定位到源 文件位置 SourceMap 文件 源 文件路路径 处理理 源 文件 行行列列数 生成 文件 行行列列数 原变量量名!function(n){function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:! 1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={};r.m=n,r.c=t,r.i=function(n){return n},r.d=function(n,t,e){r.o(n,t) Object.defineProperty(n,t,{configurable:!1,enumerable:! 0,get:e})},r.n=function(n){var t=n&&n. esmodule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,r) {return Object.prototype.hasOwnProperty.call(n,r)},r.p="",r(r.s=0)} ([function(n,r){function t(){noerror}t()}]); sourcemap + 行行列列数 function init() { noerror; // } 需要 支持 sourcemap 生成 不不会增加线上代码 大 小
5 基于 SourceMap 脚本报错监控系统 方案 SourceMap 统 一存储 脚本错误处理理平台 SouceMap 文件 3 处理理 错误信息展示平台 错误信息 源代码 1 2 错误上报 外 网环境 发布 文件
6 基于 SourceMap 脚本报错监 方案示例例 https://github.com/joeyguo/noerror
7 开源 方案 sentry 错误列列表 错误量量告警 项 目的团队管理理 指定修复负责 人 进展与记录 邮件通知 https://github.com/getsentry/sentry
8 发现代码不不存在的错误信息 这报错信息在代码中不不存在! 页 面被注 入了了别的代码?
4 Web 安全与脚本错误
1 监控 上报 监控 上报 非 白名单中的前端资源 监听 document 的 onload 事件, 对加载的 src 内容进 行行上报 只上报 非 白名单的资源 偷听监控
2 数据分析, 场景还原
CSP 内容安全策略略 白名单 可信任的内容来源 非 白名单 无法正常执 行行
1 CSP (Content Security Policy) CSP 使 用 方式 HTML Meta 标签 <meta http-equiv="content-security-policy" content="script-src 'self'"> HTTP Header ( 响应头带上 CSP 的指令 )
2 CSP 配置 两种策略略 上报 Content-Security-Policy-Report-Only 不不拦截 只上报 拦截 Content-Security-Policy 1 进 行行拦截 阻 止执 行行 多类参数
3 脚本错误量量越来越少 线上的脚本错误量量变少了了! 开发测试, 从 源头 减少错误
5 开发测试与脚本错误
1 测试客户端内嵌 页 面脚本报错 报错了了! 网络问题? 缓存导致? CGI 没返回? 发现不不及时定位困难
js-error-dialog 脚本错误弹窗组件 https://github.com/joeyguo/js-error-dialog
1 测试客户端内嵌 页 面脚本报错 报错 自动唤起 ( 及时 可视化 )?!
2 js-error-dialog 拷 贝错误信息 ( 易易传播 ) 弹出错误提示 点击拷 贝, 发送给我
3 js-error-dialog 错误信息还原 ( 可视化 ) 打开错误 页 面, 增加 jed 参数, 自动唤起输 入框 粘贴错误信息, 查看 生成报告
js-error-dialog 原理理分析
1 js-error-dialog 基础组成 现场分析 偷听监控 当场抓错误信息展示
2 js-error-dialog 信息 详细的错误信息 - 基础报错信息与 自定义信息 报错信息 U/A 客户端版本 - 查看压缩代码格式化的位置 精确地看到具体报错代码
3 js-error-dialog 实现核 心 将压缩代码格式化并找到对应位置!function(n){function r(e){if(t[e])return t[e].exports;var o=t[e]={i:e,l:! 1,exports:{}};return n[e].call(o.exports,o,o.exports,r),o.l=!0,o.exports}var t={};r.m=n,r.c=t,r.i=function(n){return n},r.d=function(n,t,e){r.o(n,t) Object.defineProperty(n,t,{configurable:!1,enumerable:! 0,get:e})},r.n=function(n){var t=n&&n. esmodule?function(){return n.default}:function(){return n};return r.d(t,"a",t),t},r.o=function(n,r) {return Object.prototype.hasOwnProperty.call(n,r)},r.p="",r(r.s=0)} ([function(n,r){function t(){noerror}t()}]);!function(n) { //... //... }([ function(n, r) { function t() { noerror; } t(); } ]);
4 将压缩代码格式化并找到对应位置 原理理分析 prettyjs 1 压缩 文件 ( 源 ) 格式化 文件 ( 生成 ) 资源体积过 大? SourceMap 文件 2 格式化 文件路路径 3 压缩 文件 行行列列数 处理理 文件 行行列列数 代码 高亮展示 原变量量名 https://github.com/joeyguo/prettyjs
5 js-error-dialog 执 行行流程 js-error-dialog 监控 入 口 代码格式化 sourcemap 代码 高亮 错误展示 动态拉取 prettyjs 报错更更容易易发现, 线上错误更更加少了了!
拓拓展 基于 prettyjs 脚本报错监控系统 方案
1 基于 prettyjs 脚本报错监控系统 方案 不不增加线上代码 大 小 脚本错误处理理平台 不不需要 支持 sourcemap 生成 通过特征值快速找到报错代码 错误信息 prettyjs 3 错误信息展示平台 2 错误上报 源代码 1 发布 文件 外 网环境
6 回顾
1 回顾 如何发现代码出了了问题? 监控体系基础组成 错误信息分析与优化 针对 Web 安全的脚本错误优化 针对开发测试的脚本错误优化
2 相关内容 脚本错误量量极致优化 - 监控上报与 Script error https://github.com/joeyguo/blog/issues/13 脚本错误量量极致优化 - 让脚本错误 一 目了了然 https://github.com/joeyguo/blog/issues/14 XSS 终结者 -CSP 理理论与实践 https://github.com/joeyguo/blog/issues/5 noerror https://github.com/joeyguo/noerror js-beautify-sourcemap https://github.com/joeyguo/js-beautify-sourcemap js-error-dialog https://github.com/joeyguo/js-error-dialog prettyjs https://github.com/joeyguo/prettyjs
THANK YOU