This commit is contained in:
手瓜一十雪
2025-08-06 18:43:44 +08:00
parent beb189bd49
commit 7f08d62b5b
10 changed files with 1400 additions and 95 deletions

197
src/common/perf-cli.ts Normal file
View File

@@ -0,0 +1,197 @@
#!/usr/bin/env node
/**
* 性能监控命令行工具
*/
import { performanceMonitor } from '../common/performance-monitor';
function printColoredText(text: string, color: string): void {
const colors: Record<string, string> = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
white: '\x1b[37m',
reset: '\x1b[0m',
bright: '\x1b[1m'
};
console.log(`${colors[color] || colors['white']}${text}${colors['reset']}`);
}
function printHeader(title: string): void {
const line = '='.repeat(50);
printColoredText(line, 'cyan');
printColoredText(title.toUpperCase().padStart((50 + title.length) / 2), 'bright');
printColoredText(line, 'cyan');
}
function printSubHeader(title: string): void {
const line = '-'.repeat(30);
printColoredText(line, 'yellow');
printColoredText(title, 'yellow');
printColoredText(line, 'yellow');
}
function formatTime(ms: number): string {
if (ms < 1) {
return `${(ms * 1000).toFixed(2)}μs`;
} else if (ms < 1000) {
return `${ms.toFixed(2)}ms`;
} else {
return `${(ms / 1000).toFixed(2)}s`;
}
}
function printStatsTable(stats: any[], title: string): void {
printSubHeader(`${title} (Top 10)`);
if (stats.length === 0) {
printColoredText(' 暂无数据', 'yellow');
return;
}
console.log();
console.log(' 排名 | 函数名称 | 调用次数 | 总耗时 | 平均耗时 | 最小耗时 | 最大耗时');
console.log(' ' + '-'.repeat(100));
stats.slice(0, 10).forEach((stat, index) => {
const rank = (index + 1).toString().padEnd(4);
const name = stat.name.length > 30 ? stat.name.substring(0, 27) + '...' : stat.name.padEnd(30);
const callCount = stat.callCount.toString().padEnd(8);
const totalTime = formatTime(stat.totalTime).padEnd(10);
const avgTime = formatTime(stat.averageTime).padEnd(10);
const minTime = formatTime(stat.minTime).padEnd(10);
const maxTime = formatTime(stat.maxTime).padEnd(10);
const color = index < 3 ? 'green' : 'white';
printColoredText(
` ${rank} | ${name} | ${callCount} | ${totalTime} | ${avgTime} | ${minTime} | ${maxTime}`,
color
);
});
console.log();
}
function printSummary(): void {
const stats = performanceMonitor.getStats();
const totalFunctions = stats.length;
const totalCalls = stats.reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = stats.reduce((sum, stat) => sum + stat.totalTime, 0);
const avgTimePerCall = totalCalls > 0 ? totalTime / totalCalls : 0;
printSubHeader('📊 统计摘要');
console.log();
printColoredText(` 监控函数数量: ${totalFunctions}`, 'cyan');
printColoredText(` 总调用次数: ${totalCalls}`, 'cyan');
printColoredText(` 总耗时: ${formatTime(totalTime)}`, 'cyan');
printColoredText(` 平均每次调用耗时: ${formatTime(avgTimePerCall)}`, 'cyan');
console.log();
}
function main(): void {
const args = process.argv.slice(2);
const command = args[0] || 'report';
switch (command) {
case 'report':
case 'r':
printHeader('🚀 NapCat 性能监控报告');
console.log();
printSummary();
const totalTimeStats = performanceMonitor.getTopByTotalTime(10);
const callCountStats = performanceMonitor.getTopByCallCount(10);
const avgTimeStats = performanceMonitor.getTopByAverageTime(10);
printStatsTable(totalTimeStats, '🔥 总耗时排行榜');
printStatsTable(callCountStats, '📈 调用次数排行榜');
printStatsTable(avgTimeStats, '⏱️ 平均耗时排行榜');
break;
case 'top':
case 't':
const type = args[1] || 'total';
const limit = parseInt(args[2] || '10') || 10;
switch (type) {
case 'total':
case 'time':
printHeader('🔥 总耗时排行榜');
printStatsTable(performanceMonitor.getTopByTotalTime(limit), '');
break;
case 'count':
case 'calls':
printHeader('📈 调用次数排行榜');
printStatsTable(performanceMonitor.getTopByCallCount(limit), '');
break;
case 'avg':
case 'average':
printHeader('⏱️ 平均耗时排行榜');
printStatsTable(performanceMonitor.getTopByAverageTime(limit), '');
break;
default:
printColoredText('未知的排行榜类型。可用类型: total, count, avg', 'red');
}
break;
case 'clear':
case 'c':
performanceMonitor.clear();
printColoredText('✅ 性能统计数据已清空', 'green');
break;
case 'json':
case 'j':
const jsonStats = performanceMonitor.toJSON();
console.log(JSON.stringify(jsonStats, null, 2));
break;
case 'help':
case 'h':
case '--help':
printHelp();
break;
default:
printColoredText(`未知命令: ${command}`, 'red');
printHelp();
process.exit(1);
}
}
function printHelp(): void {
printHeader('📖 帮助信息');
console.log();
printColoredText('用法: napcat-perf <command> [options]', 'cyan');
console.log();
printColoredText('命令:', 'yellow');
console.log(' report, r 显示完整性能报告 (默认)');
console.log(' top <type> [limit] 显示指定类型的排行榜');
console.log(' - total, time 按总耗时排序');
console.log(' - count, calls 按调用次数排序');
console.log(' - avg, average 按平均耗时排序');
console.log(' clear, c 清空所有统计数据');
console.log(' json, j 以JSON格式输出数据');
console.log(' help, h 显示此帮助信息');
console.log();
printColoredText('示例:', 'yellow');
console.log(' napcat-perf report');
console.log(' napcat-perf top total 20');
console.log(' napcat-perf top count');
console.log(' napcat-perf clear');
console.log();
}
// 如果直接运行此文件
if (require.main === module) {
main();
}
export { main as runPerfMonitor };

