前端 数据埋点、监控方案

215 阅读1分钟

埋点上报的主要信息

  • 埋点的标识信息: eventId, eventType, pv(page view), uv(uset pv),
  • 业务信息:sku
  • 通用的设备信息/用户消息: userId, deviceId, userAgent, timeStamp, localtion

埋点上报的分类

  • 实时上班: 立发送请求
  • 延时上报: 依托于防抖或者在浏览器空闲时间或者页面卸载前统一上报,上报失败后可以加补偿措施

埋点的实现方式

  • 代码埋点
  • 无埋点
  • 可视化埋点

代码埋点上报的实现方案

上报方案实现后,只需要提供一个需要上报的数据,和上报的最终操作即可,内部逻辑的封装就是上报的方案

import { debounce } from 'lodash'

interface RequiredData {
  timestamp: number | string
}
// 存储逻辑
class TaskQueueStorableHelper<T extends RequiredData = any>{

  public static getInstance<T extends RequiredData = any>(){
    if(!this.instance){
      this.instance = new TaskQueueStorableHelper<T>()
    }
    return this.instance
  }
  protected store: any = null
  private STORAGE_KEY = 'lubai_store'

  private static instance: TaskQueueStorableHelper | null = null

  constructor() {
    const localStorageValue = localStorage.getItem(this.STORAGE_KEY)
    if(localStorageValue){
      this.store = JSON.parse(localStorageValue)
    }
  }

  get queueData(){
    return this.store.queueData || []
  }
  set queueData(queueData: T[]){
    this.store = {
      ...this.store,
      queueData: this.queueData.sort((a,b) => Number(a.timestamp) - Number(b.timestamp))
    }
    localStorage.setItem(this.STORAGE_KEY, JSON.stringify(this.store))
  }
}

// 功能函数,获取要上报的数据,同时提供具体上报操作的接口
export abstract class AsyncTaskQueue<T extends RequiredData = any>{
  private get storableService(){
    return TaskQueueStorableHelper.getInstance<T>()
  }
  private get queueData(){
    return this.storableService.queueData
  }
  private set queueData(value: T[]){
    this.storableService.queueData = value
    if(value.length){
      this.debounceRun()
    }
  }

  protected debounceRun = debounce(this.run.bind(this), 1000)
  protected abstract consumeTaskQueue(data: T[]): Promise<any>

  protected addTask(data: T | []){
    this.queueData = this.queueData.concat(data)
  }

  private run(){
    const currentDataList = this.queueData
    if(currentDataList.length){
      this.queueData = []
      this.consumeTaskQueue(currentDataList)
    }
  }
}

无埋点方案

 window.addEventListener('click', (e) => {
      const target = e.target
      const xPath = getXPath(e.target)
      console.log(xPath)
    })
    function getXPath(element){
      if(element.id){
        return `//*[@id=\'${element.id}'\]`
      }
      if(element == document.body){
        return `/html/${element.tagName.toLowerCase()}`
      }
      let currentIndex = 1
      let siblings = element.parentNode.childNodes
      for(let sibling of siblings){
        if(sibling === element){
          return getXPath(element.parentNode) + '/' + element.tagName.toLowerCase() + '[' + currentIndex + ']'
        } else if(sibling.nodeType === 1 && sibling.tagName === element.tagName){
          currentIndex++
        }
      }
    }