# 字节前端监控实践

## 方式

* 数据的采集（埋点与收集）
* 数据上报与监控

## JS 异常监控

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-5b1aa7cec1c4aa0f028b22d96f0d9adab98c2102%2F2022-05-29-16-28-20.png?alt=media)

```js
// 同步
window.addEventListener("error", function (e) {
  console.log(e);
});
// 异步
window.addEventListener("unhandleRejection", function (e) {
  console.log(e);
});
```

对 sourceMap 的进行反编译

通过 issue Grouping 聚合提交

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-ecc645796d4d21b12bdf6f566d51882b861dcd3e%2F2022-05-30-11-15-59.png?alt=media)

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-abef3cc5b9bc5edb25d1bd1cbf02b3ade88556e5%2F2022-05-30-11-23-38.png?alt=media)

注意落地：

1. 线上报错项目对应的源代码仓库元信息 2.线上报错代码的发生版本 3.线上报错版本的关联的起止信息

## 性能监控 性能指标

一. 指标采集： Web Vitals & Navigation Timings

二. 关联用户行为

1.采集交互事件，触发全局 action\_id 记录 通过 addEventListen 记录下 2.Mutation Observer 观察 DOM，观察后续请求，静态资源加载，longtask 等数据 3.100ms 内若没有新的变更产生，则结算上报，若持续时长超过 10s，强制结算上报

三：单页面视角的性能度量

## 请求/静态资源监控

采集方案： 手动打点： 使用场景：请求耗时兜底、请求状态、请求响应相关信息 监控方式： hook XMLHttpRequest.prototype Hook fetch -> wrappedFetch 优势： 兼容性佳、采集方便 ResourceTiming： 使用场景：精确的请求耗时、静态资源耗时、分阶段观测数据 监控方式：performance.getEntriesByType('resource') PerformanceObserver.observe(type) 优势: 更精准，可以避开额外的事件队列处理耗时

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-88e71c8302283ef1ef9556bb5b69a1fc3dcadc69%2F2022-05-30-13-02-48.png?alt=media)

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-8d26e56f802ba388bfe1f7a69930189a5046d3a4%2F2022-05-30-13-03-07.png?alt=media)

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-fa0a1f34f8d7006fbbb20c1047861d92f0620dae%2F2022-05-30-13-04-01.png?alt=media)

## SDK 设计

### 异步加载

js nipper 异步加载 运用了 async ![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-f1464bbc506839cdc25c8e7ed1602b93a66b1bb5%2F2022-05-30-13-06-10.png?alt=media)

### 预收集

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-db6cefbc7b326d739f4062a6d5da41e24c94e8ac%2F2022-05-30-13-24-12.png?alt=media)

### 事件驱动

1.按需加载，不使用的代码就不拉取 2.高扩展性，允许开发自己的采集逻辑 3.降低多端开发成本

异步加载 ![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-8de6b61c4102be13a29b21e1fa503156992f0def%2F2022-05-30-13-31-14.png?alt=media)

## 搭建一个前端监控系统需要几步

数据埋点：前端 sdk 埋点，精准捕获性能数据\[性能监控 （包括页面加载耗时、接口耗时等各项数据统计）]和异常数据

事实上数据埋点分为三类：

* 业务埋点，统计诸如 PV、UV、点击率、流失率、转化率等.
* 大数据埋点，统计与用户行为相关信息，比如那个用户点击了那个商品，上报用户 ID 和商品 ID，方便后台分析用户和商品的关系，可以用做大数据分析，推荐算法来为用户推荐商品。
* 工程埋点，统计工程上的数据信息，比如页面秒开率，DNS 时间等。

存储与分析：TB 级数据清洗，实时入库存储 前端可视化：前端数据可视化，提供数据分析图形和报表 实时报警：设置报警规则和报警阙值，及时通知开发者

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-952e72beb192f3f8d3ab556c57151b49f5d957f5%2F2022-05-29-11-18-04.png?alt=media) ![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-265345b4e006ffa50c47156327844d32fb0c49be%2F2022-05-29-11-24-52.png?alt=media)

