阿里云日志服务之WebTracking 小程序端 JavaScript SDK (阿里SDK埋点和原生uni.request请求冲突问题)
目录标题
- 1、官方文档
- 2、安装和配置sdk
- 3、自定义封装埋点事件,进行日志上报
1、官方文档
阿里云日志服务官方文档
2、安装和配置sdk
npm install --save @aliyun-sls/web-track-mini
使用下面方式,既能解决请求冲突问题,又不会丢失日志信息
// webTracker.js
import SlsTracker from '@aliyun-sls/web-track-mini'
import {logFlag
} from '@/utils/logger.js'
import {AppState
} from '@/utils/globalState.js';
export default class webTracker {/**** @param slsConfig 阿里云上传参数* @param publicParam 项目标准公参* @param projectArgs 项目个性化公参*/constructor(slsConfig, publicParam = {}, projectArgs = {}) {// this.tracker = new SlsTracker(slsConfig)// logFlag && console.log('创建SlsTracker对象this.tracker:',this.tracker)// 800ms延迟确保普通请求优先处理// 避免SLS初始化期间拦截有效请求:当SLS初始化未完成时,直接调用uni.request会导致请求丢失setTimeout(() => {this.tracker = new SlsTracker({...slsConfig,// opt:接收完整的uni.request配置对象(含url/method/data等)// 通过call(uni)显式绑定uni-app运行环境,避免this指向错误// 强制所有SLS日志请求通过uni-app原生方法发出,确保上下文绑定和参数透// 严格兼容uni-app环境时(如小程序、H5混合开发),必须使用自定义request配置,在纯浏览器环境或对请求无特殊要求时,可省略该配置// 如果不使用request配置将导致:SDK初始化阶段请求方法未就绪导致的调用失败||跨平台运行时出现的this指向混乱 || 特殊header或参数被SDK默认实现过滤// _originalRequest:指向备份的原始请求方法(通常是对 uni.request 的引用)// 头函数 (opt) => _originalRequest.call(uni, opt) 会 继承外层作用域的变量(即模块作用域中的 _originalRequest),无需额外处理request: (opt) => _originalRequest.call(uni, opt)});AppState.setSLSReady(true); // 更新全局状态this._flushQueue();}, 800);this._queue = []; // 日志暂存队列// 项目标准公参// 保留字段系日志服务自动生成,无需进行埋点 (日志来源、上报时间、主题、分区时间、其它日志字段)this.params = {// log_source: '', // 日志来源// log_time: Math.floor(Date.now() / 1000), // 日志上报时间// log_topic: '', // 日志主题// log_partition_time: 'null', // 分区时间// log_extract_others: '', // 其他日志字段...publicParam}// 项目个性化公参this.args = {...projectArgs}}/*** 更新参数* @param publicParam 项目标准公参* @param projectArgs 项目个性化公参*/updateParam(publicParam = {}, projectArgs = {}) {this.params = {...this.params,...publicParam,}this.args = {...this.args,...projectArgs}}// 改造send方法(关键修改)send(params, args) {const sendParam = this._buildParams(params, args);if (AppState.slsReady) {this.tracker.sendImmediate(sendParam);} else {this._queue.push({type: 'single',data: sendParam});}}// 改造sendBatch方法(关键修改)sendBatch(arr) {const batchParams = arr.map(item => this._buildParams(item));if (AppState.slsReady) {this.tracker.sendBatchLogsImmediate(batchParams);} else {this._queue.push({type: 'batch',data: batchParams});}}// 新增私有方法_buildParams(params, args = {}) {let allParams = {...this.params,...params,args: JSON.stringify({...this.args,...args,...params.args,})};logFlag && console.log(`***当前${allParams.event_name}事件日志上报数据allParams***:`, allParams)return allParams}_flushQueue() {this._queue.forEach(item => {item.type === 'single' ?this.tracker.sendImmediate(item.data) :this.tracker.sendBatchLogsImmediate(item.data);});this._queue = [];}/*** 点击事件*/click(params, args) {this.send(params, args)}/*** 发送统计事件*/// send(params, args) {// //项目标准公参,埋点事件相关字段,项目个性化公参,埋点事件相关args字段// let sendParam = this._buildParams(params, args);// logFlag && console.log('发送统计事件sendParam:',sendParam)// this.tracker.sendImmediate(sendParam)// }/*** 组合发送统计事件*/// sendBatch(arr) {// let batchParams = arr.map(item => this._buildParams(item));// logFlag && console.log('组合发送统计事件batchParams:',batchParams)// this.tracker.sendBatchLogsImmediate(batchParams)// }}
// globalState.js
// 导出一个全局状态管理对象 AppState
export const AppState = {// SLS(日志服务)就绪状态标志位,默认false表示未就绪slsReady: false, // 替代旧变量 _slsReady,命名更规范// 设置SLS就绪状态的方法setSLSReady(value) {// 更新当前对象的slsReady状态this.slsReady = value;// 通过uni-app的事件总线触发全局事件// 参数说明:// - 'sls-ready': 事件名称,其他组件可监听此事件// - value: 传递的状态值(true/false)uni.$emit('sls-ready', value); }
};
// request.js
// 在ES6模块(import/export)中,顶层变量(非函数内部声明)默认具有 模块级作用域,即同一模块内的所有代码均可访问,且不会污染全局命名空间
// 保留原始请求方法引用,确保即使SDK初始化失败也能回退到原始请求
const _originalRequest = uni.request;
// SLS初始化状态标识
import {AppState
} from '@/utils/globalState.js';// 将以下:接口请求方式
uni.request({})
// 替换为
// 动态选择请求方法(核心修改)
// slsReady=false:使用原始方法(绕过SDK) slsReady=true:切换到SDK增强方法
const requestExecutor = AppState.slsReady ? uni.request : _originalRequest;
//call(uni, {...}) 上下文绑定, 显式绑定uni-app运行环境,确保请求方法中的this指向正确,避免回调函数丢失uni对象。
requestExecutor.call(uni, {})
3、自定义封装埋点事件,进行日志上报
import webTracker from './webTracker.js'const opts = {host: '', // 所在地域的服务入口。project: '', // Project名称。logstore: '' // Logstore名称。
}// 项目标准公参
const publicParam = {}// 项目个性化公参
const projectArgs = {}//new webTracker(阿里云上传参数、项目标准公参、项目个性化公参)
export const tracker = new webTracker(opts, publicParam, projectArgs)/*** 更新参数 (项目标准公参 + 项目个性化公参 )* 针对缓存本地数据,再次放入(防止埋点加载太快,没有获取到缓存数据)*/
export const updateParam = () => {tracker.updateParam({//...项目标准公参}, {//...项目个性化公参})
}/*** 发送统计事件*/
export const send = (data, args) => {updateParam()tracker.send(data, args)
}/*** 组合发送统计事件*/
export const sendBatch = (data) => {updateParam()tracker.sendBatch(data)
}/*** 点击事件*/
export const click = (data, args) => {updateParam()tracker.click(data, args)
}// 以下是事件方法:/*** 小程序启动事件*/
export const triggerLoad = (trackData) => {let data = {}let args = {}send(data, args)
}/*** 小程序退出*/
export const triggerMpend = (trackData) => {let data = {}let args = {}send(data, args)
}/*** 曝光事件*/
export const triggerExposure = (trackData) => {let data = {// ...args: {// ...}}sendBatch(data)
}/*** 批量曝光事件*/
let triggerExposureArr = []
let triggerExposureTimer = null
export function triggerExposureBatch(trackData) {let data = {// ...args: {// ....}}triggerExposureArr.push(data)if (triggerExposureTimer === null) {triggerExposureTimer = setTimeout(() => {sendBatch(triggerExposureArr)clearTimeout(triggerExposureTimer)triggerExposureTimer = nulltriggerExposureArr = []}, 500)}
}export default {tracker,// 发送统计事件send,// 点击事件click,// 组合发送统计事件sendBatch,// 更新参数 (项目标准公参 + 项目个性化公参 )updateParam,// 小程序启动事件triggerLoad,// 小程序退出triggerMpend,// 曝光事件triggerExposure,// 批量曝光事件triggerExposureBatch,
}