Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端监控科普文 #71

Open
peng-yin opened this issue Aug 10, 2021 · 0 comments
Open

前端监控科普文 #71

peng-yin opened this issue Aug 10, 2021 · 0 comments

Comments

@peng-yin
Copy link
Owner

peng-yin commented Aug 10, 2021

前端监控可以分为三类:数据监控(埋点)、性能监控和异常监控。

image


数据监控

数据监控,就是监听用户的行为,常见的监控项有:

  • PV/UV:PV(page view):即页面浏览量或点击量;UV:指访问某个站点或点击某条新闻的不同 IP 地址的人数
  • 用户通过什么入口来访问该网页
  • 用户在相应的页面中触发的行为
  • 用户在每一个页面的停留时间

image

  • pv,page view 页面打开的次数,无论用户是否浏览里面的子模块,都会记录
  • mv,module view 页面打开了,用户浏览了页面里的子模块的次数
  • mc,module click 页面打开了,用户点击了页面里的子模块的次数

image

统计这些数据是有意义的,比如我们知道了用户来源的渠道,可以促进产品的推广,知道用户在每一个页面停留的时间,可以针对停留较长的页面,增加广告推送等等。


性能监控

性能监控指的是监听前端的性能,主要包括监听网页或者说产品在用户端的体验。常见的性能监控项包括:

  • 不同用户,不同机型和不同系统下的首屏加载时间
  • 白屏时间
  • http 等请求的响应时间
  • 静态资源整体下载时间
  • 页面渲染时间
  • 页面交互动画完成时间

这些性能监控的结果,可以展示前端性能的好坏,根据性能监测的结果可以进一步的去优化前端性能,比如兼容低版本浏览器的动画效果,加快首屏加载等等。


异常监控

由于产品的前端代码在执行过程中也会发生异常,因此需要引入异常监控。及时的上报异常情况,可以避免线上故障的发上。虽然大部分异常可以通过 try catch 的方式捕获,但是比如内存泄漏以及其他偶现的异常难以捕获。常见的需要监控的异常包括:

  • Javascript 的异常监控
  • 样式丢失的异常监控

前端埋点

我们说完了前端监控的三个分类,现在就来聊聊怎么实现前端监控。实现前端监控,第一步肯定是将我们要监控的事项(数据)给收集起来,再提交给后台,最后进行数据分析。数据收集的丰富性和准确性会直接影响到我们做前端监控的质量,因为我们会以此为基础,为产品的未来发展指引方向。

收集监控数据我们是通过前端埋点来实现的,目前常见的前端埋点方法有三种:手动埋点、可视化埋点和无埋点。

  1. 手动埋点

手动埋点,也叫代码埋点,即纯手动写代码,调用埋点 SDK 的函数,在需要埋点的业务逻辑功能位置调用接口,上报埋点数据,像友盟、百度统计等第三方数据统计服务商大都采用这种方案。

手动埋点让使用者可以方便地设置自定义属性、自定义事件;所以当你需要深入下钻,并精细化自定义分析时,比较适合使用手动埋点。

手动埋点的缺陷就是,项目工程量大,需要埋点的位置太多,而且需要产品开发运营之间相互反复沟通,容易出现手动差错,如果错误,重新埋点的成本也很高。这会导致整个数据收集周期变的很长,收集成本变的很高,而且效率很低。因为手动埋点需要开发人员完成,所以每次有埋点更新,或者漏埋点,都需要重新走上线发布流程,更新成本也高,对线上系统稳定性也有一定危害。

  1. 可视化埋点

通过可视化交互的手段,代替上述的代码埋点。将业务代码和埋点代码分离,提供一个可视化交互的页面,输入为业务代码,通过这个可视化系统,可以在业务代码中自定义的增加埋点事件等等,最后输出的代码耦合了业务代码和埋点代码。缺点就是可以埋点的控件有限,不能手动定制。

可视化埋点听起来比较高大上,实际上跟代码埋点还是区别不大。也就是用一个系统来实现手动插入代码埋点的过程。比如国外比较早做可视化的是 Mixpanel,国内较早支持可视化埋点的有TalkingData、诸葛 IO,2017年腾讯的 MTA 也宣布支持可视化埋点;相比于手动埋点更新困难,埋点成本高的问题,可视化埋点优化了移动运营中数据采集的流程,能够支持产品运营随时调整埋点,无需再走发版流程,直接把配置结果推入到前端,数据采集流程更简化,也更方便产品的迭代。

可视化埋点中多数基于Xpath的方案,XPath 是一门在 XML 文档中查找信息的语言,XPath 可用来在 XML 文档中对元素和属性进行遍历。

  1. 无埋点

无埋点则是前端自动采集全部事件,上报埋点数据,由后端来过滤和计算出有用的数据。优点是前端只要一次加载埋点脚本,缺点是流量和采集的数据过于庞大,服务器性能压力山大。

采用无埋点技术的有主流的 GrowingIO、神策。

总结
在不同场景下我们需要选择不同的埋点方案。例如对于简单的用户行为类事件,可以使用全埋点解决;而对于需要携带大量运行时才可获知的业务字段的埋点需求,就需要声明式埋点来解决。

简单实践

  1. 收集用户信息

