一文搞懂前端监控

公众号:程序员白特,欢迎一起交流学习~
原文:一文搞懂前端监控 - 掘金 (juejin.cn)

前端监控在项目维护过程中至关重要。监控的核心目标包括及时发现问题快速定位问题所在。尽管各公司所用的前端监控工具和服务可能各有不同,但从业务开发者角度来看,它们都具有一些共通之处。

一个完善的前端上报服务工具应该包含以下功能:

  • 累积量上报:每次上报触发都对计数器进行累加。
  • 日志上报:将详细的埋点信息上报到服务端,可以分为全量上报和本地缓存上报。
  • 错误上报:捕获 JavaScript 错误并将错误信息进行收集上报到服务端,通常使用 window.onerror 实现。
  • 耗时上报:对关键阶段的耗时进行收集上报,用于性能分析。

告警策略是及时发现问题的重要手段,一个完善的前端上报服务工具应该包含以下告警策略:

  • 有则告警:一旦有上报就会触发告警,适用于一些重要异常上报。
  • 阈值告警:当一段时间内(如每分钟)的上报量达到设定的阈值时触发告警,比如关键后台接口失败量的最大值上报。阈值可以根据数量或者百分比来设定。阈值上报可分为最小值阈值和最大值阈值。
  • 波动告警:用于监控指标的变化和波动情况。当指标超出正常范围时,快速发现问题并采取相应措施。

PV/UV上报

PV(页面访问量)指的是页面被浏览的次数,而UV(独立访客数)表示访问网站的实际用户数量。这两种上报可以使用累积量上报。

PV上报除了用于业务分析外,还可以设置告警来及时发现问题。

  • PV波动告警:当PV的波动轨迹出现异于正常的波动时触发告警,这适用于波动变化具有一定规律性的页面。
  • PV最小值阈值告警:当PV值低于某个阈值时触发告警。

举例来说,对于这样的B端页面,在配置波动告警时,可以作如下考量:由于用户通常会在工作日使用该页面,因此可以通过比较每天的PV数据与相应的上周同一工作日的数据,来触发波动告警。

例如,如果今天是周四,可与上周同一天(也是周四)的PV数据进行对比,以便在异常波动出现时触发告警:

另外一种告警是最小值阈值告警,告警策略配置了每天8点以后pv低于200时告警:

JS错误上报

JS错误经常是导致页面无法访问的主要原因。通过使用 window.onerrordocument.addEventlistener(error)XMLHttpRequest status 等方式,可以捕获并上报JS错误,以便获取页面的错误信息及错误堆栈。大多数前端上报工具的SDK都已内置了上报错误的功能,因此这里不再赘述。

在JS错误上报过程中,通常会配合使用sourcemap,这有助于我们找到错误发生的确切位置。

另一个问题是错误上报产生的噪音。噪音指的是诸如不会影响页面正常使用的报错。错误上报的噪音并不能完全消除,通常采用忽略这些错误的方式来处理。

日志上报

日志上报对于快速定位问题而言至关重要。通常情况下,业务开发者需要自行进行日志上报。在哪些区域进行日志上报能够很好地考验一个前端开发者的经验与能力。

链路日志上报

其中一种思路是根据首屏加载链路,对各个阶段添加 info 级别的日志。例如:

  • html 开始加载成功
  • css 加载成功
  • index.js 加载成功
  • 后台接口请求成功
  • 图片请求成功
  • 各种点击事件

当页面出现问题时,例如白屏问题,可以查看上述链路的日志,检查是否有日志阻塞,结合错误上报进行排查,可以大致定位到错误的位置。

异常日志上报

有一些错误信息,如 WebSocket API 的 onerror 事件,或某些第三方 NPM 包自行使用 try-catch 后暴露的 error 事件,是无法被 window.onerror 等方法拦截到的。对于这些异常信息,需要进行手动上报,例如:

import logger from 'your_logger_package';
const ws = new WebSocket("wss://mywebsocket.com");
ws.onerror = function(error) {
 logger.error(`websocket连接失败: ${error}`)
};

这些错误可以根据需要添加"有则告警"或"阈值告警"等告警策略。

白屏上报

白屏上报是一种非常特殊且普遍的异常上报方式。白屏指的是用户在访问页面时,页面长时间保持空白(白屏)状态的问题。这种情况会给用户带来非常糟糕的体验,因此及时让开发人员了解并做出响应至关重要。

"白屏"的判断方法可以参考文章 前端白屏的检测方案,让你知道自己的页面白了 中关于采样对比的方案。它的大致思路是在页面中间选取17个样本点形成十字形排列,在一段时间后判断这些采样点的最上层元素是否是容器元素,如['html', 'body', '#app', '#root']。如果所有采样点的最上层元素都是容器元素,则可以确定页面出现了白屏。

