首页/文章/前端监控

前端性能监控和错误收集与上报

2022-10-25
16256 分钟
...

错误信息收集方案:

前端错误分为:代码执行错误、资源加载错误

一、try catch

try{
    // 代码块
} catch(e){
    // 错误处理、这块可以进行上报
}

这种方式需要开发者手动对预估存在错误风险进行包裹,这种可以手动完成也可以通过自动化工具和类库完成。自动化工具也是基于AST实现的,比如UglityJS等。

try catch的特点:对于处理运行时非异步错误没问题,但无法处理语法错误和异步错误。

// 能捕获
try{
    a // 未定义变量
} catch(e){
    console.log(e)
}
// 不能捕获--语法错误
try{
    var a = \ 'a'
}catch(e){
   console.log(e)
}
// 不能捕获--异步
try{
   setTimeout(()=>{
        a
    })
} catch(e){
    console.log(e)
}
// 在setTimeout里面再加一层try catch才会生效

总结:try catch有局限性,且对代码侵入性强

二、window.onerror--事件冒泡的方式处理

window.onerror的特点:能对语法异常和运行时异常(异步也能捕获)进行处理,但是对语法错误和网络错误(因为网络请求异常不会发生事件冒泡)无能为力,代码侵入性小,不必通过AST向代码中自动插入脚本。

window.onerror = function(message, source, lineno, colno, error) { ... }
  • message:错误消息(字符串)。
  • source:引发错误的脚本的URL(字符串)
  • lineno:发生错误的行号(数值)
  • colno:发生错误的行的列号(数值)
  • error:错误对象(对象),比如error.stack会获取错误的堆栈信息

跨域脚本的错误处理:

对于不同域的js文件,window.onerror不能捕获到有效信息。出于安全原因,不同浏览器返回的错误信息参数可能不一致。跨域之后window.onerror在很多浏览器中是无法捕获异常信息的,统一会返回脚本错误(script error)。所以需要对脚本进行设置

crossorigin="anonymous"

使用source map进行错误还原

三、Promise的错误信息处理

提倡Promise最后写上catch函数的习惯,可以通过ESLint插件eslint-plugin-promise检查。

promise的错误通过捕获事件unhandlerejection

window.addEventListener('unhandledrejection',e=>{
    console.log(e)
})

四、网络异常错误处理

可以进行如下操作:

<script src"**.js" onerror="errorHandle(this)"></script>

或者使用:window.addEventListener('error') 事件捕获

window.addEventListener('error',e=>{
    console.log(e)
},true)

那这两个error:window.onerror和window.addEventListener('error')怎么区分网络加载错误和其他一般错误,可以通过以下方式

window.addEventListener('error',e=>{
    if(!e.message){
        // 网络资源加载错误
    }
},true)

window.onerror和window.addEventListener('error')区别:

  • window.onerror:冒泡,需要进行函数赋值,重复声明会被替换

  • window.addEventListener('error'):捕获,可以绑定多个回调函数,按照绑定顺序执行

五、页面崩溃收集和处理

通过监听window对下的load和beforeunload进行处理并结合sessionStorage

window.addEventListener('load',()=>{
    sessionStorage.setTitem('page_exit','pending')
})
window.addEventListener('beforeunload',()=>{
    sessionStorage.setTitem('page_exit','true')
})

sessionStorage.getTitem('page_exit')!='true' // 页面崩溃

六、vue框架的错误处理

Vue.config.errorHandler捕获的错误会以console.error的方式输出

Vue.config.errorHandler = function (err, vm, info) {       reportError({             code: 'xx',             msg: encodeURIComponent(err),             action: 'vue-render-error',             line: info,             attach2: err && err.stack || ''       });};

因此可以劫持console.error来捕获框架中的错误

const nativeConsoleError = window.console.error

window.console.error = (..args)=>nativeConsoleError.apply(this,args)

性能数据

监控指标:首次绘制时间(FP)、首次有内容绘制(FCP)时间、 首次有意义绘制时间(FMP)、首屏时间、用户可交互(TTI)时间

关键数据耗时:

var navigasitonInfo = null;if (typeof performance != 'undefined' && typeof performance.getEntriesByType != 'undefined') {  navigasitonInfo = performance.getEntriesByType('navigation')[0];};
let timing = window.performance.timing;
//页面完全加载耗时let complete_load_duration = timing.loadEventEnd - timing.navigationStart 
// 页面白屏时间
let white_screen_duration = time.domLoading - timing.navigationStart
// 首屏时间
let first_screen_duration = calcFirstScreen()
// 可交互

let interactive_duration = timing.domContentLoadedEventEnd - timing.navigationStart
// 准备耗时
let stalled_duration= timing.domainLookupStart - timing.navigationStart
// 重定向耗时
let redirect_duration = timing.redirectEnd - timing.redirectStart
// DNS解析耗时
let dns_duration = timing.domainLookupEnd - timing.domainLookupStart
// ip连接耗时
let ip_connect_duration = timing.connectEnd - timing.connectStart
// 首包耗时
let first_data_duration = timing.responseStart - timing.requestStart 
// 完整包加载耗时
let final_data_duration = timing.responseEnd - (timing.requestStart?timing.requestStart:timing.fetchStart)),
// dom处理
let dom_operate_duration = timing.domComplete - timing.domLoading
// 资源加载耗时
let res_load_duration  = info.duration
// 当前页面文件大小
let res_size = navigasitonInfo ? navigasitonInfo.transferSize : 0
function calcFirstScreen() {        var timing = window.performance.timing;        var firstScreen = timing.loadEventStart - timing.navigationStart;        if (typeof window.performance != 'undefined' && typeof window.performance.getEntriesByName != 'undefined'){            try{                var iHeight = window.innerHeight;                var iWidth = window.innerWidth;                var imgList = document.querySelectorAll('img');                var loadEventDuration = timing.loadEventEnd - timing.navigationStart;                for (var i=0,j=imgList.length;i<j;i++){                    var target = imgList[i];                    if (typeof target.getBoundingClientRect != 'undefined' && target.src) {                        var rectInfo = target.getBoundingClientRect();                        if (rectInfo){                            var pageYOffset = window.pageYOffset;                            var topH = rectInfo.top + pageYOffset;                            if (topH < iHeight && rectInfo.left >=0 &&rectInfo.left<iWidth){                                var perList = window.performance.getEntriesByName(target.src);                                if (perList.length){                                    var targetPer = perList[0];                                    if (targetPer.fetchStart < loadEventDuration){                                        firstScreen = targetPer.responseEnd;                                    };                                };                            };                        };                    };                };            }catch (e) {            }        };        return firstScreen;    }

