Browse Source

webRTC增加日志上报埋点

tangdehang 3 months ago
parent
commit
3f197167ab
2 changed files with 528 additions and 74 deletions
  1. 255 74
      static/rtcEngine/js/WXtrialInterface.js
  2. 273 0
      static/rtcEngine/js/logReport.js

+ 255 - 74
static/rtcEngine/js/WXtrialInterface.js

@@ -1,6 +1,8 @@
 
 
 import request from './request.js'
 import request from './request.js'
 import { clickCopyText, pasteText } from './common.js'
 import { clickCopyText, pasteText } from './common.js'
+import logReport from './logReport.js'
+
 // 禁止双击缩放
 // 禁止双击缩放
 document.addEventListener('dblclick', function (e) {
 document.addEventListener('dblclick', function (e) {
     e.preventDefault();
     e.preventDefault();
@@ -21,7 +23,10 @@ let noOperationSetTimeoutTimeInterval = null
 let noOperationSetIntervalTimeInterval = null
 let noOperationSetIntervalTimeInterval = null
 // 倒计时定时标识
 // 倒计时定时标识
 let countdownTimeInterval = null
 let countdownTimeInterval = null
-// let vc = new VConsole()
+
+// 日志上报实例
+let logReportObj = null;
+
 const app = new Vue({
 const app = new Vue({
     el: '#app',
     el: '#app',
     data: {
     data: {
@@ -109,7 +114,9 @@ const app = new Vue({
         this.initConfig()
         this.initConfig()
     },
     },
     mounted() {
     mounted() {
-        this.getUserCardInfo()
+        // 初始化日志上报
+        this.initLogReport();
+        this.getUserCardInfo();
     },
     },
     computed: {
     computed: {
         // 右侧弹框退出相关按钮
         // 右侧弹框退出相关按钮
@@ -153,6 +160,7 @@ const app = new Vue({
     methods: {
     methods: {
         // 初始化
         // 初始化
         initConfig() {
         initConfig() {
+            
             // 获取窗口尺寸
             // 获取窗口尺寸
             this.getInitSize()
             this.getInitSize()
             let levitatedSpherePositionData = localStorage.getItem('levitatedSpherePositionData')
             let levitatedSpherePositionData = localStorage.getItem('levitatedSpherePositionData')
@@ -167,6 +175,7 @@ const app = new Vue({
             // 给api增加需要的参数
             // 给api增加需要的参数
             request.defaults.headers.Authorization = token
             request.defaults.headers.Authorization = token
             request.defaults.headers.versionname = '5.9.0'
             request.defaults.headers.versionname = '5.9.0'
+            request.defaults.headers.merchantSign = 'SZX'
             window.onresize = () => {
             window.onresize = () => {
                 this.getInitSize()
                 this.getInitSize()
             }
             }
@@ -189,6 +198,7 @@ const app = new Vue({
                 { 'CHINANET-GD': webrtcNetwork  }, // 电信
                 { 'CHINANET-GD': webrtcNetwork  }, // 电信
                 { 'UNICOM-GD': webrtcNetwork }, // 联通
                 { 'UNICOM-GD': webrtcNetwork }, // 联通
             ];
             ];
+
             const connection = {
             const connection = {
                 name: "猪猪令是猪",
                 name: "猪猪令是猪",
                 topic, // 云机ID 必填
                 topic, // 云机ID 必填
@@ -207,6 +217,10 @@ const app = new Vue({
                 fps: 30, //必填
                 fps: 30, //必填
                 callback: this.statusCallBack,//回调函数 必填
                 callback: this.statusCallBack,//回调函数 必填
             };
             };
+
+            // 设置日志参数 推流质量
+            logReportObj.setParams({imageQuality: 6000});
+
             // 初始化 SDK
             // 初始化 SDK
             this.engine = new RtcEngineSDK(connection);
             this.engine = new RtcEngineSDK(connection);
             this.engine.RtcEngine()
             this.engine.RtcEngine()
@@ -217,6 +231,7 @@ const app = new Vue({
             if (event.type !== 'StreamStates') {
             if (event.type !== 'StreamStates') {
                 // console.log("链接的状态", event, event.val);
                 // console.log("链接的状态", event, event.val);
             }
             }
+
             switch (event.type) {
             switch (event.type) {
                 case "screenChange":
                 case "screenChange":
                     // 0:横屏 1:竖屏
                     // 0:横屏 1:竖屏
@@ -228,6 +243,15 @@ const app = new Vue({
                     // success 链接成功
                     // success 链接成功
                     const status = ["TIMEOUT", "failed"]
                     const status = ["TIMEOUT", "failed"]
                     if (status.includes(event.val)) {
                     if (status.includes(event.val)) {
+                        // 设置日志 推流状态为失败
+                        logReportObj.setParams({plugFowStatus: 2, linkWay: 1});
+                        // 日志上报
+                        logReportObj.collectLog(
+                            `信令服务地址nats失败:
+                            url: ${this.engine.options.url}
+                            消息: ${JSON.stringify(event)}`
+                        );
+                        
                         Dialog.alert({
                         Dialog.alert({
                             title: '提示',
                             title: '提示',
                             message: '链接超时',
                             message: '链接超时',
@@ -258,10 +282,35 @@ const app = new Vue({
                         return
                         return
                     }
                     }
                     if (event.val === "connecting") return
                     if (event.val === "connecting") return
+                    if (event.val === 'failed') {
+                        // 设置日志 推流状态为失败
+                        logReportObj.setParams({plugFowStatus: 2, linkWay: 1});
+                        // 日志上报
+                        logReportObj.collectLog(
+                            `rtc链接失败:
+                            消息: ${JSON.stringify(event)}`
+                        );
+                    }
+                    if(event.val === 'disconnected') {
+                        // 设置日志 推流状态为失败
+                        logReportObj.setParams({plugFowStatus: 2, linkWay: 1});
+                        // 日志上报
+                        logReportObj.collectLog(
+                            `rtc链接超时:
+                            消息: ${JSON.stringify(event)}`
+                        );
+                    }
                     this.exit()
                     this.exit()
                     break;
                     break;
                 case "AuthenticationStatus":
                 case "AuthenticationStatus":
                     // console.log(`鉴权${event.val === "success" ? '成功' : '失败'}`);
                     // console.log(`鉴权${event.val === "success" ? '成功' : '失败'}`);
+                    if (event.val !== "success") {
+                        // 日志上报
+                        logReportObj.collectLog(
+                            `rtc鉴权失败:
+                            消息: ${JSON.stringify(event)}`
+                        );
+                    }
                     break;
                     break;
                 case "StreamStates":
                 case "StreamStates":
                     // “currentRoundTripTime”:延迟 “lostRate”:丢包率 “seconds_KBytes”:带宽 “framesPerSecond”:帧率
                     // “currentRoundTripTime”:延迟 “lostRate”:丢包率 “seconds_KBytes”:带宽 “framesPerSecond”:帧率
@@ -302,6 +351,10 @@ const app = new Vue({
         definitionFun(value) {
         definitionFun(value) {
             this.definitionValue = value
             this.definitionValue = value
             this.engine.makeBitrate && this.engine.makeBitrate(value)
             this.engine.makeBitrate && this.engine.makeBitrate(value)
+
+            // 设置日志参数 推流质量
+            logReportObj.setParams({imageQuality: value});
+
             localStorage.setItem('definitionValue', value)
             localStorage.setItem('definitionValue', value)
             this.levitatedSphereVisible = false
             this.levitatedSphereVisible = false
         },
         },
@@ -362,7 +415,8 @@ const app = new Vue({
         // 业务指令
         // 业务指令
         doConnectDirectives() {
         doConnectDirectives() {
             let { internetHttps, localIp, cardToken } = this.userCardInfoData
             let { internetHttps, localIp, cardToken } = this.userCardInfoData
-            let cUrl = `wss://${internetHttps}/businessChannel?cardIp=${localIp}&token=${cardToken}&type=directives`
+            const isWss = location.protocol === 'https:'
+            let cUrl = `${isWss ? 'wss' : 'ws'}://${internetHttps}/businessChannel?cardIp=${localIp}&token=${cardToken}&type=directives`
             this.doConnectDirectivesWs = new WebSocket(cUrl);
             this.doConnectDirectivesWs = new WebSocket(cUrl);
             this.doConnectDirectivesWs.binaryType = 'arraybuffer'
             this.doConnectDirectivesWs.binaryType = 'arraybuffer'
             clearInterval(doConnectDirectivesIntervalerPing)
             clearInterval(doConnectDirectivesIntervalerPing)
@@ -379,6 +433,9 @@ const app = new Vue({
                 }, 3000)
                 }, 3000)
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getVsStatus' }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getVsStatus' }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'bitRate', data: { bitRate: 1243000 } }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'bitRate', data: { bitRate: 1243000 } }))
+                // 设置日志参数 推流质量
+                logReportObj.setParams({imageQuality: 1243000});
+
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'InputMethod', data: { type: 2 } }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'InputMethod', data: { type: 2 } }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getPhoneSize' }))
                 this.doConnectDirectivesWs.send(JSON.stringify({ type: 'getPhoneSize' }))
             }
             }
@@ -416,6 +473,16 @@ const app = new Vue({
 
 
             // 链接报错的回调
             // 链接报错的回调
             this.doConnectDirectivesWs.onerror = res => {
             this.doConnectDirectivesWs.onerror = res => {
+                // 设置日志 推流状态为失败
+                logReportObj.setParams({plugFowStatus: 1, linkWay: ''});
+                // 日志上报
+                logReportObj.collectLog(
+                    `业务指令通道报错:
+                    url: ${res.target.url}
+                    type: ${res.type}
+                    消息: ${JSON.stringify(res)}`
+                );
+
                 clearInterval(doConnectDirectivesTimerInterval)
                 clearInterval(doConnectDirectivesTimerInterval)
                 if (doConnectDirectivesRequestNum > 6) {
                 if (doConnectDirectivesRequestNum > 6) {
                     this.exit()
                     this.exit()
@@ -543,92 +610,189 @@ const app = new Vue({
             this.obtainCardInfoStartTime = + new Date()
             this.obtainCardInfoStartTime = + new Date()
             let { userCardId } = this.parametersData
             let { userCardId } = this.parametersData
             userCardId = +userCardId
             userCardId = +userCardId
+
             const statusTips = {
             const statusTips = {
+                // 5200:RBD资源挂载中
                 5200: '网络异常,请稍后重试',
                 5200: '网络异常,请稍后重试',
+                // 入使用排队9.9,年卡
                 5220: '云手机正在一键修复中',
                 5220: '云手机正在一键修复中',
                 5203: '正在排队中,请稍等',
                 5203: '正在排队中,请稍等',
-                5204: '云机异常,正在为你重新分配云机'
+                // 9.9年卡连接异常,重新进入排队
+                5204: '云机异常,正在为你重新分配云机',
+                5228: '卡的网络状态为差',
+                5229: '接口返回链接信息缺失',
             }
             }
-            request.post('/api/resources/user/cloud/connect', { userCardId }).then(async res => {
-                console.log('res', res);
-                if (!res.success) { return }
-                const { isWeixin } = this.parametersData;
-                switch (res.status) {
-                    case 0:
-                        getUserCardInfoRequestNum = 1
-                        // 不支持webRTC跳转到指定的页面
-                        if (!res.data.isWebrtc) {
-                            location.replace(`${location.origin}/h5/webRtcYJ/WXtrialInterface.html${location.search}`)
-                            return
-                        }
-                        if (!this.isSupportRtc) {
-                            Dialog.alert({
-                                title: '提示',
-                                message: `${+isWeixin ? '微信小程序' : ''}当前版本暂不支持使用,可下载谷歌浏览器或双子星客户端进行使用`,
-                                confirmButtonText: '确定',
-                                confirmButtonColor: '#3cc51f',
-                                beforeClose: (action, done) => {
-                                    this.exit()
-                                    done()
-                                }
-                            })
-                            return
-                        }
+            let { isWeixin } = this.parametersData;
+            let clientType = +isWeixin ? 'wx' : undefined;
 
 
-                        // webRtc连接,需获取连接地址
-                        if (res.data.webrtcNetworkAnalysis) {
-                            // 如果有网络分析的请求地址, 则请求,否则失败
-                            const webrtcNetworkAnalysisReq = await request.get(res.data.webrtcNetworkAnalysis);
+            // 设置上报参数
+            logReportObj.setParams({userCardId});
+            clientType && logReportObj.setParams({clientType});
 
 
-                            console.log('webrtcNetworkAnalysisReq', webrtcNetworkAnalysisReq);
+            request.post('/api/resources/user/cloud/connect', { userCardId }).then(async res => {
+                try {
+                    if (!res.success) {
+                        // 设置日志 推流状态为失败,和链接状态
+                        logReportObj.setParams({plugFowStatus: 2, linkWay: logReportObj.RESPONSE_CODE[res.status] || ''});
 
 
-                            if (webrtcNetworkAnalysisReq !== null && webrtcNetworkAnalysisReq.success) {
-                                if (webrtcNetworkAnalysisReq.data) {
-                                    // 保存获取的连接地址到上个请求的响应中, 方便后面使用
-                                    res.data.webrtcNetwork = webrtcNetworkAnalysisReq.data;
-                                }
+                        // 日志上报
+                        logReportObj.collectLog(
+                            `接口获取数据失败:
+                            url: /api/resources/user/cloud/connect
+                            method: post
+                            参数: ${JSON.stringify({ userCardId })}
+                            响应: ${JSON.stringify(res)}`
+                        );
+                        return;
+                    }
+    
+                    const data = res.data;
+    
+                    // 设置上报参数
+                    logReportObj.setParams({videoType: data.videoCode, resourceId: data.resourceId});
+    
+                    switch (res.status) {
+                        case 0:
+                            getUserCardInfoRequestNum = 1
+                            // 不支持webRTC跳转到指定的页面
+                            if (!res.data.isWebrtc) {
+                                // 关闭日志上报
+                                logReportObj.destroy();
+    
+                                // 跳转指定页面
+                                location.replace(`${location.origin}/h5/webRtcYJ/WXtrialInterface.html${location.search}`)
+                                return
                             }
                             }
-                        }
-                        
-                        // 如果没有获取到连接地址, 则失败
-                        if(!res.data.webrtcNetwork) {
-                            Dialog.alert({
-                                title: '提示',
-                                message: '访问失败,请稍后重试',
-                                confirmButtonText: '确定',
-                                confirmButtonColor: '#3cc51f',
-                                beforeClose: (action, done) => {
-                                    this.exit()
-                                    done()
+    
+                            if (!this.isSupportRtc) {
+                                // 设置日志 推流状态为失败
+                                logReportObj.setParams({plugFowStatus: 2});
+                                // 日志上报
+                                logReportObj.collectLog(`${+isWeixin ? '微信小程序' : ''}当前版本暂不支持使用`);
+    
+                                Dialog.alert({
+                                    title: '提示',
+                                    message: `${+isWeixin ? '微信小程序' : ''}当前版本暂不支持使用,可下载谷歌浏览器或双子星客户端进行使用`,
+                                    confirmButtonText: '确定',
+                                    confirmButtonColor: '#3cc51f',
+                                    beforeClose: (action, done) => {
+                                        this.exit()
+                                        done()
+                                    }
+                                })
+                                return
+                            }
+    
+                            // webRtc连接,需获取连接中转地址
+                            if (res.data.webrtcNetworkAnalysis) {
+                                // 如果有网络分析的请求地址, 则请求,否则失败
+                                const webrtcNetworkAnalysisReq = await request.get(res.data.webrtcNetworkAnalysis);
+    
+                                if (webrtcNetworkAnalysisReq !== null && webrtcNetworkAnalysisReq.success) {
+                                    if (webrtcNetworkAnalysisReq.data) {
+                                        // 保存获取的连接地址到上个请求的响应中, 方便后面使用
+                                        res.data.webrtcNetwork = webrtcNetworkAnalysisReq.data;
+                                        
+                                        // 设置上报参数
+                                        logReportObj.setParams({transferServerIp: webrtcNetworkAnalysisReq.data});
+                                    }else{
+                                        // 设置上报参数
+                                        logReportObj.setParams({linkWay: 4, plugFowStatus: 2});
+                                        // 日志上报
+                                        logReportObj.collectLog(
+                                            `webRtc连接,获取中转地址成功,但返回数据为空:
+                                            url: ${res.data.webrtcNetworkAnalysis}
+                                            method: get
+                                            参数: 无
+                                            响应: ${JSON.stringify(webrtcNetworkAnalysisReq)}`
+                                        );
+                                    }
+                                }else{
+                                    // 设置上报参数
+                                    logReportObj.setParams({linkWay: 4, plugFowStatus: 2});
+                                    // 日志上报
+                                    logReportObj.collectLog(
+                                        `webRtc连接,获取中转地址失败:
+                                        url: ${res.data.webrtcNetworkAnalysis}
+                                        method: get
+                                        参数: 无
+                                        响应: ${JSON.stringify(webrtcNetworkAnalysisReq)}`
+                                    );
                                 }
                                 }
-                            })
-                            return;
-                        }
-
-                        this.userCardInfoData = res.data
-                        // let ms = + new Date() - this.obtainCardInfoStartTime
-                        this.connectWebRtc()
-                        return
-                    case 5200:
-                    case 5220:
-                    case 5203:
-                    case 5204:
-                        if (res.status === 5200) {
-                            reconnect.bind(this)()
+                            }else{
+                                // 设置上报参数
+                                logReportObj.setParams({linkWay: 4, plugFowStatus: 2});
+                                // 日志上报
+                                logReportObj.collectLog(
+                                    `webRtc连接,获取请求中转地址为空:
+                                    url: /api/resources/user/cloud/connect
+                                    method: post
+                                    参数: ${JSON.stringify({ userCardId })}
+                                    响应: ${JSON.stringify(res)}`
+                                );
+                            }
+                            
+                            // 如果没有获取到连接地址, 则失败
+                            if(!res.data.webrtcNetwork) {
+                                Dialog.alert({
+                                    title: '提示',
+                                    message: '访问失败,请稍后重试',
+                                    confirmButtonText: '确定',
+                                    confirmButtonColor: '#3cc51f',
+                                    beforeClose: (action, done) => {
+                                        this.exit()
+                                        done()
+                                    }
+                                })
+                                return;
+                            }
+    
+                            this.userCardInfoData = res.data
+                            // let ms = + new Date() - this.obtainCardInfoStartTime
+                            this.connectWebRtc()
                             return
                             return
-                        }
-                        Toast(statusTips[res.status])
-                        break
-                    default:
-                        Toast('画面异常,请重新进入')
-                        break
+                        case 5200:
+                        case 5220:
+                        case 5203:
+                        case 5204:
+                            if (res.status === 5200) {
+                                reconnect.bind(this)()
+                                return
+                            }
+                            Toast(statusTips[res.status])
+                            break
+                        default:
+                            // 设置上报参数
+                            logReportObj.setParams({linkWay: logReportObj.RESPONSE_CODE[res.status] || ''});
+                            // 日志上报
+                            logReportObj.collectLog(statusTips[res.status]);
+                            
+                            Toast('画面异常,请重新进入')
+                            break
+                    }
+                    setTimeout(() => {
+                        this.exit()
+                    }, 3000)
+                    
+                } catch (error) {
+
                 }
                 }
-                setTimeout(() => {
-                    this.exit()
-                }, 3000)
+            }).catch((error) => {
+                // 设置上报参数
+                logReportObj.setParams({linkWay: 4, plugFowStatus: 2});
+                // 日志上报
+                logReportObj.collectLog(
+                    `接口获取数据失败:
+                    url: /api/resources/user/cloud/connect
+                    method: post
+                    参数: ${JSON.stringify({ userCardId })}
+                    响应: ${JSON.stringify(error)}`
+                );
             })
             })
 
 
+            // 重连
             function reconnect() {
             function reconnect() {
+                // 重连6次结束
                 if (getUserCardInfoRequestNum > 6) {
                 if (getUserCardInfoRequestNum > 6) {
                     Toast('网络异常,请稍后重试')
                     Toast('网络异常,请稍后重试')
                     clearTimeout(getUserCardInfoTimerInterval)
                     clearTimeout(getUserCardInfoTimerInterval)
@@ -638,6 +802,7 @@ const app = new Vue({
                     return
                     return
                 }
                 }
 
 
+                // 重连
                 getUserCardInfoTimerInterval = setTimeout(() => {
                 getUserCardInfoTimerInterval = setTimeout(() => {
                     this.getUserCardInfo()
                     this.getUserCardInfo()
                     getUserCardInfoRequestNum++
                     getUserCardInfoRequestNum++
@@ -657,6 +822,8 @@ const app = new Vue({
 
 
         // 退出功能
         // 退出功能
         exit() {
         exit() {
+            // 关闭日志上报
+            logReportObj.destroy();
             this.engine.disconnect && this.engine.disconnect();
             this.engine.disconnect && this.engine.disconnect();
             this.doConnectDirectivesWs && this.doConnectDirectivesWs.close()
             this.doConnectDirectivesWs && this.doConnectDirectivesWs.close()
             uni.reLaunch({
             uni.reLaunch({
@@ -805,6 +972,20 @@ const app = new Vue({
         volumeControl(value) {
         volumeControl(value) {
             this.engine.ExexuteKeyBoard && this.engine.ExexuteKeyBoard(value)
             this.engine.ExexuteKeyBoard && this.engine.ExexuteKeyBoard(value)
             this.$refs.rtcMediaPlayer && (this.$refs.rtcMediaPlayer.muted = false)
             this.$refs.rtcMediaPlayer && (this.$refs.rtcMediaPlayer.muted = false)
+        },
+
+        // 初始化日志上报实例
+        initLogReport() {
+            let clientType;
+            uni.getEnv((res)=>{
+                console.log('当前环境:', res, );
+                clientType = Object.entries(res)?.[0];
+            })
+
+            // 初始化日志上报实例
+            logReportObj = new logReport({request});
+            // 设置上报参数
+            logReportObj.setParams({clientType: clientType[0]});
         }
         }
     }
     }
 })
 })

+ 273 - 0
static/rtcEngine/js/logReport.js

@@ -0,0 +1,273 @@
+/**
+ * @description 日志上报
+ */
+
+export default class LogReport {
+  URL_API = 'http://www.androidscloud.com:8002';
+  URL_SWITCH = '/api/public/v5/log/card/getLinkLogReportSwitch'; // 日志开关查询接口地址
+  URL_ADDRESS = '/api/public/v5/log/card/reportCardLinkLog';  // 日志上报地址
+  
+  $Request = null; // 用于发送 HTTP 请求的对象
+  version = ''; // 版本号 可通过$Request版本获取
+
+  // 上报参数
+  paramsJson = {
+    'clientVersion': '', // 客户端版本号 // 通过$Request版本获取
+    'clientType': '', // 客户端类型 // 目前判断出wx或h5
+    'phoneModel': '', // 手机型号 // 无法获取
+    'phoneSystemVersion': '', // 手机系统版本号 // 无法获取
+    'phoneNetwork': '', // 手机网络类型 // 无法获取
+    'videoType': '', // 视频类型 H265 // 可以获取
+    'imageQuality': '', // 推流质量 [高清 | 流畅] // 可以判断
+    'userCardId': '', // 可以获取
+    'cardInfoId': '', // 无法获取
+    'resourceId': '', // 资源ID // 可以获取
+    'transferServerIp': '', // 中转服务器IP // 无法获取
+    'linkStartTime': '', // 链接开始时间 格式 yyyy-MM-dd HH:mm:ss
+    'linkEndTime': '', // 链接结束时间 格式 yyyy-MM-dd HH:mm:ss
+    // 'linkTime': '', // 链接时间 格式 yyyy-MM-dd HH:mm:ss
+    'linkScene': '直连', // 链接场景[直连 | 重连] // 可以判断
+    'linkWay': 1, // 链接方式(1:中转链接、2:打洞链接、3:安卓卡网络状态差、4:接口返回链接信息缺失)// 可以判断
+    'plugFowStatus': '', // 推流状态 int: 1 成功 2:失败 // 可以判断
+    'logContent': '' // 日志内容
+  };
+  
+  reportSwitchStatus = false; // 上报日志开关状态
+  logs = []; // 用于存储日志的数组
+
+  // 客户端类型 枚举值
+  CLIENT_TYPE = Object.freeze({
+    'android': 1,
+    'ios': 2,
+    'pc': 3,
+    'wx': 5, // wx小程序
+    'h5': 7,
+  });
+
+  // 连接状态 枚举值
+  LINK_WAY = Object.freeze({
+    1: '中转连接',
+    2: '打洞连接',
+    3: '安卓卡网络状态差',
+    4: '接口返回链接信息缺失',
+  });
+
+  // 码率 枚举值
+  BITRATE = Object.freeze({
+    1800: '标清',
+    2200: '标清',
+    2800: '标清',
+    6000: '高清',
+    1243000: '超清',
+  });
+
+  // 接口响应码 枚举值 对应 linkWay字段状态
+  RESPONSE_CODE = Object.freeze({
+    5200: 3, // RBD资源挂载中
+    5220: 3, // 云手机正在一键修复中
+    5203: 3, // 入使用排队9.9,年卡
+    5204: 3, // 9.9年卡连接异常,重新进入排队
+    5228: 3, // '卡的网络状态为差'
+    5229: 4, // '接口返回链接信息缺失'
+  });
+  
+  maxLogs = 1; // 存储最大日志数量
+  timer = null; // 定时器
+  timerTime = 6000; // // 日志上报间隔时间
+
+	/**
+	 * 构造函数,初始化 LogReport 类的实例
+	 * @param {Object} $Request - 用于发送 HTTP 请求的对象
+	 * @param {Object} opt - 可选的配置对象
+	 */
+	constructor(opt) {
+
+    /**
+      // 扩展API是否准备好,如果没有则监听“plusready"事件
+      // if (window.plus) {
+      //   this.plusReady()
+      // } else {
+      //   document.addEventListener('plusready', this.plusReady, false) 
+      // }
+
+      document.addEventListener('plusready', ()=> {
+        console.log('plusReady')
+        console.log(plus)
+        console.log(plus.device.model)
+        console.log(plus.networkinfo.getCurrentType())
+        // plus.device.model
+        // plus.networkinfo.getCurrentType()
+        // plus.device.networkinfo.getCurrentType()
+      }, false);
+    */
+  
+		// 初始化 $Request 属性
+		this.$Request = opt.request;
+    this.version = this.$Request.defaults.headers.versionname;
+
+		this.init();
+	}
+
+  // 初始化
+  async init() {
+    // 调用 checkSwitch 方法,检查后端上报日志开关是否打开
+		await this.checkSwitch();
+
+    // 创建定时器
+    // await this.createTimer();
+  }
+
+	/**
+	 * 检查后端上报日志开关是否打开
+	*/
+	async checkSwitch() {
+		try{
+			const res = await this.getLinkLogReportSwitch();
+      if(res.status === 0 && res.success){
+        let { cardLinkRodeSwitch } = res.data;
+        this.reportSwitchStatus = cardLinkRodeSwitch;
+      }else{
+        console.error('检查日志上报开关失败')
+      }
+		}catch(e){
+			console.error(e)
+		}
+	}
+
+  /**
+   * 查询webRTC日志上报开关状态
+   * 无参
+   * @return {Request} 
+   * {
+   *  "status": 0,
+   *  "msg": "",
+   *  "data": {
+   *    "cardLinkRodeSwitch":true, // 是否上报记录
+   *    "cardLinkLogFileSwitch":true // 是否收集错误日志
+   *	}
+  * }
+  */ 
+  getLinkLogReportSwitch() {
+    return this.$Request.get(this.URL_API + this.URL_SWITCH);
+  }
+
+  // 采集日志, 等待日志收集到一定数量或一定时间后再上报
+  collectLog(log) {
+    try {
+      // 日志开关关闭,直接返回
+      if(!this.reportSwitchStatus) {return;}
+  
+      // 组装本次日志上报参数
+      let logData = this.combinativeParam();
+      logData.logContent = log;
+
+      // 设置本次日志上报时间
+      const date = new Date();
+      let formattedDate = `${date.getFullYear()}-${('0' + (date.getMonth() + 1)).slice(-2)}-${('0' + date.getDate()).slice(-2)} ${('0' + date.getHours()).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}:${('0' + date.getSeconds()).slice(-2)}`;
+      logData.linkStartTime = formattedDate;
+  
+      this.logs.push(logData);
+  
+      // 超过最大日志数量,上报日志
+      if(this.logs.length >= this.maxLogs && this.reportSwitchStatus){
+        this.report();
+  
+        // 重置定时器
+        // this.createTimer();
+      }
+    } catch (error) {
+      console.error(error);
+    }
+  }
+
+  // 设置日志上报参数
+  setParams(obj) {
+    try {
+      // 合并参数
+      this.paramsJson = {
+        ...this.paramsJson,
+        ...obj
+      }
+    } catch (error) {
+      console.error(error);
+    }
+  }
+
+  // 组装日志上报固定参数
+  combinativeParam() {
+    try {
+      let params = {
+        ...this.paramsJson,
+      };
+  
+      params.clientVersion = this.version;
+  
+      // 客户端类型 枚举值赋值
+      params.clientType = this.enumAssignment(this.CLIENT_TYPE, this.paramsJson.clientType);
+  
+      params.imageQuality = this.enumAssignment(this.BITRATE, this.paramsJson.imageQuality);
+  
+      params.linkWay = this.enumAssignment(this.BITRATE, this.paramsJson.linkWay);
+  
+      return params;
+    } catch (error) {
+      console.error(error);
+    }
+  }
+
+  // 日志记录上报 字符串日志上报
+  report() {
+    this.logs.forEach(() => {
+      this.$Request.post(this.URL_API + this.URL_ADDRESS, { ...this.logs.shift() })
+      .then(res => {
+        console.log('日志上报成功', res);
+      });
+    })
+  }
+
+  // 生成or重置定时器
+  async createTimer() {  	
+    await this.clearTimer();
+
+    if(this.reportSwitchStatus){
+      this.timer = setInterval(() => {
+        if(this.logs.length > 0){
+          this.report();
+        }
+      }, this.timerTime);
+    }
+  }
+
+  // 清空定时器
+  async clearTimer() {
+    this.timer && clearInterval(this.timer);
+    return true;
+  } 
+
+  // 检查是否为枚举值
+  isCheckEnum(enumObj, velue) {
+    return Object.values(enumObj).includes(velue);
+  }
+
+  // 使用枚举值赋值
+  enumAssignment(enumObj, velue){
+    let str = '';
+
+    if(velue.toString() !== '') {
+      // 判断是否已为枚举值
+      str = this.isCheckEnum(enumObj, velue) ? velue : enumObj[velue];
+    }
+
+    return str;
+  }
+
+  // 关闭销毁
+  async destroy() {
+    // 清空日志
+    this.logs = [];
+    // 关闭日志上报开关
+    this.reportSwitchStatus = false;
+    // 清空定时器
+    await this.clearTimer();
+  }
+}