阿里云日志服务之WebTracking 小程序端 JavaScript SDK (阿里SDK埋点和原生uni.request请求冲突问题)

发布于:2025-09-04 ⋅ 阅读:(23) ⋅ 点赞:(0)

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 = null
			triggerExposureArr = []
		}, 500)
	}
}


export default {
	tracker,
	// 发送统计事件
	send,
	// 点击事件
	click,
	// 组合发送统计事件
	sendBatch,
	// 更新参数 (项目标准公参 + 项目个性化公参 )
	updateParam,
	// 小程序启动事件
	triggerLoad,
	// 小程序退出
	triggerMpend,
	// 曝光事件
	triggerExposure,
	// 批量曝光事件
	triggerExposureBatch,
}