#!/usr/bin/env node /** * 性能监控工具 * 收集和分析测试性能数据,识别性能瓶颈 */ const fs = require('fs'); const path = require('path'); class PerformanceMonitor { constructor() { this.performanceDataPath = path.join(process.cwd(), 'test-results', 'performance-data.json'); this.performanceData = this.loadPerformanceData(); this.currentSession = { startTime: Date.now(), tests: [], metrics: {} }; } loadPerformanceData() { try { if (fs.existsSync(this.performanceDataPath)) { return JSON.parse(fs.readFileSync(this.performanceDataPath, 'utf-8')); } } catch (error) { console.warn('加载性能数据失败:', error.message); } return { sessions: [], summary: { avgTestTime: 0, avgPageLoadTime: 0, avgApiTime: 0, totalTests: 0, slowTests: [], fastTests: [] } }; } savePerformanceData() { const dir = path.dirname(this.performanceDataPath); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } fs.writeFileSync(this.performanceDataPath, JSON.stringify(this.performanceData, null, 2), 'utf-8'); } startTest(testName) { const test = { name: testName, startTime: Date.now(), metrics: { pageLoads: [], apiCalls: [], domOperations: [] } }; this.currentSession.tests.push(test); return test; } endTest(test) { test.endTime = Date.now(); test.duration = test.endTime - test.startTime; return test; } recordPageLoad(test, url, loadTime) { test.metrics.pageLoads.push({ url, loadTime, timestamp: Date.now() }); } recordApiCall(test, endpoint, duration) { test.metrics.apiCalls.push({ endpoint, duration, timestamp: Date.now() }); } recordDomOperation(test, operation, duration) { test.metrics.domOperations.push({ operation, duration, timestamp: Date.now() }); } endSession() { this.currentSession.endTime = Date.now(); this.currentSession.duration = this.currentSession.endTime - this.currentSession.startTime; this.performanceData.sessions.push(this.currentSession); this.updateSummary(); this.savePerformanceData(); return this.currentSession; } updateSummary() { const sessions = this.performanceData.sessions; const allTests = sessions.flatMap(s => s.tests); if (allTests.length === 0) return; const totalDuration = allTests.reduce((sum, t) => sum + t.duration, 0); const avgTestTime = totalDuration / allTests.length; const allPageLoads = allTests.flatMap(t => t.metrics.pageLoads); const avgPageLoadTime = allPageLoads.length > 0 ? allPageLoads.reduce((sum, p) => sum + p.loadTime, 0) / allPageLoads.length : 0; const allApiCalls = allTests.flatMap(t => t.metrics.apiCalls); const avgApiTime = allApiCalls.length > 0 ? allApiCalls.reduce((sum, a) => sum + a.duration, 0) / allApiCalls.length : 0; const sortedTests = [...allTests].sort((a, b) => b.duration - a.duration); const slowTests = sortedTests.slice(0, 10); const fastTests = sortedTests.slice(-10).reverse(); this.performanceData.summary = { avgTestTime, avgPageLoadTime, avgApiTime, totalTests: allTests.length, slowTests: slowTests.map(t => ({ name: t.name, duration: t.duration })), fastTests: fastTests.map(t => ({ name: t.name, duration: t.duration })) }; } generateReport() { const summary = this.performanceData.summary; const sessions = this.performanceData.sessions; console.log(''); console.log('═══════════════════════════════════════════'); console.log('📊 性能监控报告'); console.log('═══════════════════════════════════════════'); console.log(''); console.log(`📈 总测试数: ${summary.totalTests}`); console.log(`⏱️ 平均测试时间: ${this.formatDuration(summary.avgTestTime)}`); console.log(`🌐 平均页面加载时间: ${this.formatDuration(summary.avgPageLoadTime)}`); console.log(`📡 平均API响应时间: ${this.formatDuration(summary.avgApiTime)}`); console.log(''); if (summary.slowTests.length > 0) { console.log('🐌 最慢的10个测试:'); summary.slowTests.forEach((test, index) => { console.log(` ${index + 1}. ${test.name} - ${this.formatDuration(test.duration)}`); }); console.log(''); } if (summary.fastTests.length > 0) { console.log('⚡ 最快的10个测试:'); summary.fastTests.forEach((test, index) => { console.log(` ${index + 1}. ${test.name} - ${this.formatDuration(test.duration)}`); }); console.log(''); } this.analyzePerformanceTrends(); this.generateRecommendations(); } analyzePerformanceTrends() { const sessions = this.performanceData.sessions; if (sessions.length < 2) return; const recentSessions = sessions.slice(-5); const avgDurations = recentSessions.map(s => { const tests = s.tests; if (tests.length === 0) return 0; return tests.reduce((sum, t) => sum + t.duration, 0) / tests.length; }); const trend = this.calculateTrend(avgDurations); console.log('📈 性能趋势:'); console.log(` 趋势: ${this.getTrendEmoji(trend)} ${trend.toUpperCase()}`); console.log(` 最近5次平均测试时间: ${avgDurations.map(d => this.formatDuration(d)).join(', ')}`); console.log(''); } calculateTrend(values) { if (values.length < 2) return 'stable'; const firstHalf = values.slice(0, Math.floor(values.length / 2)); const secondHalf = values.slice(Math.floor(values.length / 2)); const firstAvg = firstHalf.reduce((a, b) => a + b, 0) / firstHalf.length; const secondAvg = secondHalf.reduce((a, b) => a + b, 0) / secondHalf.length; const change = ((secondAvg - firstAvg) / firstAvg) * 100; if (change < -10) return 'improving'; if (change > 10) return 'degrading'; return 'stable'; } generateRecommendations() { const summary = this.performanceData.summary; const recommendations = []; if (summary.avgTestTime > 5000) { recommendations.push('⚠️ 平均测试时间超过5秒,建议优化测试执行效率'); } if (summary.avgPageLoadTime > 2000) { recommendations.push('⚠️ 平均页面加载时间超过2秒,建议优化页面性能'); } if (summary.avgApiTime > 1000) { recommendations.push('⚠️ 平均API响应时间超过1秒,建议优化API性能'); } const slowTestsCount = summary.slowTests.filter(t => t.duration > 10000).length; if (slowTestsCount > 5) { recommendations.push(`⚠️ 有${slowTestsCount}个测试执行时间超过10秒,建议重点优化`); } if (recommendations.length > 0) { console.log('💡 性能优化建议:'); recommendations.forEach(rec => { console.log(` ${rec}`); }); console.log(''); } } getTrendEmoji(trend) { switch (trend) { case 'improving': return '📈'; case 'degrading': return '📉'; default: return '➡️'; } } formatDuration(ms) { if (ms < 1000) { return `${ms}ms`; } else if (ms < 60000) { return `${(ms / 1000).toFixed(1)}s`; } else { return `${(ms / 60000).toFixed(1)}m`; } } exportData(filePath) { const exportPath = filePath || 'performance-data-export.json'; fs.writeFileSync(exportPath, JSON.stringify(this.performanceData, null, 2), 'utf-8'); console.log(`✅ 性能数据已导出到: ${exportPath}`); } } // 命令行接口 if (require.main === module) { const monitor = new PerformanceMonitor(); const command = process.argv[2]; switch (command) { case 'report': monitor.generateReport(); break; case 'export': const exportFile = process.argv[3]; monitor.exportData(exportFile); break; case 'start': const testName = process.argv[3]; if (testName) { const test = monitor.startTest(testName); console.log(`✅ 测试已启动: ${testName}`); console.log(`测试ID: ${monitor.currentSession.tests.length - 1}`); } else { console.error('❌ 错误: 请提供测试名称'); process.exit(1); } break; case 'end': const testId = parseInt(process.argv[3]); if (!isNaN(testId)) { const test = monitor.currentSession.tests[testId]; if (test) { monitor.endTest(test); console.log(`✅ 测试已结束: ${test.name}`); console.log(`执行时间: ${monitor.formatDuration(test.duration)}`); } else { console.error('❌ 错误: 测试ID不存在'); process.exit(1); } } else { console.error('❌ 错误: 请提供有效的测试ID'); process.exit(1); } break; case 'session': monitor.endSession(); console.log('✅ 测试会话已结束'); console.log(`会话时长: ${monitor.formatDuration(monitor.currentSession.duration)}`); console.log(`测试数量: ${monitor.currentSession.tests.length}`); break; default: console.log('性能监控工具'); console.log(''); console.log('用法:'); console.log(' node performanceMonitor.js report - 生成性能报告'); console.log(' node performanceMonitor.js export [file.json] - 导出性能数据'); console.log(' node performanceMonitor.js start - 启动测试监控'); console.log(' node performanceMonitor.js end - 结束测试监控'); console.log(' node performanceMonitor.js session - 结束测试会话'); console.log(''); break; } } module.exports = PerformanceMonitor;