发布时间:2023-05-23 文章分类:WEB开发, 电脑百科 投稿人:李佳 字号: 默认 | | 超大 打印

提示:基于运营活动的需求,需要对用户行为进行埋点监控,因此以下文章是代码埋点的实现。

文章目录

  • 前言
  • 一、埋点思考
  • 二、埋点实现
    • 1.埋点工具类实现(operationLog.js)
    • 2.埋点监控的开启
    • 3.页面访问日志的写入
    • 4.点击事件的日志写入
  • 总结

前言

前端埋点有两种:全局埋点、代码埋点。
全局埋点:收集的用户所有行为,但是收集的数据驳杂,要进行处理。
代码埋点:收集的用户所有行为更加精准,能够进行更细节的处理。

基于上述优缺点以及团队现状,因此采用代码埋点的方式来实现。

以下仅介绍关于前端的部分,至于登录次数、登录人数等均可完全靠后端进行实现

一、埋点思考

运营需求:用户浏览各页面情况(时长等),用户对各个规定元素的点击操作。
根据运营的需求我们需要做如下事:
1.对页面的访问,离开进行监控(包括关闭浏览器等操作)。
document.addEventListener(‘visibilitychange’)

2.对点击事件进行监控
采用vue自定义命令:v-log

3.避免耗费请求资源,需要批量的给后端发送前端操作日志
在离开页面的时候或每隔三分钟发送一次日志

4.避免日志内容丢失和错误
存储在本地localStorage中,一定要通过navigator.sendBeacon发送请求,若是不支持,则采用同步请求,避免发送不成功,成功后定时清除

二、埋点实现

以下代码是埋点封装类的实现,具体详解看注释

1.埋点工具类实现(operationLog.js)

