front-tracker-sdk 项目开发规划与技术文档
该项目最初是用于实践埋点数据采集相关知识的需要开发的,因此先有的代码,后通过AI生成的文档,文档中的内容较为简略。完整代码可访问,距离生产使用还需要进一步根据需要优化或开发需要的插件。
埋点采集sdk仅为整个埋点平台前端采集的一部分,仍有大量工作在于后端数据收集平台、分析平台等等。
一、功能需求
- 多维度前端数据采集:包括JS错误、资源错误、Promise未处理异常、性能指标、白屏检测、日志等。
- 高可靠性数据上报:支持多种上报方式,具备缓存、重试、批量等机制,保证数据不丢失。
- 插件化扩展:采集能力可插拔,便于按需组合和自定义扩展。
- 类型安全与易用性: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 })
]
})