错误上报

最好采用单独域名,防止对业务服务器造成压力以及同一个域名的请求量有并发数的限制

上报方式:

1、图片的形式

图片不涉及跨域的问题,使用构造空的Image对象的方式

let reportUrl = 'xxurl.json';let reportImg= new Image();let dataStr = paramStr(xxx);reportImg.src = newReportUrl + '?' + dataStr;

2、navigator.sendBeacon

使用 sendBeacon() 方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能。这就解决了提交分析数据时的所有的问题:数据可靠,传输异步并且不会影响下一页面的加载。

window.addEventListener('unload', logData, false);

function logData() {
    navigator.sendBeacon("/log", analyticsData);
}

上报时机:

  • 页面加载和重新刷新
  • 页面切换路由
  • 页面关闭
  • 页面所在的tab标签重新可见

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
前端监控2025-05-29
前端错误收集方式
在讲错误收集方式之前,先理清一下前端编码时有那些常见错误 &nbsp; &nbsp; &nbsp; &nbsp; 前端错误收集是保障用户体验、排查线上问题的关键手段。本文将从多个角度详解前端错误收集的各种方式,包括不同框架(如 React、Vue)、JavaScript 执行环境中的同步/异步/Promise 错误、资源加载错误(如图片、script 加载失败)等。以下是全面的错误捕获机制总结: &nbsp; &nbsp; 🌐 一、通...
学习
前端监控2024-10-12
前端小玩具~错误捕获定位工具
正文 我们在平时的工作中,开发功能的同时不可能把场景考虑的面面俱到,而生产环境往往情况是非常复杂的,用户录入进去的数据总是千奇百怪,那如果遇到问题的话,我们又要如何进行排查呢?总不可能让用户录个屏吧哈哈 所以我们就出现了 前端埋点 的操作,不过埋点的方向以及文章都挺多的,也都挺复杂的,这篇文章我们就讲一个比较有趣的 错误捕获思路 。 我们平时在使用框架开发 遇到bug 时,比如 Vue ,如果是在本地环境,我们在控制台可以很容易的找到出...
学习
前端监控2024-09-19
🍻 前端服务监控原理与手写开源监控框架SDK前端服务监控概述
前端监控的目的很明确,无非就是让我们的产品更完善,更符合我们和用户的需求。运营与产品团队需要关注用户在产品内的行为记录,通过用户的行为记录来优化产品,研发与测试团队则需要关注产品的性能以及异常,确保产品的性能体验以及安全迭代。 而一个完整的前端监控平台至少需要包括三个部分: 数据采集与上报、数据整理和存储、数据展示 。算上需要监控的项目的话,也就是说,至少需要4个项目才能完整的记录前端监控的内容。 下图是一个完整的前端监控平台需要处理和...
学习
前端监控2024-08-19
前端数据监控平台
比如客户说:你们的页面咋白屏了? 开发:具体可以给我说一下,白屏之前你做了什么吗? 客户:我就点了一下按钮 开发:你能告诉我是哪个页面的哪个按钮吗? 最后,客户极其不耐烦的地丢过来一张没有缺少url的白屏给你看,试图证明他说的是真的,真白屏了。 最后不再理睬你! 开发人员试图找到解决方案,想快速解决线上bug,但是客户却以为你在质疑他。 真的有点秀才遇上兵有理说不清,其实真的没有谁对谁错,只是双方立场不同,作为客户,你的系统bug了,我...
学习
前端监控2024-02-20
前端实现埋点&监控
实现埋点功能的意义主要体现在以下几个方面: 0. 数据采集:埋点是数据采集领域(尤其是用户行为数据采集领域)的术语,它针对特定用户行为或事件进行捕获、处理和发送的相关技术及其实施过程。通过埋点,可以收集到用户在应用中的所有行为数据,例如页面浏览、按钮点击、表单提交等。 1. 数据分析:采集的数据可以帮助业务人员分析网站或者App的使用情况、用户行为习惯等,是后续建立用户画像、用户行为路径等数据产品的基础。通过数据分析,企业可以更好地了解...
学习
前端监控2023-12-19
前端埋点重要环节:前端如何录制用户的操作过程
前言 大家好,我是林三心,用最通俗易懂的话讲最难的知识点是我的座右铭,基础是进阶的前提是我的初心。 背景 公司有很多的项目,但是并不是每一个项目都很重要,其实重要的项目就那么几个,上面也是很重视这几个项目,尤其是对一些生产问题的关注度很高。 这几天上面交代下来了,需要对这些项目做一些用户行为的记录,主要是为了更好地还原用户在某一个时间点的操作过程 注意点 想要完成这个需求,仔细想了一下,需要注意几个点: 跨框架使用: 这些项目有vue、...
学习

评论

请登录后发表评论

去登录
加载评论中...

目录