这是我参与「第五届青训营」伴学笔记创作活动的第 16 天
关于前端监控
什么是前端监控
采集“输入URL到页面显示的过程”和“用户后续交互”中产出的性能指标与发生的异常事件,并上报平台完成消费
为什么要前端监控
-
用户遇到的问题
- 打开慢
- 交互卡
- 资源加载失败
- 页面无法显示
-
没有监控,无法获得问题发生的原因,无法解决问题
-
有了监控,可以得知导致问题的地方,优化代码、优化体验
-
页面性能对业务非常重要,直接和用户数量关联
前端监控要监控什么
- 性能指标
- 异常事件
- 用户行为
概述:前端监控之常用性能指标
web性能标准的发展
- 早期网页是静态的,没有性能问题
- 随着网页变为动态,性能开始需要评测
- 传统性能指标:只关注技术细节,过程+耗时
- 以用户为中心的性能指标:因为优化加载速度不一定实现提升用户体验,所以需要用户为中心
以用户为中心的性能指标
- 发生了吗? FP(首次渲染时间),FCP(首次有内容的渲染时间)
- 内容有用吗? FMP(首次绘制有意义内容时间),SI(页面可视区域加载速度),LCP(最大的内容变得可见的时间点,容易理解计算上报)
- 内容可用吗? TTI(可靠响应用户交互时间),TBT(加载期间无法响应用户输入的时间,可以量化主线程的繁忙程度)
- 令人愉悦吗? FID(用户首次与页面交互到浏览器相应交互花费的时间),CLS(页面加载时元素偏移程度)
前端常见异常
静态资源错误
- 静态资源:html, css, js, 多媒体文件(图片,音频,视频)
- 静态资源错误:拉取资源的时候发生预期之外的错误,如网络异常,最后导致静态资源无法渲染
请求异常
- HTTP请求状态码大于等于400,属于请求异常,包括客户端错误和服务端错误
- fetch异步请求错误也属于请求异常
- 状态码为0的时候,表明请求被停止
JS错误
- 有些JS错误会影响页面的渲染和交互
- 一般在控制台可以看到错误信息
- 是前端监控的重点
白屏异常
-
页面上没有信息
-
没法通过浏览器监听到
-
可以通过判断DOM树的结构粗略判断
-
白屏归因
- JS错误导致关键资源加载失败
- 请求异常,静态资源加载失败
- 长时间JS线程繁忙阻塞任务
监控前端性能与异常
性能指标监控
原理
利用浏览器提供的Performance, PerformanceObserver,建议先参考标准文档
监听FP, FCP, LCP, FIP
- FP, FCP 通过 paint 监听
- LCP 通过 largest-contentful-paint 监听
- FIP 通过 first-input 监听
PerformanceObserver监听
- 新建Observer,接收参数list
- 启动Observer,用要监听的类型组成字符串数组,将数组传入observe函数
performance对象监听
- window.performance.getEntriesByType()函数
- 在渲染结束之后调用
- 对象的属性少于Observer
封装monitor
-
需求
- 起名字
- 监听能力
- 主动开启(而不是被动开启)
- 上报能力
-
写一个函数,将监听作为成员函数
-
返回名字和监听函数
-
高阶函数使用上报函数作为参数
-
在监听成员函数里面调用上报函数
JS错误监控
原理
根节点/window注册监听器,监听error event。unhandledrejection用来监听Promise的异常。
error event监听
- 在window上添加监听器
- 限制监听error event
- 传入处理函数,过滤错误(
e.error不为空一般都是JS错误,为空可能是别的错误)
unhandledrejection监听
- 在window上添加监听器
- 限制监听unhandledrejection监听
- 传入处理函数
封装monitor
和上一小节类似
静态资源错误监控
原理
静态资源错误也是error event,用监听器处理
监听
-
在window上添加监听器
-
限制监听error event
-
传入处理函数,过滤错误(
e.target或e.srcElement不为空的,是静态资源错误) -
进一步筛选,用
instanceof进行区分- 如HTMLElement就是link和script
- 要继续细分,可以用
target.tagName取出标签名进行判断
-
监听器第二个参数置为true
请求异常监控
原理
XHR和fetch函数,通过回调处理错误
XHR错误监听
- 写一个简易钩子函数,三个参数:对象、对象属性、自己的逻辑
- 返回一个函数,接收属性及参数
- 写要hook的方法:将自己的方法挂载到XHR的方法上
- 在open方法的钩子上,监听open的参数,然后恢复open执行
- 在send方法的钩子上,监听readystatechange,如果状态码status大于等于400就读取,当然也不能干扰原有函数的执行
封装通用SDK
- SDK主要执行“数据采集”、“组装上报”
- 服务主要执行“清洗存储”、“数据消费”
实现sdk
- 创建函数作为入口,传入参数是上传url
- 使用一个数组存储所有的monitor函数
- 创建sdk对象,存储sdk自己的属性
- 实现report函数,使用navigator.sendBeacon即可
- 创建加载monitor的函数,将小的monitor加入数组,返回sdk可以支持链式调用风格
- 创建启动monitor的函数,对数组中的所有元素全部传入
让sdk更健壮
- 监听慢请求
- 给hook函数增加unhook能力
- 用户行为监控