白屏上报有许多不同的时机策略,其中一种是每隔一秒钟轮询判断是否白屏并上报,这种策略相对消耗页面性能。另一种策略是选择几个时间点,例如10秒后的若干时间点,在这些有限的时间点进行上报。

// 判断是否白屏
const whiteScreen = () => {
 // 该代码请参考 "前端白屏的检测方案,让你知道自己的页面白了" 文章
 // 此时不再重复
};
window.addEventListener("load", () => {
 // 白屏上报时机是10秒、15秒、20秒
 const intervals = [10, 15, 20];
 // 白屏上报的时间间隔,如果遇到一个非白屏的时间点,则不再上报
 const runTime = (index) => {
   const frontSecond = intervals[index - 1] || 0;
   const curSecond = intervals[index];
   const diffSecond = curSecond - frontSecond;
   if (diffSecond > 0) {
     setTimeout(() => {
       // 如果是白屏则上报,且进入下个时间点的白屏上报
       if (isWhiteScreen(containerSelectors)) {
         logger.error(`白屏${curSecond}秒`);
         runTime(index + 1);
       }
     }, diffSecond * 1000);
   }
 };
 runTime(0);
});

同样地,白屏上报也可以配置“有则告警”或“阈值告警”的告警策略。

日志存储方案

对于日志存储,有两种主要方案:

  • 服务器存储:所有日志直接以全量上报的方式存储到日志服务器。当页面 PV 较大时,将上报的日志量较多,可能会给日志服务带来较大压力。
  • 本地存储:大部分日志存储到浏览器本地,通常使用 IndexDB 作为缓存存储。在用户出现问题时,给用户标记,并等用户下次请求时将日志尽数上报到服务器。

随着日志量的增长,存储服务的成本也会越高。出于成本控制与效率提升的目的,我个人更倾向于采用本地存储的上报方式,因为大部分日志并非都是有用的。在用户出现问题时标记用户再进行日志收集,通常已经足够应对问题。

性能上报

作为程序员,最令人担忧的情况之一就是用户能够重现问题,但自己却无法重现。性能问题也是类似,本来在自己的设备上表现良好,但用户却可能遇到页面加载缓慢的情况。在这种时候,性能上报就成了重要的救命工具。

web vitals 上报

性能上报最简单的方式是使用谷歌推出的核心 Web 体验指标(Core Web Vitals),只需使用 Web Vitals 的 NPM 包即可实现。需要特别关注的指标包括:

  • LCP(Largest Contentful Paint):表示页面上最大的内容块(如文本块或图像)渲染完成并出现在用户视野中的时间,衡量页面的视觉加载速度。
  • FID(First Input Delay):指页面首次交互的延迟时间,即用户首次与页面交互到浏览器实际能够响应的时间。
  • CLS(Cumulative Layout Shift):反映页面元素的不稳定性,衡量用户在浏览页面时遇到的视觉布局变化的程度。
  • FCP(First Contentful Paint):指浏览器将第一个 DOM 渲染到屏幕的时间,可以是任何文本、图像、SVG 等内容的时间。

Web Vitals 的上报可以通过耗时上报来实现。

链路耗时上报

Web Vitals 能够帮助我们了解性能出现问题的情况,然而要知道具体出现了什么问题则相对困难。在这种情况下,我们需要对页面的各个阶段进行细化,并对每个阶段进行耗时上报,以缩小问题出现的范围。可以通过 Chrome 的性能工具分析页面的大致加载链路,进而了解页面整个加载过程中各个阶段的性能情况。

抽象出这种性能链路后,可以基于业务需求自定义关注的各个阶段,并统计这些阶段的耗时。例如,对于上述的单页面应用链路,可以上报以下信息:

  • 前置JS资源请求耗时
  • 页面JS请求耗时
  • 后台接口耗时
  • 图片加载耗时

通过细化各个阶段的耗时后,一旦页面变慢,可以提取这些耗时数据,以查看哪个阶段出现了问题,进而确定性能瓶颈所在。

链路耗时上报的另一个用途在于防劣化:一轮性能优化后,后续的需求更新可能会对性能产生影响。因此,对各个环节的耗时设置最大阈值告警,一旦超过阈值,便能够立即追踪是哪次更新导致的问题,然后及时进行修复。

其他上报

除了上述提到的上报方式,根据业务需求还有其他方式可以进行上报:

  • 后台接口异常上报
  • 卡顿上报:使用 PerformanceObserver 判断页面是否出现卡顿现象。
  • 资源错误上报
  • 资源被篡改上报:通过 script 标签的 integrity 属性,判断页面资源是否被篡改,以确定资源是否遭到劫持。

总结