/*
  用户操作相关的所有方法
*/
import store from '@/store/store'
import router from '@/router/router'
import { VueLocalStorage } from '@/utils/storage'
class OperationLog {
// 操作类型
 operationType = {
   visit: 'PAGE_ACCESS',//访问行为
   click: 'BUTTON_CLICK'//点击行为
 }
 // 页面路由与标识符对应关系
 visitPage = new Map([
   [['/dashboard'], 'INSTRUMENT_PANEL_PAGE'],
   [['/incidents/list'], 'EVENT_ANALYZE_PAGE'],
   [['/incidentsVul/detail'], 'EVENT_ANALYZE_DETAIL_PAGE'],
   [['/application/main/monitor'], 'APPLICATION_MANAGE_MONITOR_PAGE'],
   [['/application/main/nomonitor'], 'APPLICATION_MANAGE_NOT_MONITOR_PAGE'],
   [['/application/detail/risk'], 'APPLICATION_DETAIL_RISK_LIST_PAGE'],
   [['/applicationVul/history'], 'APPLICATION_DETAIL_RISK_DETAIL_ATTACK_HISTORY_PAGE'],
   [['/applicationVul/info'], 'APPLICATION_DETAIL_RISK_DETAIL_VUL_INFO_PAGE'],
   [['/baseline/list'], 'BASELINE_INSPECT_PAGE'],
   [['/instance/list'], 'NODE_MANAGE_PAGE'],
   [['/instance/details/cpufusing', '/instance/details/baseline'], 'NODE_MANAGE_DETAIL_PAGE'],
   [['/blackWhite/main/black'], 'BLACK_LIST_MANAGE_PAGE'],
   [['/blackWhite/main/white'], 'WHITE_LIST_MANAGE_PAGE'],
   [['/strategy'], 'DEFEND_POLICY_MANAGE_PAGE'],
   [['/patch/main/list'], 'VIRTUAL_PATCH_PAGE'],
   [['/user/account', '/user/security', '/user/message'], 'PERSONAL_CENTER_PAGE'],
   [['/message/list'], 'MESSAGE_NOTIFICATION_PAGE']
 ])
 constructor () {
   this.timeOut = null// 记录定时器id
   this.timeNum = 60000 // 定时毫秒数
   this.localKeyName = 'raspLog'// 存储本地数据key
 }
 start () {
   this.visibilitywindow()
   this.timer()
 }
 // 发送操作日志信息给后端
 async reportData () {
   const data = VueLocalStorage.getJSON(this.localKeyName) || []
   // 没有操作日志的时候不进行日志发送
   if (!data.length) { return }
   const url = `/rasp/api-v1/data/event/track/push?Authorization=Bearer ${store.state.login.accessToken}`
   if (navigator.sendBeacon) {
     const blob = new Blob([JSON.stringify(data)], {
       type: 'application/json; charset=UTF-8'
     })
     navigator.sendBeacon(url, blob)
     this.clearLog()
   } else {
     // 同步请求
     const xhr = new XMLHttpRequest()
     xhr.open('POST', url, false)
     xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8')
     xhr.send(JSON.stringify(data))
     this.clearLog()
   }
 }
 // 监控窗口变化(锁屏,关闭等)
 visibilitywindow () {
   document.addEventListener('visibilitychange', () => {
     if (document.visibilityState !== 'visible') { // 离开页面
       this.reportData()
     } else { // 进入页面
       this.writeLog('visit', router.currentRoute.path)
     }
   })
 }
 // 定时器
 timer () {
   if (this.timeOut) {
     clearTimeout(this.timeOut)
     this.timeOut = null
   }
   this.timeOut = setTimeout(() => {
     this.reportData()
     this.timer()
   }, this.timeNum)
 }
 /* 记录操作信息
      type(操作类型):visit/click
      operation:type为visit的时候是path路径,为click的时候是直接传给后端的字符
  */
 writeLog (type, operation) {
   const params = { // 要记录的单挑数据
     eventType: this.operationType[type],
     functionType: '',
     createDate: Number.parseInt((new Date().getTime()) / 1000)
   }
   // 获取本地存储的数据
   const localData = (VueLocalStorage.getJSON(this.localKeyName) || [])
   if (type === 'visit') { // 访问页面
     // 根据路由找到对应页面的标识符
     const functionType = Array.from(this.visitPage.entries()).find(item => {
       return item[0].some(path => path === operation)
     })?.[1]
     // 若是未找到对应的标识符则代表此页面路由不进行记录
     if (functionType) {
       params.functionType = functionType
     } else {
       return
     }
   } else if (type === 'click') { // 点击元素
     params.functionType = operation
   }
   // 记录前检查是否为重复数据
   const repeat = (VueLocalStorage.getJSON(this.localKeyName) || []).some(item => {
     return item.eventType === params.eventType && item.functionType === params.functionType && item.createDate === params.createDate
   })
   if (!repeat) { // 没有重复则进行记录
     localData.push(params)
     VueLocalStorage.setJSON(this.localKeyName, localData)
   }
 }
 // 清空操作信息
 clearLog () {
   localStorage.removeItem(this.localKeyName)
 }
}
const operation = new OperationLog()
export function start () {
  operation.start()
}
export function writeLog (type, content) {
  operation.writeLog(type, content)
}

2.埋点监控的开启

在App.vue中

import { start } from '@/utils/operationLog.js'
  mounted () {
    start()// 开启日志监控
  },

3.页面访问日志的写入

路由拦截中进行写入日志

import { writeLog } from '@/utils/operationLog.js'
router.beforeEach((to, from, next) => {
  writeLog('visit', to.path)
})

4.点击事件的日志写入

通过定义全局的vue命令进行写入

定义命令,在mai.js对指令进行全局引入

import { writeLog } from '@/utils/operationLog.js'
// 记录点击日志
Vue.directive('log', {
  bind: (el, binding, vnode) => {
    el.addEventListener('click', () => {
      writeLog('click', binding.value)
    }, false)
  }
})

使用命令

 <el-button v-log="'EVENT_ANALYZE_ADVANCED_SEARCH_BUTTON'">确认</el-button>

总结

以上方法经过测试,可以对用户操作行为做到精准检测,除ie浏览器外,针对用户关闭、最小化、切出浏览器等操作行为进行监听且有效的上报。用户只需要在针对特殊操作如点击等使用v-log就能做到全面的监控。