miniprogramme/utils/performance-monitor.js
2025-09-12 16:08:17 +08:00

768 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 性能监控管理器 - 微信小程序专用
// 监控应用性能、内存使用、网络请求等关键指标
/**
* 性能监控管理器
* 功能:
* 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;