前端采集： 性能 错误 点击 停留 用户行为 ![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-29c15e30eff7bd938928145c5ba61edf6bf061c9%2F2022-05-29-11-42-45.png?alt=media)

![](https://3888932816-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MJIFoScNWx6TyBhNr8W%2Fuploads%2Fgit-blob-4850c72a7d4930fa8c1941769eddb53b15c34177%2F2022-05-29-11-47-27.png?alt=media)

要想做线上监控，分两个部分：数据采集；数据展现。数据采集部分，同样需要发布平台或者开发工具来配合，对性能数据来说，Performance API 非常好用，它是浏览器记录的性能数据，一般来说，我们用统一的代码把它上传到服务器端就够用了。

数据的展现部分就比较自由了，可以用不同的数据可视化方案来展现性能数据，没有一定之规。一般的数据监控平台，会提供报警机制，对性能来说，报警需求不是特别强烈，但是也可以设置一些条件，针对秒开率特别低的网页报警。

## 如何检测到页面的白屏？

1. 利用性能指标: 通过 PerformanceObserver 监听 paint 类型的性能条目，获取 First Paint（FP）和 First Contentful Paint（FCP）指标.

```js
const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach((entry) => {
    if (entry.entryType === "paint") {
      console.log(`${entry.name}: ${entry.startTime}`);
    }
  });
});
observer.observe({ entryTypes: ["paint"] });
```

通过监测这些指标，可以判断页面是否存在白屏现象。

2. DOM 变化检测: 通过 MutationObserver 监听 DOM 变化，判断页面是否存在白屏现象。

```js
const observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (mutation.type === "childList") {
      console.log("DOM 变化");
      // 判断特定元素是否已渲染
    }
  });
});
observer.observe(document.body, { childList: true });
```

3. 资源加载监控：监听关键资源（如 CSS、JS、图片）的加载状态，确保它们在页面渲染前已加载完成。

```js
window.addEventListener("load", () => {
  // 检查关键资源加载状态
});
```

## 如何检测并上报页面的卡死？

页面卡死通常指主线程长时间被阻塞，导致页面无法响应用户操作。

1. 使用 PerformanceObserver 监听 longtask 指标，检测页面是否存在长任务。

```js
const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach((entry) => {
    if (entry.entryType === "longtask") {
      console.log(`Longtask: ${entry.duration}ms`);
    }
  });
});
observer.observe({ entryTypes: ["longtask"] });
```

2. 使用 requestAnimationFrame 检测卡顿：通过 requestAnimationFrame 定期检查页面渲染帧率，判断是否存在卡顿。

```js
let lastTime = 0;
function checkFrame() {
  const now = performance.now();
  const delta = now - lastTime;
  if (delta > 1000 / 60) {
    // 页面卡顿，执行上报
  }
  lastTime = now;
  requestAnimationFrame(checkFrame);
}
requestAnimationFrame(checkFrame);
```

3. Web Worker 心跳检测：使用 Web Worker 定期向主线程发送心跳消息，检测主线程是否响应。

```js
// 主线程
const worker = new Worker("worker.js");
worker.postMessage("start");
worker.onmessage = (event) => {
  if (event.data === "alive") {
    // 主线程正常
  } else {
    // 主线程卡死，执行上报
  }
};

// worker.js
let status = "alive";
setInterval(() => {
  if (status === "dead") {
    postMessage("dead");
  } else {
    postMessage("alive");
  }
  status = "dead";
  setTimeout(() => {
    if (status === "dead") {
      postMessage("dead");
    }
  }, 3000);
}, 6000);
```

## 如何优化首页渲染性能？

1. 资源预加载：使用 提前加载关键资源，减少首屏加载时间。

```html
<!-- 预加载关键资源 -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preconnect" href="https://cdn.example.com" />

<!-- 异步非关键JS -->
<script defer src="non-critical.js"></script>
```

2. 代码拆分：利用 Webpack 等工具进行代码拆分，按需加载，减少初始加载的 JavaScript 体积。
3. 懒加载：对非首屏内容使用懒加载，延迟加载不影响首屏渲染的资源。
4. 服务端渲染（SSR）：在服务器端渲染页面，减少客户端的渲染负担，加快首屏渲染速度。
