共计 4451 个字符,预计需要花费 12 分钟才能阅读完成。
监控 SDK 的作用是收集客户端产生的日志,如何全面的收集日志是本篇探讨的重点。监控日志能够分为 3 类,异样日志、失常日志、性能日志,每次日志上报时还会上报 1 类通用的信息:
日志类型 | 二级分类 | 阐明 |
---|---|---|
异样日志 | JS 谬误、Server/Native 接口异样、资源下载异样 | 监控要害,需实时上报 |
失常日志 | 用户行为、log 信息、路由信息 | 日志量大,由用户被动上报 |
性能日志 | 首屏工夫、耗时明细、PV/UV | 上报一次,同时统计 PV/UV |
通用信息 | 我的项目标识、用户标识、环境信息 | 其余 3 类日志附加的通用信息 |
异样日志
JS 谬误是最常见的异样日志,但并不是所有异样都是 JS 谬误。接下来会介绍可能被大家疏忽的但对线上问题定位很重要的 三类异样的捕捉办法,包含
- Server 接口异样的捕捉办法
- Native 接口异样的捕捉办法
- 资源下载异样的捕捉办法
如何收集 Server 接口异样?
在第一篇中,咱们顺带介绍过通过 onerror
和 unhandledrejection
收集全局 JS 谬误。然而在咱们的理论业务中,其实是一些谬误或异样不会抛到全局,也就不会被 onerror
和 unhandledrejection
收集到。比方,Server 接口异样,就不会被抛到全局。
const serverAPI = '58.com/api'
const response = await fetch(serverAPI)
if(response.status < 400) {setState(() => ({data: response.json()}))
} else {
// 手动上报异样
errorReport({serverAPI, status: response.status})
}
else
这块代码,有些业务同学可能会不写。如果没有手动上报,这些 Server 异样就会被疏忽,然而要求所有业务同学将每个 Server 异样都手动上报,也又不事实。怎么办呢?在监控 SDK 中对立上报。
重写 fetch 办法
Server 接口异样的捕捉办法,就是重写 window.fetch
:
const originalFetch = window.fetch
window.fetch = function wrapperFetch(...args) {return originalFetch.apply(window, args).then((response) => {
// 主动上报异样
if (response.status >= 400) {errorReport({ serverApi: args[0], status: response.status })
}
return response;
})
}
应用相似的办法:
- 咱们也能够 wrapper XMLHttpRequest,捕捉 status >= 400 的状况。
- 捕捉 fetch 走到 catch 中的状况。
- 捕捉 fetch 超时的状况。
绕过 Hybrid SDK 与 Native 交互
咱们再往前想一步,调用 Server 接口异样不会被抛到全局,Native 接口异样也不会被抛到全局。Native 接口异样,也能够由监控 SDK 专门捕捉上传。思路和 wrapperFetch
办法思路相似,咱们能够 wrapperHybrid
。
这里先介绍一下业务怎么应用 Hybrid SDK 手动上报。接下来再说怎么主动上报。
const request = {action: 'getUser', params: 'useName'}
const response = await hybrid.action(request)
// 手动上报
if(response.code === 1) {errorReport({ ...request, code: response.code})
}
然而咱们有些老页面,一些业务自定义的交互协定,业务间接通过协定就和 Native 进行交互了,并没有应用通用的 Hybrid SDK。通过重写 Hybrid SDK 的形式就不失效了。怎么办呢?间接监听最底层的 JSBridge。
jsbridge('wbmain://hybrid/jsbridge?action=getUser&parmas=userName&callback=windowGetUser')
监听 JSBridge
JSBridge 包含申请和响应两个局部:
-
Web 申请 Native
- 申请形式有多种,这些形式是由 Native 和 Web 当时进行约定的,也根本不会改变,因而比下层接口更为稳固。
- 罕用办法一:Web 调用全局办法,Native 重写该办法进行拦挡。如重写拦挡
window.prompt()
、自定义全局函数。 - 罕用办法二:Web 通过 iframe 发送申请,Native 拦挡 iframe 申请。
-
Native 响应 Web
- Web 当时自定义全局函数,并把全局函数名通过
schema
的callback
参数传过来 - Native 调用该全局函数,Web 就能拿到 Native 响应的参数了
- Web 当时自定义全局函数,并把全局函数名通过
以 iframe 计划为例,实现 Native 接口异样的捕捉办法是监控 iframe src 的变动,如下:
// 创立监听
const observer = new MutationObserver((mutationList) => {mutationList.forEach((mutation) => {
// 获取申请 的 schema
const schema = mutation.target.src
// 获取当时定义的全局函数名
const callback = getURLSearchParams(schema,'callback');
// 通过 wrapper 拦挡响应,获取 native params,当 params 异样时上报
// params 异样,由标准决定,如 errorCode = 1
window[callback] = wrapperCallback(window[callback])
})
});
// 开始监听 iframe src 变动
observer.observe(iframe, {attributes: true,attributeFilter: ['src']});
应用监听的形式获取更多信息
另外 2 类异样的监听形式:
- 跨域报错(左):应用 CDN 托管 JS 资源,JS 和 Web url 不在同一个域,跨域 JS 的报错浏览器只会给出
script error
的提醒。script error
的提醒并不是真的报错信息,也没有谬误堆栈。通过重写监听函数,就能通过 try catch 捕捉这些函数的报错了 - 资源报错(右):加载资源报错不会被
window.onerror
捕捉到,然而 资源异样能够被window.addEventListener('error')
捕捉到。
性能收集
性能收集罕用的有两种计划,一种是业务自定义的性能规范,一种是业内通用的性能规范。
业务自定义的性能规范,须要入侵业务代码进行埋点收集,各个业务计划之间的都有一些差异,业务之间也不能横向比照,可用性较差。本篇重点探讨业内通用的性能规范,并 创新性地实现了 FP、LCP 指标在 RN 端的收集办法。
性能规范
另一种是 W3C 的性能规范,因为是浏览器的规范实现,能够不入侵业务代码,就能获取到页面加载耗时。W3C 的规范包含了 Navigation Timing 和 Navigation Timing Level 2,两份规范。第一份规范浏览器曾经反对很好了,第二份新规范支持性差一些,但规范之间差异不大,收集的时候做下向下兼容即可。
性能收集包含两块
- 耗时明细
- 白屏工夫
其中耗时明细的 Level 2 定义如下,所有耗时明细都须要收集:
白屏工夫统计
相比耗时明细,大家可能更关注白屏工夫,也就是页面整体渲染耗时。白屏工夫大家定义各不相同,这是因为大家业务场景不一样导致的,大抵能够分为两种定义。
- 一种是后端间接生成模板,白屏工夫的定义为 DOMContentLoaded(DCL) 或 Load(L) 这种老指标
- 一种是前端渲染页面,比方 React/Vue 为代表的 JS 生成页面,白屏工夫的定义为 FP、FCP、LCP 这类新指标
获取 FP、FCP、LCP 新指标的形式是 performance.getEntries()
,然而这些新指标并未纳入 W3C 标准,只是 Google 提出来的一种标准,目前兼容性比拟差,可用性咱们也在验证中。
FP (first paint)指的是第一个像素渲染到屏幕上所用的工夫,FCP(first contentful paint)指的是第一个文字、图片等渲染到屏幕上所用的工夫。在大多数场景下,二者根本相等。
LCP(largest contentful paint) 指的是显示面积最大的文字或图片渲染到屏幕上所用的工夫。Google 提出的以 LCP 指标,曾经能够在最新 Chrome 和 Android 机型上收集到了,大家设计 SDK 时无妨一起收集上来,作为一种辅助指标来掂量页面性能。
RN 收集 FP LCP 指标
FP、LCP 指标非常适合 React Web 页面,那么也同样适宜 React Native 页面。业内并未有 FP、LCP 在 RN 上的实现,北斗 SDK 参考了 W3C 的 FP 和 LCP 规范,在 RN 中实现了 FP、LCP 指标的收集。
实现思路如下,在 RN 中监听所有 Text Image 组件的 onLayout
事件,就能获取该组件的渲染工夫点。这样 FP 第一个像素的渲染耗时,就能够算进去。onLayout
中蕴含渲染元素的 width
和 height
,就能够晓得那个是渲染面积最大的文字或图片,从而计算出 LCP 的耗时。 伪代码如下:
class RNVitals {
// 记录 FP
private fp;
// 记录 LCP
private lcp;
// 监听所有 Image 的 onLayout 事件
private setWrapperImage() {}
// 监听所有 Text 的 onLyaout 事件
private setWrapperText () {
const TextRender = Text.render
Text.render = (...args) => {const originImage = TextRender.apply(this, args);
const {onLayout} = originImage.props ;
return React.cloneElement(originImage, {onLayout: (event: LayoutChangeEvent) => {this.track(event)
onLayout && onLayout(event)
}
});
}
}
// 计算 FP、LCP 指标
private track(event: LayoutChangeEvent): void {
const size = event.height * event.width
// 记录第一个元素渲染的工夫戳
if(this.fp == null) {
this.fp = {
size,
layoutTime: Date.now()}
}
// 记录 & 更新最大面积元素渲染的工夫戳
if (this.lcp.size < size) {
this.lcp = {
size,
layoutTime: Date.now()}
}
}
// 如果用户点击了页面或 lcp 2s 都没有更新,则进行性能上报
publish report(){}
}