You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
10 KiB
365 lines
10 KiB
|
2 months ago
|
const pkg = require('../package.json');
|
||
|
|
const BeaconAction = require('../lib/beacon_mp.min');
|
||
|
|
let beacon = null;
|
||
|
|
|
||
|
|
const getBeacon = (delay) => {
|
||
|
|
if (!beacon) {
|
||
|
|
beacon = new BeaconAction({
|
||
|
|
appkey: '0AND0VEVB24UBGDU',
|
||
|
|
versionCode: pkg.version,
|
||
|
|
channelID: 'mp_sdk', //渠道,选填
|
||
|
|
openid: 'openid', // 用户id, 选填
|
||
|
|
unionid: 'unid', //用户unionid , 类似idfv,选填
|
||
|
|
strictMode: false, //严苛模式开关, 打开严苛模式会主动抛出异常, 上线请务必关闭!!!
|
||
|
|
delay, // 普通事件延迟上报时间(单位毫秒), 默认1000(1秒),选填
|
||
|
|
sessionDuration: 60 * 1000, // session变更的时间间隔, 一个用户持续30分钟(默认值)没有任何上报则算另一次 session,每变更一次session上报一次启动事件(rqd_applaunched),使用毫秒(ms),最小值30秒,选填
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return beacon;
|
||
|
|
};
|
||
|
|
|
||
|
|
const utils = {
|
||
|
|
// 生成uid 每个链路对应唯一一条uid
|
||
|
|
getUid() {
|
||
|
|
var S4 = function () {
|
||
|
|
return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
|
||
|
|
};
|
||
|
|
return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4();
|
||
|
|
},
|
||
|
|
// 获取网络类型
|
||
|
|
getNetType() {
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
if (wx.canIUse('getNetworkType')) {
|
||
|
|
try {
|
||
|
|
wx.getNetworkType({
|
||
|
|
success(res) {
|
||
|
|
resolve(res.networkType);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
resolve('can_not_get_network_type');
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
resolve('can_not_get_network_type');
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
// 获取系统信息
|
||
|
|
getSystemInfo() {
|
||
|
|
const defaultInfo = {
|
||
|
|
devicePlatform: '',
|
||
|
|
wxVersion: '',
|
||
|
|
wxSystem: '',
|
||
|
|
wxSdkVersion: '',
|
||
|
|
};
|
||
|
|
return new Promise((resolve) => {
|
||
|
|
if (wx.canIUse('getSystemInfo')) {
|
||
|
|
try {
|
||
|
|
wx.getSystemInfo({
|
||
|
|
success(res) {
|
||
|
|
const { platform, version, system, SDKVersion } = res;
|
||
|
|
Object.assign(defaultInfo, {
|
||
|
|
devicePlatform: platform,
|
||
|
|
wxVersion: version,
|
||
|
|
wxSystem: system,
|
||
|
|
wxSdkVersion: SDKVersion,
|
||
|
|
});
|
||
|
|
resolve(defaultInfo);
|
||
|
|
},
|
||
|
|
});
|
||
|
|
} catch (e) {
|
||
|
|
resolve({
|
||
|
|
devicePlatform: 'can_not_get_system_info',
|
||
|
|
wxVersion: 'can_not_get_system_info',
|
||
|
|
wxSystem: 'can_not_get_system_info',
|
||
|
|
wxSdkVersion: 'can_not_get_system_info',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
resolve({
|
||
|
|
devicePlatform: 'can_not_get_system_info',
|
||
|
|
wxVersion: 'can_not_get_system_info',
|
||
|
|
wxSystem: 'can_not_get_system_info',
|
||
|
|
wxSdkVersion: 'can_not_get_system_info',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
});
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
// 设备信息,只取一次值
|
||
|
|
const deviceInfo = {
|
||
|
|
// ↓上报项
|
||
|
|
devicePlatform: '', // ios/anroid/windows/mac/devtools
|
||
|
|
wxVersion: '',
|
||
|
|
wxSystem: '',
|
||
|
|
wxSdkVersion: '',
|
||
|
|
};
|
||
|
|
utils.getSystemInfo().then((res) => {
|
||
|
|
Object.assign(deviceInfo, res);
|
||
|
|
});
|
||
|
|
|
||
|
|
// 分块上传原子方法
|
||
|
|
const sliceUploadMethods = [
|
||
|
|
'multipartInit',
|
||
|
|
'multipartUpload',
|
||
|
|
'multipartComplete',
|
||
|
|
'multipartList',
|
||
|
|
'multipartListPart',
|
||
|
|
'multipartAbort',
|
||
|
|
];
|
||
|
|
|
||
|
|
const uploadApi = ['putObject', 'postObject', 'appendObject', 'sliceUploadFile', 'uploadFile', 'uploadFiles'].concat(
|
||
|
|
sliceUploadMethods,
|
||
|
|
);
|
||
|
|
const downloadApi = ['getObject'];
|
||
|
|
|
||
|
|
function getEventCode(apiName) {
|
||
|
|
if (uploadApi.includes(apiName)) {
|
||
|
|
return 'cos_upload';
|
||
|
|
}
|
||
|
|
if (downloadApi.includes(apiName)) {
|
||
|
|
return 'cos_download';
|
||
|
|
}
|
||
|
|
return 'base_service';
|
||
|
|
}
|
||
|
|
|
||
|
|
// 上报参数驼峰改下划线
|
||
|
|
function camel2underline(key) {
|
||
|
|
return key.replace(/([A-Z])/g, '_$1').toLowerCase();
|
||
|
|
}
|
||
|
|
function formatParams(params) {
|
||
|
|
const formattedParams = {};
|
||
|
|
const allReporterKeys = [
|
||
|
|
'tracePlatform',
|
||
|
|
'cossdkVersion',
|
||
|
|
'region',
|
||
|
|
'networkType',
|
||
|
|
'host',
|
||
|
|
'accelerate',
|
||
|
|
'requestPath',
|
||
|
|
'size',
|
||
|
|
'httpMd5',
|
||
|
|
'httpSign',
|
||
|
|
'httpFull',
|
||
|
|
'name',
|
||
|
|
'result',
|
||
|
|
'tookTime',
|
||
|
|
'errorNode',
|
||
|
|
'errorCode',
|
||
|
|
'errorMessage',
|
||
|
|
'errorRequestId',
|
||
|
|
'errorStatusCode',
|
||
|
|
'errorServiceName',
|
||
|
|
'errorType',
|
||
|
|
'traceId',
|
||
|
|
'bucket',
|
||
|
|
'appid',
|
||
|
|
'partNumber',
|
||
|
|
'retryTimes',
|
||
|
|
'reqUrl',
|
||
|
|
'customId',
|
||
|
|
'fullError',
|
||
|
|
'devicePlatform',
|
||
|
|
'wxVersion',
|
||
|
|
'wxSystem',
|
||
|
|
'wxSdkVersion',
|
||
|
|
];
|
||
|
|
const successKeys = [
|
||
|
|
'tracePlatform',
|
||
|
|
'cossdkVersion',
|
||
|
|
'region',
|
||
|
|
'bucket',
|
||
|
|
'appid',
|
||
|
|
'networkType',
|
||
|
|
'host',
|
||
|
|
'accelerate',
|
||
|
|
'requestPath',
|
||
|
|
'partNumber',
|
||
|
|
'size',
|
||
|
|
'name',
|
||
|
|
'result',
|
||
|
|
'tookTime',
|
||
|
|
'errorRequestId',
|
||
|
|
'retryTimes',
|
||
|
|
'reqUrl',
|
||
|
|
'customId',
|
||
|
|
'devicePlatform',
|
||
|
|
'wxVersion',
|
||
|
|
'wxSystem',
|
||
|
|
'wxSdkVersion',
|
||
|
|
];
|
||
|
|
// 需要上报的参数字段
|
||
|
|
const reporterKeys = params.result === 'Success' ? successKeys : allReporterKeys;
|
||
|
|
for (let key in params) {
|
||
|
|
if (!reporterKeys.includes(key)) continue;
|
||
|
|
const formattedKey = camel2underline(key);
|
||
|
|
formattedParams[formattedKey] = params[key];
|
||
|
|
}
|
||
|
|
return formattedParams;
|
||
|
|
}
|
||
|
|
|
||
|
|
// 链路追踪器
|
||
|
|
class Tracker {
|
||
|
|
constructor(opt) {
|
||
|
|
const { parent, traceId, bucket, region, apiName, fileKey, fileSize, accelerate, customId, delay, deepTracker } =
|
||
|
|
opt;
|
||
|
|
const appid = (bucket && bucket.substr(bucket.lastIndexOf('-') + 1)) || '';
|
||
|
|
this.parent = parent;
|
||
|
|
this.deepTracker = deepTracker;
|
||
|
|
this.delay = delay;
|
||
|
|
// 上报用到的字段
|
||
|
|
this.params = {
|
||
|
|
// 通用字段
|
||
|
|
cossdkVersion: pkg.version,
|
||
|
|
region,
|
||
|
|
networkType: '',
|
||
|
|
host: '',
|
||
|
|
accelerate: accelerate ? 'Y' : 'N',
|
||
|
|
requestPath: fileKey || '',
|
||
|
|
size: fileSize || -1,
|
||
|
|
httpMd5: 0, // MD5耗时
|
||
|
|
httpSign: 0, // 计算签名耗时
|
||
|
|
httpFull: 0, // http请求耗时
|
||
|
|
name: apiName || '',
|
||
|
|
result: '', // sdk api调用结果Success、Fail
|
||
|
|
tookTime: 0, // 总耗时
|
||
|
|
errorNode: '',
|
||
|
|
errorCode: '',
|
||
|
|
errorMessage: '',
|
||
|
|
errorRequestId: '',
|
||
|
|
errorStatusCode: 0,
|
||
|
|
errorServiceName: '',
|
||
|
|
|
||
|
|
// 小程序补充字段
|
||
|
|
tracePlatform: 'cos-wx-sdk-v5', // 上报平台=小程序
|
||
|
|
traceId: traceId || utils.getUid(), // 每条上报唯一标识
|
||
|
|
bucket,
|
||
|
|
appid,
|
||
|
|
partNumber: 0, // 分块上传编号
|
||
|
|
retryTimes: 0, // sdk内部发起的请求重试
|
||
|
|
reqUrl: '', // 请求url
|
||
|
|
customId: customId || '', // 业务id
|
||
|
|
devicePlatform: deviceInfo.devicePlatform,
|
||
|
|
wxVersion: deviceInfo.wxVersion,
|
||
|
|
wxSystem: deviceInfo.wxSystem,
|
||
|
|
wxSdkVersion: deviceInfo.wxSdkVersion,
|
||
|
|
|
||
|
|
md5StartTime: 0, // md5计算开始时间
|
||
|
|
md5EndTime: 0, // md5计算结束时间
|
||
|
|
signStartTime: 0, // 计算签名开始时间
|
||
|
|
signEndTime: 0, // 计算签名结束时间
|
||
|
|
httpStartTime: 0, // 发起网络请求开始时间
|
||
|
|
httpEndTime: 0, // 网路请求结束时间
|
||
|
|
startTime: new Date().getTime(), // sdk api调用起始时间,不是纯网络耗时
|
||
|
|
endTime: 0, // sdk api调用结束时间,不是纯网络耗时
|
||
|
|
};
|
||
|
|
this.beacon = getBeacon(delay);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 格式化sdk回调
|
||
|
|
async formatResult(err, data) {
|
||
|
|
/**
|
||
|
|
* 解析到err的格式为:
|
||
|
|
* 1.服务端有返回时
|
||
|
|
* {
|
||
|
|
* err: 同下方error,
|
||
|
|
* error: {
|
||
|
|
* error: {
|
||
|
|
* Code: '', Message: '', Resource: '', RequestId: '', TraceId: '',
|
||
|
|
* },
|
||
|
|
* statusCode: xxx,
|
||
|
|
* headers: {},
|
||
|
|
* RequestId: '',
|
||
|
|
* },
|
||
|
|
* }
|
||
|
|
* 2.本地抛出或小程序直接报错
|
||
|
|
* {error: 'message'}或{error: {error: 'message' }}
|
||
|
|
*/
|
||
|
|
const now = new Date().getTime();
|
||
|
|
const tookTime = now - this.params.startTime;
|
||
|
|
const networkType = await utils.getNetType();
|
||
|
|
const errorCode = err ? err?.error?.error?.Code || 'Error' : '';
|
||
|
|
const errorMessage = err ? err?.error?.error?.Message || err?.error?.error || err?.error || '' : '';
|
||
|
|
const errorStatusCode = err ? err?.error?.statusCode : data.statusCode;
|
||
|
|
const errorServiceName = err ? err?.error?.error?.Resource : '';
|
||
|
|
const requestId = err ? err?.error?.RequestId || '' : data?.RequestId || '';
|
||
|
|
const errorType = err ? (requestId ? 'Server' : 'Client') : '';
|
||
|
|
Object.assign(this.params, {
|
||
|
|
tookTime,
|
||
|
|
networkType,
|
||
|
|
httpMd5: this.params.md5EndTime - this.params.md5StartTime,
|
||
|
|
httpSign: this.params.signEndTime - this.params.signStartTime,
|
||
|
|
httpFull: this.params.httpEndTime - this.params.httpStartTime,
|
||
|
|
result: err ? 'Fail' : 'Success',
|
||
|
|
errorType,
|
||
|
|
errorCode,
|
||
|
|
errorStatusCode,
|
||
|
|
errorMessage,
|
||
|
|
errorServiceName,
|
||
|
|
errorRequestId: requestId,
|
||
|
|
});
|
||
|
|
if (err && (!errorCode || !errorMessage)) {
|
||
|
|
// 暂存全量err一段时间 观察是否所有err格式都可被解析
|
||
|
|
this.params.fullError = err ? JSON.stringify(err) : '';
|
||
|
|
}
|
||
|
|
if (this.params.name === 'getObject') {
|
||
|
|
this.params.size = data ? data.headers && data.headers['content-length'] : -1;
|
||
|
|
}
|
||
|
|
if (this.params.reqUrl) {
|
||
|
|
try {
|
||
|
|
const exec = /^http(s)?:\/\/(.*?)\//.exec(this.params.reqUrl);
|
||
|
|
this.params.host = exec[2];
|
||
|
|
} catch (e) {
|
||
|
|
this.params.host = this.params.reqUrl;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
this.sendEvents();
|
||
|
|
}
|
||
|
|
|
||
|
|
// 设置当前链路的参数
|
||
|
|
setParams(params) {
|
||
|
|
Object.assign(this.params, params);
|
||
|
|
}
|
||
|
|
|
||
|
|
// 使用灯塔延时上报
|
||
|
|
sendEvents() {
|
||
|
|
// DeepTracker模式下才会上报分块上传内部细节
|
||
|
|
if (sliceUploadMethods.includes(this.params.name) && !this.deepTracker) {
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
const eventCode = getEventCode(this.params.name);
|
||
|
|
const formattedParams = formatParams(this.params);
|
||
|
|
|
||
|
|
// 兜底处理
|
||
|
|
if (!this.beacon) {
|
||
|
|
this.beacon = getBeacon(this.delay || 5000);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (this.params.delay === 0) {
|
||
|
|
// 实时上报
|
||
|
|
this.beacon && this.beacon.onDirectUserAction(eventCode, formattedParams);
|
||
|
|
} else {
|
||
|
|
// 周期性上报
|
||
|
|
this.beacon && this.beacon.onUserAction(eventCode, formattedParams);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// 生成子实例,与父所属一个链路,可用于分块上传内部流程上报单个分块操作
|
||
|
|
generateSubTracker(subParams) {
|
||
|
|
Object.assign(subParams, {
|
||
|
|
parent: this,
|
||
|
|
deepTracker: this.deepTracker,
|
||
|
|
traceId: this.params.traceId,
|
||
|
|
bucket: this.params.bucket,
|
||
|
|
region: this.params.region,
|
||
|
|
fileKey: this.params.requestPath,
|
||
|
|
customId: this.params.customId,
|
||
|
|
delay: this.params.delay,
|
||
|
|
});
|
||
|
|
return new Tracker(subParams);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = Tracker;
|