插件化埋点采集sdk设计与实现

front-tracker-sdk 项目开发规划与技术文档

该项目最初是用于实践埋点数据采集相关知识的需要开发的,因此先有的代码,后通过AI生成的文档,文档中的内容较为简略。完整代码可访问,距离生产使用还需要进一步根据需要优化或开发需要的插件。

埋点采集sdk仅为整个埋点平台前端采集的一部分,仍有大量工作在于后端数据收集平台、分析平台等等。

一、功能需求

  1. 多维度前端数据采集:包括JS错误、资源错误、Promise未处理异常、性能指标、白屏检测、日志等。
  2. 高可靠性数据上报:支持多种上报方式,具备缓存、重试、批量等机制,保证数据不丢失。
  3. 插件化扩展:采集能力可插拔,便于按需组合和自定义扩展。
  4. 类型安全与易用性:TypeScript全量类型定义,便于二次开发和集成。

二、架构设计与技术实现

2.1 插件化架构设计

  • 核心调度器(Tracker)负责插件注册、数据分发、方法注入等。
  • 采集插件实现统一接口,独立采集各类数据。
  • 上报模块负责数据缓存、批量、重试与多通道上报。
Tracker核心源码与注释
typescript 复制代码
export class Tracker {
  // 基础配置
  private appId: TrackerConfig['appId'];
  private userId: TrackerConfig['userId'];
  private sdkVersion: string;
  private reporter: TrackerReporter;
  public debug: TrackerConfig['debug'];
  public ssr: TrackerConfig['ssr'];
  // 插件列表
  private plugins: TrackerConfig['plugins'];
  // 方法注入表
  private provideMethods: Map<string, (...args: any[]) => void> = new Map();
  // 单例实例
  private static _instance: Tracker;
  constructor(options: TrackerConfig) {
    this.appId = options.appId;
    this.userId = options.userId;
    this.sdkVersion = '1.0.0';
    this.debug = options.debug || false;
    this.ssr = options.ssr || false;
    this.reporter = options.reporter;
    this.reporter.install(this);
    this.plugins = options.plugins || [];
    this.plugins.forEach(plugin => {
      plugin.tracker = this;
      plugin.install(this);
    });
  }
  static init(options: TrackerConfig) {
    try {
      if (Tracker._instance) {
        return Tracker._instance;
      }
      Tracker._instance = new Tracker(options);
      return Tracker._instance;
    } catch (error) {
      console.error('[Tracker] 初始化失败', error);
      return null;
    }
  }
  public submit(type: ReportType, data: Record<string, unknown>) {
    if (this.debug) {
      console.log(`[Tracker] 数据提交, 类型【${type}】 数据:`, data);
    }
    this.reporter.add({
      type,
      ...this.baseInfo,
      data
    });
  }
  public registerMethod(methodName: string, method: (...args: any[]) => void) {
    if (this.provideMethods.has(methodName)) {
      if (this.debug) {
        console.warn(`[Tracker] 方法[${methodName}]已被注册`);
      }
      return;
    }
    this.provideMethods.set(methodName, method);
  }
  public callMethods(methodName: string, ...args: any[]) {
    const method = this.provideMethods.get(methodName);
    if (!method) {
      if (this.debug) {
        console.warn(`[Tracker] 方法[${methodName}]不存在`);
      }
      return;
    }
    method(...args);
  }
}

优点:插件解耦,易于扩展和维护;支持插件间方法注入,增强灵活性;单例模式防止多实例冲突。


2.2 内置采集插件实现

2.2.1 JS错误采集插件(JsErrorPlugin)
typescript 复制代码
export class JsErrorPlugin implements TrackerPlugin {
  public tracker: Tracker | null = null;
  install(tracker: Tracker) {
    this.tracker = tracker;
    window.addEventListener('error', this.handleError.bind(this), true);
    window.addEventListener('unhandledrejection', this.handlePromiseRejection.bind(this));
  }
  private handleJsError(event: ErrorEvent): void { ... }
  private handleResourceError(event: Event): void { ... }
  private handlePromiseRejection(event: PromiseRejectionEvent): void { ... }
}
  • 监听全局JS错误、资源错误、Promise未处理异常,解析堆栈并上报。
  • 优点:采集能力独立,便于按需引入,覆盖面广。