通过浏览器内置的 JavaScript 对象,我们就可以收集当前用户的一些基本信息,我们将收集好的数据通过 Image 对象实例的 src 属性指向后端脚本并携带参数,就可以将我们收集的数据传给后端,之所以用 Image 对象而不是 ajax,是为了避免跨域的问题。

(function () {
    let params = {};
    // document
    if (document) {
        params.domain = document.domain || ''; // 域名
        params.url = document.URL || ''; // 当前 URL 地址
        params.title = document.title || ''; // 当前页面标题
        params.referrer = document.referrer || ''; // 上一个访问页面 URL 地址
    }
    // window
    if(window && window.screen) {
        params.sh = window.screen.height || 0; // 屏幕高度
        params.sw = window.screen.width || 0; // 屏幕宽度
        params.cd = window.screen.colorDepth || 0; // 屏幕颜色深度
    }
    // navigator
    if(navigator) {
        params.lang = navigator.language || ''; // 语言
    }
    // 拼接参数
    let args = '';
    for(let i in params) {
        if(args !== '') {
            args += '&';
        }
        args += `${i}=${params[i]}`
    }
    // 通过伪装成 Image 对象,传递给后端
    let img = new Image(1, 1);
    let src = `http://www.funlee.cn/api/test.jpg?args=${encodeURIComponent(args)}`;
    img.src = src;
})()

(function() {
    let hm = document.createElement("script");
    hm.type = "text/javascript";
    hm.async = true;
    hm.src = "http://www.funlee.cn/testAnalyze.js";
    let s = document.getElementsByTagName("script")[0];
    s.parentNode.insertBefore(hm, s);
})();
  1. 获取 web 各阶段响应时间

为了准确获取我们的 web 应用程序的性能特性,我们就得知道该应用程序在各个阶段的响应时间,比如:DNS 解析时间、TCP 建立连接时间、首页白屏时间、DOM 渲染完成时间、页面 load 时间等。好在这些信息都可以通过 Performance 接口获取相关信息来计算得出。

let timing = performance.timing,
    start = timing.navigationStart,
    dnsTime = 0,
    tcpTime = 0,
    firstPaintTime = 0,
    domRenderTime = 0,
    loadTime = 0;

dnsTime = timing.domainLookupEnd - timing.domainLookupStart;
tcpTime = timing.connectEnd - timing.connectStart;
firstPaintTime = timing.responseStart - start;
domRenderTime = timing.domContentLoadedEventEnd - start;
loadTime = timing.loadEventEnd - start;

console.log('DNS解析时间:', dnsTime,
            '\nTCP建立时间:', tcpTime,
            '\n首屏时间:', firstPaintTime,
            '\ndom渲染完成时间:', domRenderTime,
            '\n页面onload时间:', loadTime);
  1. CSS 埋点

CSS 埋点只能处理一些简单的事件埋点,收集复杂的数据还是得用 JS,但 CSS 埋点有个优点就是,无法被禁用。

完整实例请戳:CSS埋点

核心代码如下:

<style>
.link:active::after{
    color: red;
    content: url("http://192.168.1.110:3000/someapi?params=someInfo");
}
</style>
<a class="link btn">点击访问</a>

前端监控将主要的度量指标分为了3类:性能、请求、异常。

image

请求指标

  1. 请求 = API请求 + JS 请求 + CSS 请求 + 图片请求 + 自定义上报

其中 API 请求时间 指 API 请求类型的平均时间。该指标不支持 TP 计算。

计算方式: 单次API请求时间(ms) = (xhr.send.readyState === 4) - xhr.open

  1. 请求调用数

请求调用数 = Sum(1中所有请求的上报次数)

  1. 网络成功率

在一个请求被上报时,同时附带了两种不同的信息

  • 网络状态码

  • 业务状态码

网络成功状态的判断标准:Http状态码 2XX~3XX 表示成功,4XX~5XX 表示失败。
网络成功率 = 网络成功的请求次数 / 请求总数 * 100%

  1. 业务成功率
    业务成功状态的判断标准:在网络通信成功的基础上,前端成功获取了后端的服务响应,若成功状态码与配置的一致,则认为业务成功。在没有配置的情况下,「默认成功」

业务成功率 = 业务成功的请求次数 / 请求总数 * 100%

性能指标

  1. 秒开率 = 1 秒之内打开页面的页面数 / 总页面加载数 * 100%

  2. 首次渲染时间

  3. 首屏时间

  4. 页面加载量 = 页面被加载的次数

  5. 首字节时间

  6. DOM 解析结束

  7. DOM Ready时间

异常指标

  1. JS错误率 = ( 发生的错误总数 / 页面总PV ) * 100%

  2. 异常 = js异常 + 静态资源异常 + API请求异常 + 自定义异常

异常的判定

  • JS异常:所有的window.error事件都会被上报异常(包含 Promise的unhandledrejection)

  • 静态资源异常:静态资源加载状态为”error“的异常

  • API 异常:通过 Ajax 发送的请求,并且状态码不为 2XX 。

  • 自定义异常:使用owl.addError()上报的异常

参考来源

@peng-yin peng-yin changed the title 指标 前端监控 Aug 26, 2021
@peng-yin peng-yin changed the title 前端监控 前端监控科普文 Jan 20, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant