SDK 主要分为两部分:
第一部分:SDK 主要是 SDK 的驱动,包含:入口、核心工具以及通用类型的推断。
第二部分:也叫做插件部分(蓝色区域),主要实现上面的三项数据指标的采集。
接下来主要会详细的介绍第二部分,各项指标的采集方案。
异常采集方案通过监听 error 错误,即可捕获到所有(JS 错误、图片加载、CSS 加载、JS 加载、Promise 等)异常;它也支持 InternalError、ReferenceError 。
以下是关键性代码。
监听事件 /** * 监听 error、unhandledrejection 方法处理异常信息 * * @param {YicheMonitorInstance} instance SDK 实例 */ export default function setupErrorPlugin(instance: YicheMonitorInstance) { // JS 错误或静态资源加载错误 on('error', (e: Event, url: any, lineno: any) => { handleError(instance, e, url, lineno); }); // Promise 错误,IE 不支持 on('unhandledrejection', (e: any) => { handleError(instance, e); }); } 判断异常类型 /** * W3C 模式支持 ErrorEvent,所有的异常从 ErrorEvent 这里取 * * @param {MutationEvent} error 资源错误、代码错误 */ function handleW3C(event: any) { switch (event.type) { // 判断脚本错误,还是资源错误 case 'error': event instanceof ErrorEvent ? reportJSError(instance, event) : reportResourceError(instance, event); break; // Promise 是否存在未捕获 reject 的错误 case 'unhandledrejection': reportPromiseError(instance, event); break; } } 捕获异常数据 /** * 上报 JS 异常 * * @param {YicheMonitorInstance} instance SDK 实例 * @param {ErrorEvent} event */ export default function reportJSError( instance: YicheMonitorInstance, event: ErrorEvent, ): void { // 设置上报数据 const report = new ReportDataStruct('error', 'js'); const errorInfo = event.error ? event.error.message : `未知错误:${event.message}`; // 设置错误信息,兼容远程脚本不设置 Script error 导致的异常 report.setData({ det: errorInfo.substring(0, 2000), des: event.error ? event.error.stack : '', defn: event.filename, deln: event.lineno, delc: event.colno, rre: 1, }); } 处理 IE 兼容问题捕获异常时处理下 IE 的兼容性问题即可,IE 的方案如下:
/** * IE 8 的错误项,所以针对于 IE 8 浏览器,我们只需要获取到它出错了即可。 * * 1. 错误消息 * 2. 错误页面 * 3. 错误行号(因为文件通常是压缩的,所以统计 IE8 的行号是没有任何意义的) * * @param {string} error 错误消息 * @param {string | undefined} url 异常的 URL * @param {number | undefined} lineno 异常行数,IE 没有列数 */ export function handleIE8Error( error: string, url?: string | undefined, lineno?: number | undefined, ) { return { colno: 0, lineno: lineno, filename: url, message: error, error: { message: error, stack: `IE8 Error:${error}`, }, } as ErrorEvent; } /** * IE 9 的错误,需要在 target 里面获取到 * * @param { Element | any } error IE9 异常的元素 */ export function handleIE9Error(error: any) { // 获取 Event const event = error.currentTarget.event; return { colno: event.errorCharacter, lineno: event.errorLine, filename: event.errorUrl, message: event.errorMessage, error: { message: event.errorMessage, stack: `IE9 Error:${event.errorMessage}`, }, } as ErrorEvent; } 性能采集方案 浏览器页面加载过程 性能指标获取方式我们借助于浏览器原生的 Navigation Timing API 能够获取到上述页面加载过程中的各项性能指标数据,用于性能分析,它的时间单位是纳秒级。
当然也借助于 PerformanceObserver API 等用于测量 FCP、LCP、FID、TTI、TBT、CLS 等关键性指标。
详细的计算公式 指标 含义 计算公式ttfb 首字节时间 timing.responseStart - timing.requestStart
domReady Dom Ready时间 timing.domContentLoadedEventEnd - timing.fetchStart
pageLoad 页面完全加载时间 timing.loadEventStart - timing.fetchStart
dns DNS 查询时间 timing.domainLookupEnd - timing.domainLookupStart
tcp TCP 连接时间 timing.connectEnd - timing.connectStart
ssl SSL 连接时间 timing.secureConnectionStart > 0 ? timing.connectEnd - timing.secureConnectionStart) : 0
contentDownload 内容传输时间 timing.responseEnd - timing.responseStart
domParse DOM 解析时间 timing.domInteractive - timing.responseEnd
resourceDownload 资源加载耗时 timing.loadEventStart - timing.domContentLoadedEventEnd
waiting 请求响应 timing.responseStart - timing.requestStart
fpt 白屏时间,老 timing.responseEnd - timing.fetchStart
tti 首次可交互 timing.domInteractive - timing.fetchStart
firstByte 首包时间 timing.responseStart - timing.domainLookupStart
domComplete DOM 完成时间 timing.domComplete - timing.domLoading
fp 白屏时间,新指标 performance.getEntriesByType('paint')[0]
fcp 首次有效内容绘制 performance.getEntriesByType('paint')[1]
lcp 首屏大内容绘制时间 PerformanceObserver('largest-contentful-paint')"
快开比 页面完全加载时长 ≤ 某时长(如2s)的 采样PV / 总采样PV * 100%
慢开比 页面完全加载时长 ≥ 某时长(如5s)的 采样PV / 总采样PV * 100%
网络请求采集方案
网络请求,通过 Object.definePropety 的方式对 XHR 做的代理。关键性代码如下。
重写 XMLHttpRequest