插件化埋点采集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 })
  ]
})
相关推荐
前端老宋Running7 分钟前
一次从“卡顿地狱”到“丝般顺滑”的 React 搜索优化实战
前端·react.js·掘金日报
隔壁的大叔7 分钟前
如何自己构建一个Markdown增量渲染器
前端·javascript
用户4445543654269 分钟前
Android的自定义View
前端
WILLF10 分钟前
HTML iframe 标签
前端·javascript
枫,为落叶27 分钟前
Axios使用教程(一)
前端
小章鱼学前端32 分钟前
2025 年最新 Fabric.js 实战:一个完整可上线的图片选区标注组件(含全部源码).
前端·vue.js
ohyeah33 分钟前
JavaScript 词法作用域、作用域链与闭包:从代码看机制
前端·javascript
流星稍逝35 分钟前
手搓一个简简单单进度条
前端
倚栏听风雨1 小时前
详解 TypeScript 中,async 和 await
前端
4***14901 小时前
TypeScript在React中的前端框架
react.js·typescript·前端框架