2.2.2 性能采集插件(performancePlugin)
typescript 复制代码
export class performancePlugin implements TrackerPlugin {
  public tracker: Tracker | null = null;
  private metrics = { ... };
  private config: performancePluginConfig;
  constructor(config: Partial<performancePluginConfig> = {}) {
    this.config = { ...defaultPerformancePluginConfig, ...config }
  }
  install(tracker: Tracker) {
    this.tracker = tracker;
    // 监听FMP、LCP等性能指标
    new PerformanceObserver((entryList, observer) => { ... }).observe({ entryTypes: ['element']})
    // ...更多性能指标采集
    window.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        this.tracker?.submit('performance', this.metrics);
      }
    }, { once: true });
  }
}
  • 采集FMP、LCP、CLS等核心性能指标,支持长任务、资源、绘制等多维度性能数据,对于数据获取、计算有大量文章介绍,这不展开。
2.2.3 白屏检测插件(WhiteScreenDetectPlugin)
typescript 复制代码
export class WhiteScreenDetectPlugin implements TrackerPlugin {
  public tracker: Tracker | null = null;
  private frameElementIds: string[];
  private numSamples: number;
  private delay: number;
  private autoDetect: boolean = true;
  constructor(config: Partial<WhiteScreenDetectPluginConfig>) { ... }
  install(tracker: Tracker) {
    this.tracker = tracker;
    this.autoDetect && setTimeout(() => {
      this.handleDetectScreen();
    }, this.delay);
    this.tracker.registerMethod('handleDetectScreen', this.handleDetectScreen.bind(this));
  }
  handleDetectScreen () { ... }
}
  • 自动/手动检测页面白屏,支持自定义采样点与无效元素规则。
2.2.4 Vue错误采集插件(vueErrorPlugin)
typescript 复制代码
export class vueErrorPlugin implements TrackerPlugin {
  public tracker: Tracker | null = null;
  private app: App | null = null;
  private originalErrorHandler: ((err: unknown, vm: ComponentPublicInstance | null, info: string) => void) | undefined;
  constructor(app: App) { ... }
  install(tracker: Tracker) {
    this.tracker = tracker;
    if (!this.app) throw new Error('[vueErrorPlugin]: app不存在')
    this.app.config.errorHandler = (
      err: unknown,
      vm: ComponentPublicInstance | null,
      info: string
    ) => {
      const errorData = {
        error: this.normalizeError(err),
        ...this.getVueContext(vm, info),
      }
      this.tracker?.submit('vueError', errorData)
      if (typeof this.originalErrorHandler === 'function') {
        this.originalErrorHandler.call(null, err, vm, info)
      }
    }
  }
  getVueContext = (vm: ComponentPublicInstance | null, info: string) => { ... }
  normalizeError(err: unknown) { ... }
}
  • 捕获Vue 3运行时错误,采集组件上下文、生命周期等信息。
2.2.5 日志采集插件(loggerPlugin)
typescript 复制代码
export class loggerPlugin implements TrackerPlugin {
  public tracker: Tracker | null = null;
  private loggerLevel: LOGGER_LEVEL;
  constructor(config: Partial<consolePluginConfig> = {}) {
    this.loggerLevel = config.loggerLevel || LOGGER_LEVEL.WARN;
  }
  install(tracker: Tracker) {
    this.tracker = tracker;
    this.tracker.logger = this.createLogger(console);
  }
  createLogger(originalConsole: Console): Console { ... }
}
  • 代理console对象,采集日志信息,支持日志级别过滤。

2.3 上报模块设计与源码

  • 队列缓存:采集数据先入队,定时批量上报,减少请求压力。
  • 本地持久化:队列数据持久化到localStorage,防止刷新/崩溃丢失。
  • 重试机制:上报失败自动重试,最大重试次数可配置。
  • 多通道支持:支持fetch、image、sendBeacon等多种上报方式。
