Back To Blog

How would you create a Analytics SDK used by webpages

July 18, 20245 min read

How would you create a Analytics SDK used by webpages

1. Define Requirements and Features

  • Tracking: Page views, custom events, user interactions (e.g., clicks, form submissions), and errors.
  • User Identification: Ability to track anonymous users and support user identification.
  • Session Management: Manage user sessions to track behaviors across sessions.
  • Data Transmission: Send collected data to a server or analytics endpoint.
  • Performance: Minimal impact on page load times and resource usage.
  • Configurability: Allow users to configure what data to track and where to send it.

2.Set Up the Project

/analytics-sdk
├── /src
│   ├── index.ts
│   ├── tracker.ts
│   ├── session.ts
│   ├── config.ts
│   └── utils.ts
├── package.json
├── tsconfig.json
└── README.md

3. Create the SDK Core

  • Detailed API Design:

    AnalyticsSDK = {
      init: (config) => {},
      trackEvent: (eventName, properties) => {},
      trackPageView: (pageData) => {},
      setUserProperties: (properties) => {},
    };
    // Initialize the SDK
    AnalyticsSDK.init({
      apiKey: "your-api-key",
      trackPageViews: true,
      sessionTimeout: 30,
    });
    
    // Track a custom event
    AnalyticsSDK.trackEvent("button_click", {
      buttonId: "submit-form",
      pageSection: "contact-form",
    });
    
    // Set user properties
    AnalyticsSDK.setUserProperties({
      userId: "12345",
      userType: "premium",
    });
    
  • Session Management

    // session.js
    
    import { v4 as uuidv4 } from "uuid";
    
    // 假设 defaultConfig 在某处定义
    const defaultConfig = {
      sessionTimeout: 30 * 60 * 1000, // 30 minutes in milliseconds
    };
    
    export class SessionManager {
      constructor() {
        this.sessionId = this.getSessionId() || this.createSession();
        this.lastActivity = Date.now();
      }
    
      createSession() {
        const newSessionId = uuidv4();
        localStorage.setItem("sessionId", newSessionId);
        return newSessionId;
      }
    
      getSessionId() {
        return localStorage.getItem("sessionId");
      }
    
      refreshSession() {
        if (Date.now() - this.lastActivity > defaultConfig.sessionTimeout) {
          this.sessionId = this.createSession();
        }
        this.lastActivity = Date.now();
      }
    
      getSession() {
        return this.sessionId;
      }
    }
    
  • Event Tracking Implementation:

    // track.js
    
    import axios from 'axios';
    import { SessionManager } from './session';
    
    const defaultConfig = {
      apiEndpoint: 'https://your-analytics-api.com/events',
      batchSize: 10,  // 每批发送的最大事件数
      flushInterval: 30000  // 自动发送间隔,单位毫秒
    };
    
    export class Tracker {
      constructor(config = {}) {
        this.config = {...defaultConfig,...config}:
        this.sessionManager = new SessionManager();
        this.eventQueue = [];
        this.flushInterval = null;
        this.startAutoFlush();
      }
    
      trackEvent(event, data) {
        this.sessionManager.refreshSession();
        const eventData = {
          event,
          data,
          sessionId: this.sessionManager.getSession(),
          timestamp: new Date().toISOString()
        };
        this.eventQueue.push(eventData);
    
        if (this.eventQueue.length >= defaultConfig.batchSize) {
          this.flush();
        }
      }
    
      trackPageView(url) {
        this.trackEvent('page_view', { url });
      }
    
      async flush() {
        if (this.eventQueue.length === 0) return;
    
        const events = this.eventQueue.slice();
        this.eventQueue = [];
    
        try {
          await this.sendData(events);
          console.log(`Successfully sent ${events.length} events`);
        } catch (error) {
          console.error('Failed to send analytics data', error);
          // 发送失败时,将事件放回队列
          this.eventQueue = events.concat(this.eventQueue);
        }
      }
    
      async sendData(payload) {
        await axios.post(defaultConfig.apiEndpoint, payload);
      }
    
      startAutoFlush() {
        this.flushInterval = setInterval(() => this.flush(), defaultConfig.flushInterval);
      }
    
      stopAutoFlush() {
        if (this.flushInterval) {
          clearInterval(this.flushInterval);
          this.flushInterval = null;
        }
      }
    }
    
  • Make the SDK Usable

    //index.js
    // src/index.js
    
    import { Tracker } from "./track";
    
    export default function initializeAnalyticsSDK(config) {
      return new Tracker(config);
    }
    
    // 在你的应用中引入 SDK
    import initializeAnalyticsSDK from "analytics-sdk";
    
    // 初始化 SDK
    const analytics = initializeAnalyticsSDK({
      apiEndpoint: "https://your-analytics-api.com/events", // 替换为实际 API 地址
      batchSize: 20, // 可选: 覆盖默认配置
      flushInterval: 15000, // 可选: 覆盖默认配置
    });
    
    // 追踪一个事件
    analytics.trackEvent("button_click", { buttonId: "signup" });
    
    // 追踪页面视图
    analytics.trackPageView(window.location.href);
    
    // src/index.js
    
    import { Tracker } from "./track";
    
    class Analytics {
      constructor() {
        this.tracker = null;
      }
    
      // 简单或高级初始化
      init(options) {
        if (typeof options === "string") {
          // 简单初始化,只使用 API key
          this.tracker = new Tracker({ apiKey: options });
        } else if (typeof options === "object") {
          // 高级初始化,使用配置对象
          this.tracker = new Tracker(options);
        } else {
          throw new Error("Invalid initialization parameters.");
        }
        return this; // 返回 this,支持链式调用
      }
    
      // 设置用户属性
      setUserProperty(propertyName, value) {
        if (!this.tracker) {
          console.warn(
            "Analytics not initialized. Please call Analytics.init() first."
          );
          return this; // 返回 this,即使没有初始化也支持链式调用
        }
        this.tracker.setUserProperty(propertyName, value);
        return this; // 返回 this,支持链式调用
      }
    
      // 追踪事件
      trackEvent(event, data) {
        if (!this.tracker) {
          console.warn(
            "Analytics not initialized. Please call Analytics.init() first."
          );
          return this; // 返回 this,即使没有初始化也支持链式调用
        }
        this.tracker.trackEvent(event, data);
        return this; // 返回 this,支持链式调用
      }
    
      // 追踪页面视图
      trackPageView(url) {
        if (!this.tracker) {
          console.warn(
            "Analytics not initialized. Please call Analytics.init() first."
          );
          return this; // 返回 this,即使没有初始化也支持链式调用
        }
        this.tracker.trackPageView(url);
        return this; // 返回 this,支持链式调用
      }
    }
    
    // 导出 Analytics 单例
    const analyticsInstance = new Analytics();
    export default analyticsInstance;
    
    import Analytics from "analytics-sdk";
    
    // 链式调用示例
    Analytics.init("YOUR_API_KEY")
      .setUserProperty("age", 30)
      .setUserProperty("gender", "female")
      .trackEvent("button_click", { buttonId: "signup" })
      .trackPageView(window.location.href);
    
© MiaMia2025. All rights reserved.