项目上线后,等真正有用户的反馈故障时实际上已经为时已晚了,这时前端监控显示出了它真正的价值。前端监控需要配合公司中现有的上报服务来实现,无需将监控做到尽善尽美,而是可以根据不同的情况做出取舍。

#前端##如果可以选,你最想去哪家公司##我的实习求职记录##实习,投递多份简历没人回复怎么办##我的求职思考#
全部评论

相关推荐

        顺丰二面面经续集。1.monorepo的各个子应用是独立打包的吗?        有在字节实习的uu应该都接触过monorepo。我是进去之后才知道这个东西,但对他的原理也不明白,今天跟大家一块了解一下。        首先什么是monorepo。我个人理解monorepo是一个将不同项目的代码放在同一个代码仓库中组织起来的一种方式。可以想象github中的一个仓库中有很多小项目的代码文件。这些项目虽然有可能是相关的,但通常在逻辑上是独立的,并由不同的团队维护。像我之前实习时的项目仓库使用monorepo,仓库中既有web前端的代码,也有小程序前端的代码。        monorepo有什么好处?1.多个项目之间需要共享代码、组件或者工具库时,monorepo比较简便。使用monorepo就不需要将想要共享的部分发布npm包再去别的项目中安装,而是有一个share目录,专门放置共享的组件或方法等。2.统一依赖版本管理时更好的确保一致性。3.根目录下有一个package.json,每个项目也有自己的package.json,通过在根目录下npm install,它会自动安装所有子项目的依赖。4.在开发的时候,省去了在多个代码仓库之间切换的麻烦。还有很多优点,比如统一的CI/CD,统一的构建流程......        再谈谈不好的点。从我自己的使用角度来说,刚开始最常见的问题就是更新依赖时很容易改变了其他子项目的依赖,在全局npm install或者一些api update的时候,会给所有子项目都更新,但是可能版本更新会给别的项目带来不可预见的问题,这也是为什么需要code review,把这些不是你应该改动的东西要改回去。除此之外,打包构建需要专门优化,否则会出现打包时间过长。        那么回到monorepo的子项目是否是独立打包这个问题。要知道monorepo的每个子项目都是可以独立开发、部署、构建...试想如果只有其中一个子项目fix了一个线上bug,但是整个项目都要重新打包发布那么整个流程岂不是太过冗杂了,所以一定是可以独立打包独立安装才有足够灵活性的。但是能独立打包并不意味着你的项目中的子项目都是独立打包的,具体还要看是否在子项目package.json中配置了打包脚本。2.setState是同步还是异步        拷打完项目就开始拷打原理了。这个问题当时没记住啊,恨自己没多看两篇文章。        首先要知道这个同步和异步跟我们所谓的js同步任务异步任务不一样,是指调用setState后能否马上得到更新后的值,即是否立即调用render 函数渲染视图。        在react18之前,在react 可调度范围内的setState 就是异步的,反之,则为同步。什么是可调度范围?react 合成事件内同步执行的setState 就是可调度范围。什么是react 可调度范围外呢?宏任务:setTimeout ,微任务:.then ,或直接在DOM元素上绑定的事件等都是react 可调度范围外。        在react18中多了批处理功能(当 React 在一个单独的重渲染事件中批量处理多个状态更新以此实现优化性能),这意味着之前没办法批处理的情况都可以批处理了。在每次setstate时会产生一个优先级标志lane,对于相同优先级的多次更新任务,react实际只会复用第一个调度任务,在一次批量更新结束后才会更新实际变量的值。相同优先级的setState是异步执行的,而不同优先级则是同步的。        需要注意的是,由于js的闭包特性,同一份代码在类组件和函数组件中的执行结果是有差异的。        详细源码理解推荐以下链接(彻底搞懂setState到底是同步还是异步1,2,3):        https://blog.csdn.net/y_ang_1/article/details/136941891?spm=1001.2014.3001.5502        https://blog.csdn.net/y_ang_1/article/details/137151783?spm=1001.2014.3001.5502        https://blog.csdn.net/y_ang_1/article/details/137151803?spm=1001.2014.3001.55023.react的任务优先级策略,以及哪些任务优先级比较高?        这么多场面试,这是唯一一个问了这个问题的面试官,不得不说是有水平和经验的。东西太多了,我水平也不够,帖个帖子:https://juejin.cn/post/7207406497508114489        涉及原理的内容确实很难理解和掌握,多看点帖子多看点源码吧。        如果大家觉得对自己有帮助,感谢大家点赞收藏送花评论!希望大家意向多多offer多多!#软件开发##软件开发2024笔面经##前端##顺丰#
查看3道真题和解析 软件开发2024笔面经
点赞 评论 收藏
分享
2 20 评论
分享
牛客网
牛客企业服务