一、稳定性指标
js错误:js执行错误、promise异常。
资源错误:js、css、图片等资源加载异常。
白屏:页面加载完成后超过一定时间,页面仍然是空白。
二、上报数据结构
1. 基础数据:用来记录错误发生时用户环境
参数名 |
参数描述 |
userAgent |
记录用户UA信息 |
platform |
用户操作系统平台,iPhone、iPad、Android、Windows、Mac |
browser |
用户浏览器,IE、Safari、Chrome、Firefox、UCBrowser、Edge、Opera、360、QQBrowswe |
url |
页面链接信息 |
screen |
屏幕信息,结构:window.screen.width*window.screen.height |
system |
dev(开发平台)、ops(运营后台) |
userId |
用户ID(后端自动记录) |
createTime |
上报时间(后端自动记录) |
2. 错误数据:用来记录具体的错误信息
2.1 基础错误字段
参数名 |
参数描述 |
type |
错误类型,jsError、resError、blankError |
2.2 js错误字段
参数名 |
参数描述 |
subType |
子类型,jsError有两种子类型 jsError、vueError、promiseError |
message |
错误消息 |
stack |
错误堆栈 |
position |
行列信息,结构: lineno:colno |
2.3 资源加载错误字段
参数名 |
参数描述 |
tagName |
标签名,如IMG |
resUrl |
资源链接 |
3. 上报示例
3.1 jsError:js执行错误
{
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"platform":"Mac",
"browser":"Chrome",
"url": "https://local.qq.com:8080/api",
"screen": "1728*1117",
"system": "ops",
"type": "jsError",
"subType": "jsError",
"message": "Uncaught ReferenceError: myFunc is not defined",
"stack": "ReferenceError: myFunc is not defined\ n at eval(webpack - internal: ///./src/main.ts:78:3)\n at sentryWrapped (webpack-internal:///./node_modules/@sentry/browser/esm/helpers.js:99:17)",
"position": "121:7"
}
3.2 jsError:promise错误
{
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"platform":"Mac",
"browser":"Chrome",
"url": "https://local.qq.com:8080/api",
"screen": "1728*1117",
"system": "ops",
"type": "jsError",
"subType": "promiseError",
"message": "Promise wrong!",
"stack": "Error: Promise wrong!\n at eval (webpack-internal:///./src/main.ts:80:12)\n at sentryWrapped (webpack-internal:///./node_modules/@sentry/browser/esm/helpers.js:99:17)"
}
3.3 resError:资源加载错误
{
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"platform":"Mac",
"browser":"Chrome",
"url": "https://local.qq.com:8080/api",
"screen": "1728*1117",
"system": "ops",
"type": "resError",
"tagName": "IMG",
"resUrl": "https://local.qq.com:8080/res/1.png"
}
3.4 blankError:白屏上报
{
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
"platform":"Mac",
"browser":"Chrome",
"url": "https://local.qq.com:8080/api",
"screen": "1728*1117",
"system": "ops",
"type": "blankError"
}
三、指标采集
1. js执行错误和资源加载错误采集
window.addEventListener(
'error',
function (event) {
console.log('error')
console.log(event)
if (event.target && (event.target.src || event.target.href)) {
console.log(
`Loading failure for ${event.target.tagName} with source ${event.target.src || event.target.href}`
)
} else {
console.log('JavaScript 运行时错误:', { message: event.message, stack: event.error.stack })
}
},
true
)
2. vue组件内错误采集
vue组件内部的js错误不会被error事件捕获
Vue.config.errorHandler = function (err, vm, info) {
console.log('Vue jsError :', { message: err.message, stack: err.stack })
}
3. promise异常采集
window.addEventListener('unhandledrejection', function (event) {
console.log('Unhandled rejection :', { message: event.reason.message, stack: event.reason.stack })
})
4. 白屏数据采集
4.1白屏判定:根节点检测方案
通过检测根节点是否有dom确定是否是白屏,特点:适用SPA应用,通用性差。
function checkWhiteScreen() {
const rootNode = document.querySelector('#app')
if (rootNode.children.length === 0) {
return true
} else {
return false
}
}
4.2 白屏判定:elementsFromPoint采样检测方案
下面是采用垂线采样,屏幕中采样点如果都是白屏那么就是白屏状态。特点:通用性强,代码复杂,需要消耗更多前端计算资源。因为我们是SPA应用,所以我们选择根节点检测方案即可。
function checkWhiteScreen() {
// 定义外层容器元素的集合
const containerElements = ['html', 'body', '#app', '#root']
// 容器元素个数
let emptyPoints = 0
// 选中dom的名称
function getSelector(element) {
if (element.id) {
return '#' + element.id
} else if (element.className) {
// div home => div.home
return (
'.' +
element.className
.split(' ')
.filter(item => !!item)
.join('.')
)
} else {
return element.nodeName.toLowerCase()
}
}
// 是否为容器节点
function isContainer(element) {
const selector = getSelector(element)
if (containerElements.indexOf(selector) != -1) {
emptyPoints++
}
}
// 页面加载完毕初始化
for (let i = 1; i <= 9; i++) {
const xElements = document.elementsFromPoint((window.innerWidth * i) / 10, window.innerHeight / 2)
const yElements = document.elementsFromPoint(window.innerWidth / 2, (window.innerHeight * i) / 10)
isContainer(xElements[0])
// 中心点只计算一次
if (i != 5) {
isContainer(yElements[0])
}
}
// 17个点都是容器节点算作白屏
if (emptyPoints == 17) {
return true
}
return false
}
4.3 检测时机:轮询超时检测方案
定义:
T0:页面onload
T1:页面白屏检测间隔
T2:页面超时判定白屏的时间
这里先暂时将T1设置3s,T2设置6s
方法:
当页面onload后,每隔T1检测用户是否白屏,如果不是白屏停止检测,如果是白屏继续按照T1间隔继续检测,当检测的总时间大于T2判定为白屏。
// 页面加载完毕
function onload(callback) {
if (document.readyState === 'complete') {
callback()
} else {
window.addEventListener('load', callback)
}
}
// 定义T1和T2
const T1 = 3000; // 3秒检测间隔
const T2 = 6000; // 6秒超时判定
// 白屏检测逻辑
function startWhiteScreenDetection() {
let totalTime = 0;
const intervalId = setInterval(() => {
totalTime += T1;
const isWhiteScreen = checkWhiteScreen();
if (!isWhiteScreen) {
console.log('Page loaded successfully, stopping detection');
clearInterval(intervalId); // 停止检测
} else if (totalTime >= T2) {
console.warn('White screen detected');
// 上报白屏问题到你的监控服务
// sendToAnalytics({ issue: 'white-screen' });
clearInterval(intervalId); // 停止检测
}
}, T1);
}
// 页面加载完毕后开始白屏检测
onload(startWhiteScreenDetection);