您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
前端监控之性能与异常
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
前端监控之性能与异常
自猿其说Tech
2022-06-13
IP归属:未知
24120浏览
前端
### 1 前言 现有的大部分监控方案都是针对服务端的,而针对前端的监控很少,诸如线上页面的白屏时间是多少、静态资源的加载情况如何、接口请求耗时好久、什么时候挂掉了、为什么挂掉,这些都不清楚。 同时,在产品推广过程中,经常需要统计页面的使用情况及用户行为,从而可以从运营和产品的角度去了解用户群体,进而迭代升级产品,使其更加贴近用户,为业务的扩展提供更多可能性。 因而,我们需要一个前端的页面监控系统,持续监控和预警页面性能的状况,并且在发现瓶颈时用于指导优化工作。 ### 2 前端监控目标 前端监控主要包含两大块:性能监控及异常监控 1. 保证稳定性(异常监控) 错误监控包括 JavaScript 代码错误,Promsie 错误,接口(XHR,fetch)错误,资源加载错误(script,link等)等,这些错误大多会导致页面功能异常甚至白屏。 1. 提升用户体验(性能监控) 性能监控包括页面的加载时间,接口响应时间等,侧面反应了用户体验的好坏。 ### 3 性能监控 #### 3.1 简单描述页面加载 简单看一下,从输入url到页面加载完成的过程如下: 首先需要通过 DNS(域名解析系统)将 URL 解析为对应的 IP 地址,然后与这个 IP 地址确定的那台服务器建立起 TCP 网络连接,随后我们向服务端抛出 HTTP 请求,服务端处理完我们的请求之后,把目标数据放在 HTTP 响应里返回给客户端,拿到响应数据的浏览器就可以开始走一个渲染的流程。渲染完毕,页面便呈现给了用户。 ![](//img1.jcloudcs.com/developer.jdcloud.com/6385a7cd-c90f-4bfd-a7a8-81c2b47e022820220613155705.png) 我们可以将这个过程分为如下的过程片段: 1. DNS 解析 2. TCP 连接 3. HTTP 请求抛出 4. 服务端处理请求,HTTP 响应返回 5. 浏览器拿到响应数据,解析响应内容,把解析的结果展示给用户 #### 3.2 从开发者角度,看页面加载各阶段 从输入url到用户可以使用页面的全过程时间统计,会返回一个PerformanceTiming对象,单位均为毫秒。 关于performace,已经在《从前端角度浅谈性能》中进行过介绍,,下面再强调一下: 各阶段的性能耗时可以通过API:window.performance来获取,对应的具体方法有:performance.timing、performance.getEntriesByType('resource')、performance.navigation等。 如上,开发者可以通过performance中各阶段的时间戳,分别获取到 页面各阶段的性能指标,具体的个静态资源的加载耗时、及 页面是否重定向和重定向耗时。 ![](//img1.jcloudcs.com/developer.jdcloud.com/bffb88fd-405f-43b2-a252-a0edcc8d49e020220613155734.png) **按触发顺序排列所有属性:** - navigationStart:在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等 - redirectStart:第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 - unloadEventStart:前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0 - redirectEnd:最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内的重定向才算,否则值为 0 - unloadEventEnd:和 unloadEventStart 相对应,返回前一个网页 unload 事件绑定的回调函数执行完毕的时间戳 - fetchStart:浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前 - domainLookupStart:DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 - domainLookupEnd:DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 - connectStart:HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 - secureConnectionStart:HTTPS 连接开始的时间,如果不是安全连接,则值为 0 - connectEnd:HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等,如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间 - requestStart:HTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存,连接错误重连时,这里显示的也是新建立连接的时间 - responseStart:HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 - responseEnd:HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 - domLoading:开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件 - domInteractive:完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件 - domContentLoadedEventStart:DOM 解析完成后,网页内资源加载开始的时间,文档发生 DOMContentLoaded事件的时间 - domContentLoadedEventEnd:DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕),文档的DOMContentLoaded 事件的结束时间 - domComplete:DOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件 - loadEventStart:load 事件发送给文档,也即 load 回调函数开始执行的时间,如果没有绑定 load 事件,值为 0 - loadEventEnd:load 事件的回调函数执行完毕的时间,如果没有绑定 load 事件,值为 0 #### 3.3 各阶段性能的计算(自定义) ```javascript const { timing, navigation } = window.performance const loadPageInfo = {}; // 页面加载类型,区分第一次load还是reload, 0初次加载、1重加载 loadPageInfo.loadType = navigation.type; // 页面加载完成的时间 - 几乎代表了用户等待页面白屏的时间 loadPageInfo.loadPage = timing.loadEventEnd - timing.navigationStart; // 重定向的时间 loadPageInfo.redirect = timing.redirectEnd - timing.redirectStart; // 卸载页面的时间 loadPageInfo.unloadEvent = timing.unloadEventEnd - timing.unloadEventStart; // 查询 DNS 本地缓存的时间 loadPageInfo.appCache = timing.domainLookupStart - timing.fetchStart; // 【重要】DNS 查询时间 // 页面内是不是使用了太多不同的域名,导致域名查询的时间太长?推荐 DNS 预加载。 // 可使用 HTML5 Prefetch 预查询 DNS loadPageInfo.lookupDomain = timing.domainLookupEnd - timing.domainLookupStart; // HTTP(TCP)建立连接完成握手的时间 loadPageInfo.connect = timing.connectEnd - timing.connectStart; // 【重要】HTTP请求及获取 文档内容的时间 loadPageInfo.request = timing.responseEnd - timing.responseStart; // 【重要】前一个页面 unload 到 HTTP获取到 页面第一个字节的时间 // 【原因】这可以理解为用户拿到你的资源占用的时间,推荐 加异地机房,加 CDN 处理,加宽带,加 CPU 运算速度 // TTFB 即 Time To First Byte loadPageInfo.ttfb = timing.responseStart - timing.navigationStart; // 解析 DOM 树结构的时间 loadPageInfo.domReady = timing.domComplete - timing.responseEnd; // 【重要】执行 onload 回调函数的时间 // 【原因】是否太多不必要的操作都放在 onload 回调函数里执行了,推荐 延迟加载、按需加载的策略 loadPageInfo.loadEvent = timing.loadEventEnd - timing.loadE ``` ### 4 异常监控 前端需要监控的错误主要有两类: 1. Javascript错误(js错误、promise错误) 2. 监听error错误(资源加载错误) #### 4.1 console.error ```javascript // 重写console.error,可以捕获更全面的报错信息 var oldError = console.error; console.error = function(tempErrorMsg){ var errorMsg = ( arguments[0] && arguments[0].message ) || tempErrorMsg; var lineNumber = 0; var columnNumber = 0; var errorStack = arguments[0] && arguments[0].stack; if( !errorStack ){ saveJSError( 'console_error', errorMsg, '', lineNumber, columnNumber, 'CustomizeError: ' + errorMsg ); }else{ saveJSError( 'console_error', errorMsg, '', lineNumber, columnNumber, errorStack ); } return oldError.apply( console, arguments ); ``` #### 4.2 error事件 通过对error事件的监听,可以捕捉到 js语法 及 资源加载 的错误。根据 event.target.src / href 来判断是否为资源加载错误。 ```javascript window.addEventListener( 'error', function(e){ var errorMsg = e.error && e.error.message, errorStack = e.error && e.error.stack, pageUrl = e.filename, lineNumber = e.lineno, columnNumber = e.colno; saveJSError( 'on_error', errorMsg, pageUrl, lineNumber, columnNumber, errorStack ); } ); ``` #### 4.3 Promise ```javascript // 捕获未处理的Promise错误 window.onunhandledrejection = function(e){ var errorMsg = ''; var errorStack = ''; if( typeof e.reason === 'object' ){ errorMsg = e.reason.message; errorStack = e.reason.stack; }else{ errorMsg = e.reason; errorStack = ''; } saveJSError( 'on_error', errorMsg, '', 0, 0, 'UncaughtInPromiseError: ' + errorStack ); } ``` ### 5 总结 以上,通过简单的js代码,即可实现对页面性能与异常的监控与数据上报,后续还需要相应具体的平台汇总,及相应的业务所需数据(如PV、UV等)的计算,才能真正实现对产品的页面数据呈现,用于业务扩展及宣导。 ### 6 后续 上述代码,实现了对页面性能及异常的监控,但其实前端的监控还包括了请求接口的监控与埋点的实现,后续将陆续推出,敬请期待。 ------------ ###### 自猿其说Tech-JDL京东物流技术与数据智能部 ###### 作者:李菲菲
原创文章,需联系作者,授权转载
上一篇:MYSQL中JSON类型介绍
下一篇:SPI扩展点的使用及原理分析
相关文章
前端十年回顾 | 漫画前端的前世今生
Taro小程序跨端开发入门实战
【技术干货】企业级扫描平台EOS关于JS扫描落地与实践!
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149963
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号