// 性能监控管理器 - 微信小程序专用 // 监控应用性能、内存使用、网络请求等关键指标 /** * 性能监控管理器 * 功能: * 1. 页面性能监控 * 2. 内存使用监控 * 3. 网络请求监控 * 4. 错误监控和上报 * 5. 性能数据分析 * 6. 性能优化建议 */ class PerformanceMonitor { constructor() { this.isInitialized = false; // 性能配置 this.config = { // 监控开关 enabled: true, // 采样率 (0-1) sampleRate: 0.1, // 性能阈值 thresholds: { pageLoadTime: 3000, // 页面加载时间阈值 (ms) apiResponseTime: 5000, // API响应时间阈值 (ms) memoryUsage: 100, // 内存使用阈值 (MB) errorRate: 0.05, // 错误率阈值 (5%) crashRate: 0.01 // 崩溃率阈值 (1%) }, // 上报配置 reporting: { enabled: true, endpoint: '/api/v1/performance/report', batchSize: 10, interval: 30000 // 30秒上报一次 } }; // 性能数据 this.performanceData = { pageMetrics: new Map(), // 页面性能指标 apiMetrics: new Map(), // API性能指标 errorMetrics: new Map(), // 错误指标 memoryMetrics: [], // 内存使用指标 userMetrics: new Map() // 用户行为指标 }; // 监控状态 this.monitoringState = { startTime: Date.now(), sessionId: this.generateSessionId(), userId: null, deviceInfo: null, networkType: 'unknown' }; // 待上报数据 this.pendingReports = []; // 定时器 this.reportTimer = null; this.memoryTimer = null; this.init(); } // 初始化性能监控 async init() { if (this.isInitialized || !this.config.enabled) return; console.log('⚡ 初始化性能监控...'); try { // 获取设备信息 await this.getDeviceInfo(); // 获取网络类型 await this.getNetworkType(); // 获取用户ID this.monitoringState.userId = wx.getStorageSync('userId') || 'anonymous'; // 启动内存监控 this.startMemoryMonitoring(); // 启动上报定时器 this.startReportTimer(); // 监听应用生命周期 this.setupAppLifecycleListeners(); // 监听网络状态变化 this.setupNetworkListeners(); this.isInitialized = true; console.log('✅ 性能监控初始化完成'); } catch (error) { console.error('❌ 性能监控初始化失败:', error); } } // ⚡ ===== 页面性能监控 ===== // 开始页面性能监控 startPageMonitoring(pagePath) { if (!this.config.enabled) return; const pageId = this.generatePageId(pagePath); const startTime = Date.now(); this.performanceData.pageMetrics.set(pageId, { pagePath: pagePath, startTime: startTime, loadTime: null, renderTime: null, interactiveTime: null, memoryUsage: null, errors: [], userActions: [] }); console.log('⚡ 开始页面性能监控:', pagePath); return pageId; } // 结束页面性能监控 endPageMonitoring(pageId, metrics = {}) { if (!this.config.enabled || !this.performanceData.pageMetrics.has(pageId)) return; const pageMetric = this.performanceData.pageMetrics.get(pageId); const endTime = Date.now(); // 更新性能指标 pageMetric.loadTime = endTime - pageMetric.startTime; pageMetric.renderTime = metrics.renderTime || null; pageMetric.interactiveTime = metrics.interactiveTime || null; pageMetric.memoryUsage = this.getCurrentMemoryUsage(); // 检查性能阈值 this.checkPagePerformance(pageMetric); // 添加到待上报数据 this.addToReport('page_performance', pageMetric); console.log('⚡ 页面性能监控结束:', pageMetric.pagePath, `${pageMetric.loadTime}ms`); } // 记录用户操作 recordUserAction(pageId, action, data = {}) { if (!this.config.enabled || !this.performanceData.pageMetrics.has(pageId)) return; const pageMetric = this.performanceData.pageMetrics.get(pageId); pageMetric.userActions.push({ action: action, timestamp: Date.now(), data: data }); } // 🌐 ===== API性能监控 ===== // 开始API请求监控 startApiMonitoring(url, method = 'GET') { if (!this.config.enabled) return null; const requestId = this.generateRequestId(); const startTime = Date.now(); this.performanceData.apiMetrics.set(requestId, { url: url, method: method, startTime: startTime, endTime: null, responseTime: null, statusCode: null, success: null, errorMessage: null, requestSize: null, responseSize: null }); return requestId; } // 结束API请求监控 endApiMonitoring(requestId, result = {}) { if (!this.config.enabled || !this.performanceData.apiMetrics.has(requestId)) return; const apiMetric = this.performanceData.apiMetrics.get(requestId); const endTime = Date.now(); // 更新API指标 apiMetric.endTime = endTime; apiMetric.responseTime = endTime - apiMetric.startTime; apiMetric.statusCode = result.statusCode || null; apiMetric.success = result.success || false; apiMetric.errorMessage = result.errorMessage || null; apiMetric.requestSize = result.requestSize || null; apiMetric.responseSize = result.responseSize || null; // 检查API性能 this.checkApiPerformance(apiMetric); // 添加到待上报数据 this.addToReport('api_performance', apiMetric); console.log('⚡ API性能监控:', apiMetric.url, `${apiMetric.responseTime}ms`); } // 📊 ===== 内存监控 ===== // 启动内存监控 startMemoryMonitoring() { if (!this.config.enabled) return; this.memoryTimer = setInterval(() => { this.collectMemoryMetrics(); }, 10000); // 每10秒收集一次内存数据 } // 收集内存指标 collectMemoryMetrics() { try { // 使用新的API替代已弃用的wx.getSystemInfoSync const deviceInfo = wx.getDeviceInfo(); const memoryInfo = { system: deviceInfo.memorySize || 'unknown' }; const currentMemory = this.getCurrentMemoryUsage(); const memoryMetric = { timestamp: Date.now(), totalMemory: memoryInfo.system || 'unknown', usedMemory: currentMemory, availableMemory: memoryInfo.system ? (memoryInfo.system - currentMemory) : 'unknown', memoryWarning: currentMemory > this.config.thresholds.memoryUsage }; this.performanceData.memoryMetrics.push(memoryMetric); // 保持最近100条记录 if (this.performanceData.memoryMetrics.length > 100) { this.performanceData.memoryMetrics.shift(); } // 检查内存使用 if (memoryMetric.memoryWarning) { this.handleMemoryWarning(memoryMetric); } } catch (error) { console.error('❌ 收集内存指标失败:', error); } } // 获取当前内存使用 getCurrentMemoryUsage() { try { // 微信小程序没有直接的内存API,使用估算方法 const pages = getCurrentPages(); const cacheSize = this.estimateCacheSize(); // 估算内存使用 (页面数 * 5MB + 缓存大小) return pages.length * 5 + cacheSize; } catch (error) { return 0; } } // 估算缓存大小 estimateCacheSize() { try { const storageInfo = wx.getStorageInfoSync(); return Math.round(storageInfo.currentSize / 1024); // 转换为MB } catch (error) { return 0; } } // 🚨 ===== 错误监控 ===== // 记录错误 recordError(error, context = {}) { if (!this.config.enabled) return; const errorId = this.generateErrorId(); const errorMetric = { errorId: errorId, timestamp: Date.now(), message: error.message || error.toString(), stack: error.stack || null, type: error.name || 'UnknownError', context: context, userId: this.monitoringState.userId, sessionId: this.monitoringState.sessionId, pagePath: this.getCurrentPagePath(), deviceInfo: this.monitoringState.deviceInfo, networkType: this.monitoringState.networkType }; this.performanceData.errorMetrics.set(errorId, errorMetric); // 添加到待上报数据 this.addToReport('error', errorMetric); console.error('🚨 错误记录:', errorMetric); } // 记录崩溃 recordCrash(crashInfo) { if (!this.config.enabled) return; const crashMetric = { timestamp: Date.now(), crashInfo: crashInfo, userId: this.monitoringState.userId, sessionId: this.monitoringState.sessionId, deviceInfo: this.monitoringState.deviceInfo, memoryUsage: this.getCurrentMemoryUsage(), recentErrors: Array.from(this.performanceData.errorMetrics.values()).slice(-5) }; // 立即上报崩溃数据 this.reportImmediately('crash', crashMetric); console.error('💥 崩溃记录:', crashMetric); } // 📈 ===== 性能分析 ===== // 检查页面性能 checkPagePerformance(pageMetric) { const warnings = []; if (pageMetric.loadTime > this.config.thresholds.pageLoadTime) { warnings.push(`页面加载时间过长: ${pageMetric.loadTime}ms`); } if (pageMetric.memoryUsage > this.config.thresholds.memoryUsage) { warnings.push(`内存使用过高: ${pageMetric.memoryUsage}MB`); } if (warnings.length > 0) { console.warn('⚠️ 页面性能警告:', pageMetric.pagePath, warnings); this.addToReport('performance_warning', { type: 'page', pagePath: pageMetric.pagePath, warnings: warnings, metrics: pageMetric }); } } // 检查API性能 checkApiPerformance(apiMetric) { const warnings = []; if (apiMetric.responseTime > this.config.thresholds.apiResponseTime) { warnings.push(`API响应时间过长: ${apiMetric.responseTime}ms`); } if (!apiMetric.success) { warnings.push(`API请求失败: ${apiMetric.errorMessage}`); } if (warnings.length > 0) { console.warn('⚠️ API性能警告:', apiMetric.url, warnings); this.addToReport('performance_warning', { type: 'api', url: apiMetric.url, warnings: warnings, metrics: apiMetric }); } } // 处理内存警告 handleMemoryWarning(memoryMetric) { console.warn('⚠️ 内存使用警告:', memoryMetric); // 触发内存清理 this.triggerMemoryCleanup(); // 上报内存警告 this.addToReport('memory_warning', memoryMetric); } // 触发内存清理 triggerMemoryCleanup() { try { // 清理过期缓存 this.cleanupExpiredCache(); // 清理性能数据 this.cleanupPerformanceData(); // 通知应用进行内存清理 wx.triggerGC && wx.triggerGC(); console.log('🧹 内存清理完成'); } catch (error) { console.error('❌ 内存清理失败:', error); } } // 🔧 ===== 工具方法 ===== // 生成会话ID generateSessionId() { return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 生成页面ID generatePageId(pagePath) { return `page_${pagePath.replace(/\//g, '_')}_${Date.now()}`; } // 生成请求ID generateRequestId() { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 生成错误ID generateErrorId() { return `err_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } // 获取当前页面路径 getCurrentPagePath() { try { const pages = getCurrentPages(); const currentPage = pages[pages.length - 1]; return currentPage ? currentPage.route : 'unknown'; } catch (error) { return 'unknown'; } } // 获取设备信息 async getDeviceInfo() { try { // 使用新的API替代已弃用的wx.getSystemInfoSync const deviceInfo = wx.getDeviceInfo(); const windowInfo = wx.getWindowInfo(); const appBaseInfo = wx.getAppBaseInfo(); this.monitoringState.deviceInfo = { brand: deviceInfo.brand, model: deviceInfo.model, system: deviceInfo.system, platform: deviceInfo.platform, version: appBaseInfo.version, SDKVersion: appBaseInfo.SDKVersion, screenWidth: windowInfo.screenWidth, screenHeight: windowInfo.screenHeight, pixelRatio: windowInfo.pixelRatio }; } catch (error) { console.error('❌ 获取设备信息失败,使用兜底方案:', error); // 兜底方案 try { const systemInfo = wx.getSystemInfoSync(); this.monitoringState.deviceInfo = { brand: systemInfo.brand, model: systemInfo.model, system: systemInfo.system, platform: systemInfo.platform, version: systemInfo.version, SDKVersion: systemInfo.SDKVersion, screenWidth: systemInfo.screenWidth, screenHeight: systemInfo.screenHeight, pixelRatio: systemInfo.pixelRatio }; } catch (fallbackError) { console.error('❌ 兜底方案也失败了:', fallbackError); this.monitoringState.deviceInfo = { brand: 'unknown', model: 'unknown', system: 'unknown', platform: 'unknown', version: 'unknown', SDKVersion: 'unknown', screenWidth: 375, screenHeight: 667, pixelRatio: 2 }; } } } // 获取网络类型 async getNetworkType() { try { const networkInfo = await new Promise((resolve, reject) => { wx.getNetworkType({ success: resolve, fail: reject }); }); this.monitoringState.networkType = networkInfo.networkType; } catch (error) { console.error('❌ 获取网络类型失败:', error); } } // 设置应用生命周期监听 setupAppLifecycleListeners() { // 监听应用隐藏 wx.onAppHide(() => { this.addToReport('app_lifecycle', { event: 'hide', timestamp: Date.now(), sessionDuration: Date.now() - this.monitoringState.startTime }); }); // 监听应用显示 wx.onAppShow(() => { this.addToReport('app_lifecycle', { event: 'show', timestamp: Date.now() }); }); } // 设置网络状态监听 setupNetworkListeners() { wx.onNetworkStatusChange((res) => { this.monitoringState.networkType = res.networkType; this.addToReport('network_change', { networkType: res.networkType, isConnected: res.isConnected, timestamp: Date.now() }); }); } // 添加到上报队列 addToReport(type, data) { if (!this.config.reporting.enabled) return; // 采样控制 if (Math.random() > this.config.sampleRate) return; this.pendingReports.push({ type: type, data: data, timestamp: Date.now(), sessionId: this.monitoringState.sessionId, userId: this.monitoringState.userId }); // 检查是否需要立即上报 if (this.pendingReports.length >= this.config.reporting.batchSize) { this.reportData(); } } // 立即上报 async reportImmediately(type, data) { if (!this.config.reporting.enabled) return; try { const reportData = { type: type, data: data, timestamp: Date.now(), sessionId: this.monitoringState.sessionId, userId: this.monitoringState.userId, deviceInfo: this.monitoringState.deviceInfo }; // 这里应该调用实际的上报API console.log('📊 立即上报性能数据:', reportData); } catch (error) { console.error('❌ 立即上报失败:', error); } } // 启动上报定时器 startReportTimer() { if (!this.config.reporting.enabled) return; this.reportTimer = setInterval(() => { if (this.pendingReports.length > 0) { this.reportData(); } }, this.config.reporting.interval); } // 上报数据 async reportData() { if (!this.config.reporting.enabled || this.pendingReports.length === 0) return; try { const reports = this.pendingReports.splice(0, this.config.reporting.batchSize); // 这里应该调用实际的上报API console.log('📊 批量上报性能数据:', reports.length, '条'); // 模拟API调用 // await apiClient.request({ // url: this.config.reporting.endpoint, // method: 'POST', // data: { // reports: reports, // deviceInfo: this.monitoringState.deviceInfo, // sessionInfo: { // sessionId: this.monitoringState.sessionId, // startTime: this.monitoringState.startTime, // userId: this.monitoringState.userId // } // } // }); } catch (error) { console.error('❌ 上报性能数据失败:', error); // 上报失败,将数据重新加入队列 // this.pendingReports.unshift(...reports); } } // 清理过期缓存 cleanupExpiredCache() { try { // 清理过期的性能数据 const now = Date.now(); const expireTime = 24 * 60 * 60 * 1000; // 24小时 // 清理页面指标 for (const [key, value] of this.performanceData.pageMetrics) { if (now - value.startTime > expireTime) { this.performanceData.pageMetrics.delete(key); } } // 清理API指标 for (const [key, value] of this.performanceData.apiMetrics) { if (now - value.startTime > expireTime) { this.performanceData.apiMetrics.delete(key); } } // 清理错误指标 for (const [key, value] of this.performanceData.errorMetrics) { if (now - value.timestamp > expireTime) { this.performanceData.errorMetrics.delete(key); } } } catch (error) { console.error('❌ 清理过期缓存失败:', error); } } // 清理性能数据 cleanupPerformanceData() { try { // 保留最近的数据 const maxPageMetrics = 50; const maxApiMetrics = 100; const maxErrorMetrics = 50; // 清理页面指标 if (this.performanceData.pageMetrics.size > maxPageMetrics) { const entries = Array.from(this.performanceData.pageMetrics.entries()); entries.sort((a, b) => b[1].startTime - a[1].startTime); this.performanceData.pageMetrics.clear(); entries.slice(0, maxPageMetrics).forEach(([key, value]) => { this.performanceData.pageMetrics.set(key, value); }); } // 清理API指标 if (this.performanceData.apiMetrics.size > maxApiMetrics) { const entries = Array.from(this.performanceData.apiMetrics.entries()); entries.sort((a, b) => b[1].startTime - a[1].startTime); this.performanceData.apiMetrics.clear(); entries.slice(0, maxApiMetrics).forEach(([key, value]) => { this.performanceData.apiMetrics.set(key, value); }); } // 清理错误指标 if (this.performanceData.errorMetrics.size > maxErrorMetrics) { const entries = Array.from(this.performanceData.errorMetrics.entries()); entries.sort((a, b) => b[1].timestamp - a[1].timestamp); this.performanceData.errorMetrics.clear(); entries.slice(0, maxErrorMetrics).forEach(([key, value]) => { this.performanceData.errorMetrics.set(key, value); }); } } catch (error) { console.error('❌ 清理性能数据失败:', error); } } // 获取性能报告 getPerformanceReport() { return { sessionInfo: this.monitoringState, pageMetrics: Array.from(this.performanceData.pageMetrics.values()), apiMetrics: Array.from(this.performanceData.apiMetrics.values()), errorMetrics: Array.from(this.performanceData.errorMetrics.values()), memoryMetrics: this.performanceData.memoryMetrics, summary: this.generatePerformanceSummary() }; } // 生成性能摘要 generatePerformanceSummary() { const pageMetrics = Array.from(this.performanceData.pageMetrics.values()); const apiMetrics = Array.from(this.performanceData.apiMetrics.values()); const errorMetrics = Array.from(this.performanceData.errorMetrics.values()); return { totalPages: pageMetrics.length, averagePageLoadTime: pageMetrics.length > 0 ? pageMetrics.reduce((sum, m) => sum + (m.loadTime || 0), 0) / pageMetrics.length : 0, totalApiRequests: apiMetrics.length, averageApiResponseTime: apiMetrics.length > 0 ? apiMetrics.reduce((sum, m) => sum + (m.responseTime || 0), 0) / apiMetrics.length : 0, totalErrors: errorMetrics.length, errorRate: pageMetrics.length > 0 ? errorMetrics.length / pageMetrics.length : 0, currentMemoryUsage: this.getCurrentMemoryUsage(), sessionDuration: Date.now() - this.monitoringState.startTime }; } // 销毁监控器 destroy() { if (this.reportTimer) { clearInterval(this.reportTimer); this.reportTimer = null; } if (this.memoryTimer) { clearInterval(this.memoryTimer); this.memoryTimer = null; } // 最后一次上报 if (this.pendingReports.length > 0) { this.reportData(); } this.isInitialized = false; console.log('⚡ 性能监控器已销毁'); } } // 创建全局实例 const performanceMonitor = new PerformanceMonitor(); module.exports = performanceMonitor;