View File

@@ -0,0 +1,87 @@
/**
* 性能监控演示示例
*/
import { performanceMonitor } from './performance-monitor';
// 模拟一些函数调用来测试性能监控
class ExampleService {
async fetchData(id: string): Promise<string> {
// 模拟网络请求延迟
await new Promise(resolve => setTimeout(resolve, Math.random() * 100));
return `Data for ${id}`;
}
processData(data: string): string {
// 模拟CPU密集型操作
let result = data;
for (let i = 0; i < 1000; i++) {
result = result.split('').reverse().join('');
}
return result;
}
async saveData(data: string): Promise<void> {
// 模拟保存操作
console.log(`保存数据: ${data.length} 字符`);
await new Promise(resolve => setTimeout(resolve, Math.random() * 50));
}
}
// 工具函数
function calculateHash(input: string): string {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为32位整数
}
return hash.toString(16);
}
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
// 演示函数
export async function runPerformanceDemo(): Promise<void> {
console.log('🚀 开始性能监控演示...\n');
const service = new ExampleService();
// 执行一些操作来生成性能数据
for (let i = 0; i < 10; i++) {
try {
// 获取数据
const data = await service.fetchData(`item-${i}`);
// 处理数据
const processed = service.processData(data);
// 计算哈希
const hash = calculateHash(processed);
// 保存数据
await service.saveData(`${processed}-${hash}`);
console.log(`✅ 处理完成第 ${i + 1}`);
// 随机延迟
await delay(Math.random() * 20);
} catch (error) {
console.error(`❌ 处理第 ${i + 1} 项时出错:`, error);
}
}
// 等待一小段时间确保所有异步操作完成
await delay(100);
console.log('\n📊 性能监控演示完成!');
console.log('性能统计数据:');
// 显示性能统计
performanceMonitor.printReport();
}
// 如果直接运行此文件
if (require.main === module) {
runPerformanceDemo().catch(console.error);
}

View File