typescript 复制代码
export class Rporter implements TrackerReporter {
  private queue: Array<ReporterDataType> = [];
  private timer: any = null;
  private retryCount: number = 0;
  private isSending: boolean = false;
  private currentBatch: Array<ReporterDataType> = [];
  public add(data: ReporterDataType) {
    this.queue.push(data);
    this.queue = this.queue.slice(-this.config.maxQueueLength);
  }
  public flush() {
    if (this.queue.length > 0 && !this.isSending) {
      this.saveQueue();
      this.isSending = true;
      this.currentBatch = this.queue.splice(0, this.config.abatchLength);
      this._send();
    }
  }
  private async _send() { ... }
  private saveQueue() {
    localStorage.setItem(this._getLocalStorageKey(), JSON.stringify(this.queue));
  }
  private loadQueue() {
    const queue = JSON.parse(localStorage.getItem(this._getLocalStorageKey()) || '[]');
    if (queue && Array.isArray(queue)) {
      this.queue = queue;
    }
  }
}

优点:批量、缓存、重试机制保证数据可靠上报,多通道适配不同环境,本地持久化防止数据丢失。


三、优缺点总结

  • 插件化设计,采集与上报解耦,易于扩展和维护。
  • 上报模块具备差分、缓存、重试等机制,保证数据可靠性。
  • 采集插件覆盖前端主流监控需求,支持自定义扩展。
  • 类型安全,开发体验良好。

四、基本使用

typescript 复制代码
import { Tracker, JsErrorPlugin, Rporter, performancePlugin, WhiteScreenDetectPlugin, vueErrorPlugin, loggerPlugin, LOGGER_LEVEL } from '@irises/front-tracker-sdk'

// 初始化插件实例
const tracker = Tracker.init({
  appId: 'appId-xxxxxxxx',
  sdkVersion: '1.0.0',
  debug: false,
  userId: 'userId',
  // 传入上报实例,这是该sdk自带的上报模块,也可自行实现,符合接口标准即可
  reporter: new Rporter({
    apiUrl: 'http://localhost:3000/api/report',
  }),
  // 对于不同的埋点上报需求,分别做了不同的插件化,根据需要实例化传入,内置插件也具有一些参数,下文具体介绍
  plugins: [
    new JsErrorPlugin(),
    new performancePlugin(),
    new WhiteScreenDetectPlugin({ delay: 0, autoDetect: false }),
    new vueErrorPlugin(app),
    new loggerPlugin({ loggerLevel: LOGGER_LEVEL.INFO })
  ]
})
相关推荐
前端工作日常10 分钟前
前端基建的幸存者偏差
前端·vue.js·前端框架
Electrolux18 分钟前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法
a cool fish(无名)1 小时前
rust-参考与借用
java·前端·rust
只有干货2 小时前
前端传字符串 后端比较date类型字段
前端
波波鱼દ ᵕ̈ ૩2 小时前
学习:JS[6]环境对象+回调函数+事件流+事件委托+其他事件+元素尺寸位置
前端·javascript·学习
太阳上的雨天3 小时前
与 TRON (波场) 区块链进行交互的命令行工具 (CLI): tstroncli
typescript·区块链·交互·tron·trx·trc20
climber11213 小时前
【Python Web】一文搞懂Flask框架:从入门到实战的完整指南
前端·python·flask
Watermelo6173 小时前
极致的灵活度满足工程美学:用Vue Flow绘制一个完美流程图
前端·javascript·vue.js·数据挖掘·数据分析·流程图·数据可视化
门前云梦3 小时前
ollama+open-webui本地部署自己的模型到d盘+两种open-webui部署方式(详细步骤+大量贴图)
前端·经验分享·笔记·语言模型·node.js·github·pip
Micro麦可乐3 小时前
前端拖拽排序实现详解:从原理到实践 - 附完整代码
前端·javascript·html5·拖拽排序·drop api·拖拽api