findme-miniprogram-frontend/utils/performance-monitor.js

760 lines
21 KiB
JavaScript
Raw Normal View History

2025-12-27 17:16:03 +08:00
// 性能监控管理器 - 微信小程序专用
// 监控应用性能、内存使用、网络请求等关键指标
/**
* 性能监控管理器
* 功能
* 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;
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;
} 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: []
});
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);
}
// 记录用户操作
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);
}
// 📊 ===== 内存监控 =====
// 启动内存监控
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();
} 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
} 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
// 模拟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;
}
}
// 创建全局实例
const performanceMonitor = new PerformanceMonitor();
module.exports = performanceMonitor;