@@ -0,0 +1,316 @@
/**
* 性能监控器 - 用于统计函数调用次数、耗时等信息
*/
import * as fs from 'fs';
import * as path from 'path';
export interface FunctionStats {
name: string;
callCount: number;
totalTime: number;
averageTime: number;
minTime: number;
maxTime: number;
fileName?: string;
lineNumber?: number;
}
export class PerformanceMonitor {
private static instance: PerformanceMonitor;
private stats = new Map<string, FunctionStats>();
private startTimes = new Map<string, number>();
private reportInterval: NodeJS.Timeout | null = null;
static getInstance(): PerformanceMonitor {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor();
// 启动定时统计报告
PerformanceMonitor.instance.startPeriodicReport();
}
return PerformanceMonitor.instance;
}
/**
* 开始定时统计报告 (每60秒)
*/
private startPeriodicReport(): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
}
this.reportInterval = setInterval(() => {
if (this.stats.size > 0) {
this.printPeriodicReport();
this.writeDetailedLogToFile();
}
}, 60000); // 60秒
}
/**
* 停止定时统计报告
*/
stopPeriodicReport(): void {
if (this.reportInterval) {
clearInterval(this.reportInterval);
this.reportInterval = null;
}
}
/**
* 打印定时统计报告 (简化版本)
*/
private printPeriodicReport(): void {
const now = new Date().toLocaleString();
console.log(`\n=== 性能监控定时报告 [${now}] ===`);
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
console.log(`📊 总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms`);
// 显示Top 5最活跃的函数
console.log('\n🔥 最活跃函数 (Top 5):');
this.getTopByCallCount(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
// 显示Top 5最耗时的函数
console.log('\n⏱ 最耗时函数 (Top 5):');
this.getTopByTotalTime(5).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('===============================\n');
}
/**
* 将详细统计数据写入日志文件
*/
private writeDetailedLogToFile(): void {
try {
const now = new Date();
const dateStr = now.toISOString().replace(/[:.]/g, '-').split('T')[0];
const timeStr = now.toTimeString().split(' ')[0]?.replace(/:/g, '-') || 'unknown-time';
const timestamp = `${dateStr}_${timeStr}`;
const fileName = `${timestamp}.log.txt`;
const logPath = path.join(process.cwd(), 'logs', fileName);
// 确保logs目录存在
const logsDir = path.dirname(logPath);
if (!fs.existsSync(logsDir)) {
fs.mkdirSync(logsDir, { recursive: true });
}
const totalFunctions = this.stats.size;
const totalCalls = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.callCount, 0);
const totalTime = Array.from(this.stats.values()).reduce((sum, stat) => sum + stat.totalTime, 0);
let logContent = '';
logContent += `=== 性能监控详细报告 ===\n`;
logContent += `生成时间: ${now.toLocaleString()}\n`;
logContent += `统计周期: 60秒\n`;
logContent += `总览: ${totalFunctions} 个函数, ${totalCalls} 次调用, 总耗时: ${totalTime.toFixed(2)}ms\n\n`;
// 详细函数统计
logContent += `=== 所有函数详细统计 ===\n`;
const allStats = this.getStats().sort((a, b) => b.totalTime - a.totalTime);
allStats.forEach((stat, index) => {
logContent += `${index + 1}. 函数: ${stat.name}\n`;
logContent += ` 文件: ${stat.fileName || 'N/A'}\n`;
logContent += ` 行号: ${stat.lineNumber || 'N/A'}\n`;
logContent += ` 调用次数: ${stat.callCount}\n`;
logContent += ` 总耗时: ${stat.totalTime.toFixed(4)}ms\n`;
logContent += ` 平均耗时: ${stat.averageTime.toFixed(4)}ms\n`;
logContent += ` 最小耗时: ${stat.minTime === Infinity ? 'N/A' : stat.minTime.toFixed(4)}ms\n`;
logContent += ` 最大耗时: ${stat.maxTime.toFixed(4)}ms\n`;
logContent += ` 性能占比: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += `\n`;
});
// 排行榜统计
logContent += `=== 总耗时排行榜 (Top 20) ===\n`;
this.getTopByTotalTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 调用次数排行榜 (Top 20) ===\n`;
this.getTopByCallCount(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均: ${stat.averageTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 平均耗时排行榜 (Top 20) ===\n`;
this.getTopByAverageTime(20).forEach((stat, index) => {
logContent += `${index + 1}. ${stat.name} - 平均: ${stat.averageTime.toFixed(2)}ms, 调用: ${stat.callCount}次, 总耗时: ${stat.totalTime.toFixed(2)}ms\n`;
});
logContent += `\n=== 性能热点分析 ===\n`;
// 找出最耗时的前10个函数
const hotSpots = this.getTopByTotalTime(10);
hotSpots.forEach((stat, index) => {
const efficiency = stat.callCount / stat.totalTime; // 每毫秒的调用次数
logContent += `${index + 1}. ${stat.name}\n`;
logContent += ` 性能影响: ${((stat.totalTime / totalTime) * 100).toFixed(2)}%\n`;
logContent += ` 调用效率: ${efficiency.toFixed(4)} 调用/ms\n`;
logContent += ` 优化建议: ${stat.averageTime > 10 ? '考虑优化此函数的执行效率' :
stat.callCount > 1000 ? '考虑减少此函数的调用频率' :
'性能表现良好'}\n\n`;
});
logContent += `=== 报告结束 ===\n`;
// 写入文件
fs.writeFileSync(logPath, logContent, 'utf8');
console.log(`📄 详细性能报告已保存到: ${logPath}`);
} catch (error) {
console.error('写入性能日志文件时出错:', error);
}
}
/**
* 开始记录函数调用
*/
startFunction(functionName: string, fileName?: string, lineNumber?: number): string {
const callId = `${functionName}_${Date.now()}_${Math.random()}`;
this.startTimes.set(callId, performance.now());
// 初始化或更新统计信息
if (!this.stats.has(functionName)) {
this.stats.set(functionName, {
name: functionName,
callCount: 0,
totalTime: 0,
averageTime: 0,
minTime: Infinity,
maxTime: 0,
fileName,
lineNumber
});
}
const stat = this.stats.get(functionName)!;
stat.callCount++;
return callId;
}
/**
* 结束记录函数调用
*/
endFunction(callId: string, functionName: string): void {
const startTime = this.startTimes.get(callId);
if (!startTime) return;
const endTime = performance.now();
const duration = endTime - startTime;
this.startTimes.delete(callId);
const stat = this.stats.get(functionName);
if (!stat) return;
stat.totalTime += duration;
stat.averageTime = stat.totalTime / stat.callCount;
stat.minTime = Math.min(stat.minTime, duration);
stat.maxTime = Math.max(stat.maxTime, duration);
}
/**
* 获取所有统计信息
*/
getStats(): FunctionStats[] {
return Array.from(this.stats.values());
}
/**
* 获取排行榜 - 按总耗时排序
*/
getTopByTotalTime(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.totalTime - a.totalTime)
.slice(0, limit);
}
/**
* 获取排行榜 - 按调用次数排序
*/
getTopByCallCount(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.callCount - a.callCount)
.slice(0, limit);
}
/**
* 获取排行榜 - 按平均耗时排序
*/
getTopByAverageTime(limit = 20): FunctionStats[] {
return this.getStats()
.sort((a, b) => b.averageTime - a.averageTime)
.slice(0, limit);
}
/**
* 清空统计数据
*/
clear(): void {
this.stats.clear();
this.startTimes.clear();
}
/**
* 打印统计报告
*/
printReport(): void {
console.log('\n=== 函数性能监控报告 ===');
console.log('\n🔥 总耗时排行榜 (Top 10):');
this.getTopByTotalTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 总耗时: ${stat.totalTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n📈 调用次数排行榜 (Top 10):');
this.getTopByCallCount(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms, 平均耗时: ${stat.averageTime.toFixed(2)}ms`);
});
console.log('\n⏱ 平均耗时排行榜 (Top 10):');
this.getTopByAverageTime(10).forEach((stat, index) => {
console.log(`${index + 1}. ${stat.name} - 平均耗时: ${stat.averageTime.toFixed(2)}ms, 调用次数: ${stat.callCount}, 总耗时: ${stat.totalTime.toFixed(2)}ms`);
});
console.log('\n========================\n');
}
/**
* 获取JSON格式的统计数据
*/
toJSON(): FunctionStats[] {
return this.getStats();
}
}
// 全局性能监控器实例
export const performanceMonitor = PerformanceMonitor.getInstance();
// 在进程退出时打印报告并停止定时器
if (typeof process !== 'undefined') {
process.on('exit', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
});
process.on('SIGINT', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
process.on('SIGTERM', () => {
performanceMonitor.stopPeriodicReport();
performanceMonitor.printReport();
process.exit(0);
});
}