前端埋点监控的实现方案

前端数据监控 SDK 设计方案

一、核心功能设计

  1. 错误监控

    • JS执行错误:监听 window.onerrorunhandledrejection(Promise错误)。
    • 资源加载错误:监听全局的 error 事件,过滤资源类型(如 scriptlinkimg)。
    • 跨域脚本错误:通过添加 crossorigin="anonymous" 属性捕获跨域脚本错误。
  2. 用户行为监控

    • 点击事件:通过事件代理监听 click 事件,记录元素信息(如 data-track-id 或选择器路径)。
    • 页面停留时长
      • 进入页面时间:记录 window.performance.timing.navigationStart 或自定义时间戳。
      • 离开页面时间:监听 beforeunloadvisibilitychange 事件(页面隐藏时计算停留时长)。
    • 路由变化:监听 hashchangepopstate 事件,捕获 SPA 路由切换。
  3. 性能监控

    • 关键性能指标:通过 Performance API 获取:
      • FP/FCP(首次绘制/首次内容渲染)
      • LCP(最大内容渲染时间)
      • FID(首次输入延迟)
      • CLS(累积布局偏移)
    • 资源加载耗时:通过 performance.getEntriesByType('resource') 获取资源加载详情。
  4. 自定义事件

    • 暴露 API 供业务方手动上报自定义事件(如按钮点击、功能使用)。

二、SDK 架构设计

1
2
3
4
5
6
7
8
9
10
11
SDK 架构示意图
├── Core
│ ├── ErrorTracker // 错误监控模块
│ ├── BehaviorTracker // 用户行为模块
│ ├── PerformanceTracker // 性能监控模块
│ └── Reporter // 数据上报模块
├── Utils
│ ├── DataQueue // 数据队列(控制上报频率)
│ ├── Storage // 离线存储(localStorage降级)
│ └── UUID // 生成唯一用户标识
└── index.js // 入口文件(初始化配置)

三、关键技术实现

  1. 数据采集

    • 错误监控示例
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      window.addEventListener('error', (e) => {
      const { message, filename, lineno, colno, error } = e;
      SDK.report({
      type: 'js_error',
      message,
      file: filename,
      line: lineno,
      column: colno,
      stack: error?.stack || ''
      });
      }, true); // 使用捕获模式监听全局错误
  2. 用户标识与关联

    • 生成唯一用户ID:通过 localStorage 存储 UUID。
    • 会话ID:每次页面加载生成新的会话标识。
    • 设备信息:通过 navigator.userAgent 解析浏览器、OS 等信息。
  3. 数据队列与上报

    • 队列机制:将数据暂存到内存队列,避免频繁请求。
    • 上报策略
      • 立即上报:关键错误实时发送。
      • 批量上报:普通事件合并后定时发送(如每 10s)。
      • 离线缓存:使用 localStorage 缓存未发送数据,网络恢复后重试。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    class Reporter {
    constructor() {
    this.queue = [];
    this.timer = null;
    }

    push(data) {
    this.queue.push(data);
    if (this.queue.length >= 10) this.flush(); // 达到阈值立即上报
    else this.startTimer(); // 启动定时上报
    }

    flush() {
    const data = this.queue.slice();
    this.queue = [];
    navigator.sendBeacon?.(endpoint, JSON.stringify(data))
    || fetch(endpoint, { body: JSON.stringify(data), keepalive: true });
    }
    }
  4. 性能指标计算

    • LCP 监听示例
      1
      2
      3
      4
      5
      6
      const observer = new PerformanceObserver((list) => {
      const entries = list.getEntries();
      const lcpEntry = entries.find(entry => entry.entryType === 'largest-contentful-paint');
      SDK.report({ type: 'performance', metric: 'LCP', value: lcpEntry.startTime });
      });
      observer.observe({ type: 'largest-contentful-paint', buffered: true });

四、关键问题与解决方案

  1. 数据丢失问题

    • 解决方案
      • 使用 navigator.sendBeacon 在页面关闭时可靠上报。
      • 本地缓存 + 重试机制(指数退避重试)。
  2. 隐私合规

    • 敏感数据过滤:屏蔽 passwordinput[type=password] 等元素的点击记录。
    • GDPR/CCPA 支持:提供全局配置开关(如 disableTracking)。
  3. 性能影响

    • 优化策略
      • 使用 requestIdleCallback 延迟非关键任务。
      • 限制事件监听范围(如通过白名单过滤无意义的点击事件)。
  4. 跨域问题

    • CORS 配置:确保服务端支持 Access-Control-Allow-Origin
    • JSONP 降级:在老旧浏览器中通过 JSONP 上报数据。

五、SDK 使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
// 初始化配置
SDK.init({
endpoint: 'https://api.your-analytics.com/log',
appId: 'YOUR_APP_ID',
sampling: 0.1, // 采样率 10%
ignoreErrors: [/Script error\.?/], // 忽略某些错误
});

// 手动上报自定义事件
SDK.track('button_click', {
button_id: 'checkout',
page: 'product_detail'
});

六、扩展功能建议

  1. 前端错误源码定位

    • 通过 SourceMap 解析压缩后的错误堆栈信息。
  2. 用户行为路径还原

    • 记录用户点击、滚动、跳转序列,生成可视化行为热力图。
  3. 性能告警

    • 设置阈值(如 LCP > 2.5s),触发邮件或 Slack 通知。
  4. A/B 测试集成

    • 结合用户行为数据,动态分配实验流量。

七、最终技术栈推荐

模块 技术方案
构建工具 Rollup + Babel(生成轻量UMD模块)
错误堆栈解析 source-map 库(解析 SourceMap)
数据序列化 protobuf(二进制编码减少体积)
测试 Jest + Puppeteer(模拟用户行为与错误场景)

通过以上设计,可以实现一个轻量、可靠、可扩展的前端监控SDK,覆盖从数据采集到上报的全链路需求。