前端监控笔记(三)

消息上报机制

常见方案

  • sendBeacon :常用于页面卸载阶段上报,接口简单,不阻塞页面卸载流程;通常存在单次请求体大小限制(常见约 64KB,不同浏览器可能有差异)。
  • fetch keepalive :写法更灵活,可自定义方法、请求头与内容类型;同样受卸载场景下请求体大小限制(常见约 64KB)。

发送时机

visibilitychange 事件中,当 document.visibilityState === 'hidden' 时触发上报。

  • 触发时机早于 beforeunload,标签页切后台、最小化、息屏等场景也能触发。
  • beforeunload 可能不会触发,visiblituchange 移动端可能存在兼容问题,beforeunload 可作为兼容兜底实现。
typescript 复制代码
window.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'hidden') return;

  const payload = JSON.stringify(pendingEvents);

  // 优先使用 sendBeacon,避免页面卸载时请求被中断。
  const ok = navigator.sendBeacon('/collect', payload);
  if (ok) return;

  // sendBeacon 失败时降级为 keepalive fetch。
  fetch('/collect', {
    method: 'POST',
    keepalive: true, // 允许页面卸载时继续发送请求
    headers: { 'Content-Type': 'application/json' },
    body: payload
  }).catch(() => {
    // 失败后重传,离线缓存处理
  });
});

fetchLater

注册延迟请求,在浏览器在页面离开或 activateAfter 到时后触发发送理解成 fetch + activeAfter 构成的延迟版本)

解决 sendBeacon 和 fetch keepalive 发送时机选择的问题,由浏览器排队处理

但同样有请求体大小限制,兼容性较差。

typescript 复制代码
try {
  fetchLater('/collect', {
    method: 'POST',
    body: JSON.stringify(pendingEvents),
    activateAfter: 60_000 // 最晚约 60s 后触发发送(实际时机由浏览器决定,不指定可能是页面关闭时发送)
  });
} catch (error) {
  // 配额超限或浏览器不支持时,降级 sendBeacon / fetch keepalive
}

指数退避重传

服务端返回 429 Too Many Requests(可结合 Retry-After 响应头)或其他异常时,使用指数退避进行重试。

text 复制代码
delay = baseDelay * 2^attempt * (1 + random(0, 0.5)) // 加入随机抖动

批量合并

上传时,可以通过定时器/maxBatch 批量合并日志消息,在单个请求上传处理

离线缓存

当用户离线或临时网络抖动时,可使用 IndexedDB / 内存维护缓冲队列,待网络恢复后重传。

  1. offline 事件监听到用户网络离线 ,将数据存入缓冲队列
  2. 监听 online 事件,将数据取出并重新执行
typescript 复制代码
// 简化 indexedDB 离线队列
// 如果是内存缓存队列,可以直接在监听 offline 时暂停队列批量发送,同时把发送数据重新推入缓冲队列
class OfflineQueue {
  #dbName = 'sdk-queue'

  async enqueue(event) {
    const db = await this.#openDB()
    await db.add('events', { ...event, timestamp: Date.now() })
  }

  async flush() {
    if (!navigator.onLine) return
    const db = await this.#openDB()
    const events = await db.getAll('events')
    for (const event of events) {
      const ok = await this.#send(event)
      if (ok) await db.delete('events', event.id)
    }
  }
    
  //...
}

// 监听网络恢复,触发重传
window.addEventListener('online', () => queue.flush())

采样发送

原理:通过条件概率减少发送日志,降低存储和请求开销

  • 给定概率值,每次执行发送的时候 random 得到一个随机值
  • 如果随机值在采样概率之内,就将数据发送到后台否则不发送

采样类型

  • 固定采样:全局固定采样率,永远不变。
  • 确定性采样:通过固定 id 值,比如 userId 取 hash值,然后计算出同个用户/会话的采样率。
  • 动态采样:根据业务接口、用户身份、告警等级等业务规则实现采样,即针对业务进行不同频段的采样。
typescript 复制代码
// 固定采样值
function normalSample(rate: number) {
  return Math.random() < rate;
}

// 确定性采样示例:hash id, 采样值保持不变
function deterministicSample(id: string, rate: number) {
  const hash = simpleHash(id);
  return (hash % 10000) / 10000 < rate;
}
function simpleHash(input: string) {
  let hash = 0;
  for (let i = 0; i < input.length; i += 1) {
    hash = (hash * 31 + input.charCodeAt(i)) >>> 0;
  }
  return hash;
}



// 动态采样:根据业务、接口、以及下发采样配置实现采样
function dynamicSample(context: { url: string; isVip?: boolean }) {
  if (context.url.includes('/checkout')) return 1.0;
  if (context.isVip) return 0.5;
  return 0.1;
}
function dynamicSend(context: { url: string; isVip?: boolean }) {
  const rate = dynamicSample(context);
  return Math.random() < rate;
}

去重机制

通过为每个消息日志,错误信息生成独立指纹进行去重处理,减少重复推送,节省请求成本

消息事件去重

可组合以下字段生成指纹:

  • 消息类型。
  • 业务指纹(模块、接口、页面标识)。
  • 堆栈摘要(new Error().stack 归一化后摘要)。

异常类型去重:

  1. 判断异常类型和异常值,异常类型是 erroruncaughtrejection 这种独立类型,值是错误信息
  2. 比较指纹是否一致
  3. 比较堆栈跟踪信息是否一致

总结

  • 发送策略 :优先 visibilitychange + sendBeacon,失败时降级 fetch keepalivefetchLater 作为尝鲜使用。
  • 数据可靠处理:通过限流重试、采样、去重减少无效上报,离线队列保障弱网场景下的数据补偿。

参考内容

相关推荐
Su米苏19 分钟前
关于@vue-office/excel 渲染异常滚动失去内容
前端·vue.js·excel
灯澜忆梦20 分钟前
【基于GO的Web开发10】gin获取HTML-Form表单提交参数
前端·后端·golang·html·gin
撑伞的鱼99371 小时前
2026年前端AI编程工具评测:Figma 还原、组件复用、跨文件联动三项对比
前端·ai编程·figma·效率工具·ai编程工具
码视野1 小时前
基于 Vue3 + Element Plus 的【微短剧剧本智能创作与分镜生产协同系统】设计与实现(含PRD/三端源码/大屏)
前端·人工智能·vue3
zzzzzz3101 小时前
36K stars 的“酷炫组件”,到底该怎么用才不显得用力过猛?
前端·react.js·动效
浔溺1 小时前
al+大数据每日学习笔记28
笔记·学习
FakeOccupational3 小时前
【github 有趣项目】OpenPLC: 支持通用硬件的开源 PLC 软件平台‌
笔记
2401_894915534 小时前
GEO 优化源码全解析:从搜索引擎到 AI 引擎的底层改写逻辑
java·服务器·前端·数据库·人工智能·分布式·搜索引擎
xian_wwq9 小时前
【学习笔记】-深度认知系列-第2讲-大模型到底是什么?——拆解“参数、训练、推理”
笔记·学习·深度认知
Profile排查笔记10 小时前
指纹浏览器哪个好?从 Profile、代理、权限和自动化能力判断是否适合
前端·人工智能·后端·自动化