3.前端监控---埋点监控

52 阅读3分钟

1. 方案概述

1.1 埋点类型

  1. 手动埋点:开发人员在代码中显式调用埋点方法。
  2. 自动埋点:通过监听全局事件自动触发埋点。
  3. 可视化埋点:可视化埋点。
  4. 用户离开页面埋点:记录用户离开页面的行为。

1.2 默认埋点信息

默认埋点信息包括:

  • 页面信息:页面 URL、页面标题、页面加载时间。
  • 设备信息:设备类型、操作系统、浏览器类型、屏幕分辨率。
  • 环境信息:网络类型、语言设置、时区。

2. 默认埋点信息设计

2.1 默认埋点信息

以下是可以直接获取的默认埋点信息:

const defaultTrackingData = {
  // 页面信息
  pageUrl: window.location.href, // 页面 URL
  pageTitle: document.title, // 页面标题
  pageLoadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart, // 页面加载时间

  // 设备信息
  deviceType: navigator.userAgent, // 设备类型
  os: navigator.platform, // 操作系统
  browser: navigator.userAgent, // 浏览器类型
  screenResolution: `${window.screen.width}x${window.screen.height}`, // 屏幕分辨率

  // 环境信息
  networkType: navigator.connection?.effectiveType || 'unknown', // 网络类型
  language: navigator.language, // 语言设置
  timezone: Intl.DateTimeFormat().resolvedOptions().timeZone, // 时区
};

3. 埋点工具类实现

3.1 核心工具类

Tracker 类负责埋点的核心逻辑,包括默认埋点信息的获取、数据合并和发送。

class Tracker {
  constructor() {
    this.pageEnterTime = Date.now(); // 记录页面进入时间
    this.trackPageLeave(); // 监听页面离开事件
  }

  // 监听页面离开事件
  trackPageLeave() {
    window.addEventListener('beforeunload', () => {
      const pageStayTime = Date.now() - this.pageEnterTime; // 计算页面停留时间
      this.track('page_leave', {
        pageStayTime,
        leaveType: 'close_or_refresh', // 离开方式:关闭或刷新页面
      });
    });

    window.addEventListener('unload', () => {
      const pageStayTime = Date.now() - this.pageEnterTime; // 计算页面停留时间
      this.track('page_leave', {
        pageStayTime,
        leaveType: 'navigate', // 离开方式:跳转到其他页面
      });
    });
  }

  // 手动埋点
  track(eventType, customData = {}) {
    const trackingData = this.mergeTrackingData({
      eventType,
      ...customData,
    });
    this.sendTrackingData(trackingData);
  }

  // 合并默认信息和自定义信息
  mergeTrackingData(customData = {}) {
    return {
      pageUrl: window.location.href,
      pageTitle: document.title,
      pageLoadTime: window.performance.timing.loadEventEnd - window.performance.timing.navigationStart,
      deviceType: navigator.userAgent,
      os: navigator.platform,
      browser: navigator.userAgent,
      screenResolution: `${window.screen.width}x${window.screen.height}`,
      networkType: navigator.connection?.effectiveType || 'unknown',
      language: navigator.language,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
      ...customData,
      timestamp: new Date().toISOString(),
    };
  }

  // 发送埋点数据
  sendTrackingData(data) {
    console.log('Tracking Data:', data);
    // 示例:fetch('/api/track', { method: 'POST', body: JSON.stringify(data) });
  }
}

// 使用示例
const tracker = new Tracker();
tracker.track('button_click', { buttonId: 'submit_button' });

4. 自动埋点实现

4.1 自动埋点类

AutoTracker 类继承自 Tracker,负责监听全局事件并自动触发埋点。

class AutoTracker extends Tracker {
  constructor(config) {
    super();
    this.config = config;
    this.init();
  }

  // 初始化自动埋点
  init() {
    this.trackClicks();
    this.trackScrolls();
    this.trackInputs();
  }

  // 监听点击事件
  trackClicks() {
    document.addEventListener('click', (event) => {
      const target = event.target;
      const elementPath = this.getElementPath(target);

      if (this.shouldTrackElement(target, 'click')) {
        const eventData = this.mergeTrackingData({
          eventType: 'click',
          eventTarget: elementPath,
        });
        this.sendTrackingData(eventData);
      }
    });
  }

  // 监听滚动事件
  trackScrolls() {
    let lastScrollTime = 0;
    window.addEventListener('scroll', () => {
      const currentTime = new Date().getTime();

      // 防抖处理,避免频繁触发
      if (currentTime - lastScrollTime > 1000) {
        lastScrollTime = currentTime;
        const eventData = this.mergeTrackingData({
          eventType: 'scroll',
          scrollY: window.scrollY,
        });
        this.sendTrackingData(eventData);
      }
    });
  }

  // 监听输入事件
  trackInputs() {
    document.addEventListener('input', (event) => {
      const target = event.target;
      const elementPath = this.getElementPath(target);

      if (this.shouldTrackElement(target, 'input')) {
        const eventData = this.mergeTrackingData({
          eventType: 'input',
          eventTarget: elementPath,
          value: target.value,
        });
        this.sendTrackingData(eventData);
      }
    });
  }

  // 获取元素的路径
  getElementPath(element) {
    const path = [];
    while (element && element.nodeName.toLowerCase() !== 'body') {
      let selector = element.nodeName.toLowerCase();
      if (element.id) {
        selector += `#${element.id}`;
      } else if (element.className) {
        selector += `.${element.className.split(' ').join('.')}`;
      }
      path.unshift(selector);
      element = element.parentNode;
    }
    return path.join(' > ');
  }

  // 判断元素是否需要埋点
  shouldTrackElement(element, eventType) {
    const elementPath = this.getElementPath(element);

    // 检查埋点配置
    for (const [page, config] of Object.entries(this.config.pages)) {
      if (window.location.pathname === page) {
        if (config.excludeComponents.includes(elementPath)) {
          return false; // 禁止埋点
        }
        if (config.events.includes(eventType)) {
          return true; // 允许埋点
        }
      }
    }
    return false;
  }
}

// 使用示例
const trackingConfig = {
  pages: {
    '/home': {
      events: ['click', 'scroll', 'input'],
      excludeComponents: ['header'],
    },
    '/product/:id': {
      events: ['click', 'scroll'],
      excludeComponents: [],
    },
  },
};

const autoTracker = new AutoTracker(trackingConfig);

5. 配置管理

5.1 前端配置

在前端项目中,通常会有一个配置文件来管理埋点信息。

const trackingConfig = {
  pages: {
    '/home': {
      events: ['click', 'scroll', 'input'],
      excludeComponents: ['header'],
    },
    '/product/:id': {
      events: ['click', 'scroll'],
      excludeComponents: [],
    },
  },
};

5.2 接口获取配置

后端提供一个接口,用于动态获取埋点配置。

async function fetchTrackingConfig() {
  try {
    const response = await fetch('/api/tracking-config');
    const config = await response.json();
    return config;
  } catch (error) {
    console.error('Failed to fetch tracking config:', error);
    return null;
  }
}

6. 总结

本文档提供了一套完整的埋点方案,包括:

  1. 默认埋点信息:涵盖页面、设备和环境信息。
  2. 手动埋点:支持显式调用埋点方法。
  3. 自动埋点:监听全局事件,自动触发埋点。
  4. 用户离开页面埋点:记录页面停留时间和离开方式。