194 lines
4.2 KiB
TypeScript
194 lines
4.2 KiB
TypeScript
import { logger } from "./logger";
|
|
|
|
export interface PerformanceMetric {
|
|
name: string;
|
|
duration: number;
|
|
timestamp: number;
|
|
metadata?: Record<string, unknown>;
|
|
}
|
|
|
|
class PerformanceMonitor {
|
|
private metrics: PerformanceMetric[] = [];
|
|
private maxMetrics = 1000;
|
|
|
|
/**
|
|
* 测量函数执行时间
|
|
*/
|
|
async measure<T>(
|
|
name: string,
|
|
fn: () => Promise<T>,
|
|
metadata?: Record<string, unknown>
|
|
): Promise<T> {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const result = await fn();
|
|
const duration = Date.now() - startTime;
|
|
|
|
this.recordMetric(name, duration, metadata);
|
|
|
|
return result;
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
this.recordMetric(`${name}_error`, duration, {
|
|
...metadata,
|
|
error: true,
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 同步测量函数执行时间
|
|
*/
|
|
measureSync<T>(
|
|
name: string,
|
|
fn: () => T,
|
|
metadata?: Record<string, unknown>
|
|
): T {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
const result = fn();
|
|
const duration = Date.now() - startTime;
|
|
|
|
this.recordMetric(name, duration, metadata);
|
|
|
|
return result;
|
|
} catch (error) {
|
|
const duration = Date.now() - startTime;
|
|
this.recordMetric(`${name}_error`, duration, {
|
|
...metadata,
|
|
error: true,
|
|
});
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 记录性能指标
|
|
*/
|
|
recordMetric(
|
|
name: string,
|
|
duration: number,
|
|
metadata?: Record<string, unknown>
|
|
): void {
|
|
const metric: PerformanceMetric = {
|
|
name,
|
|
duration,
|
|
timestamp: Date.now(),
|
|
metadata,
|
|
};
|
|
|
|
this.metrics.push(metric);
|
|
|
|
// 限制指标数量
|
|
if (this.metrics.length > this.maxMetrics) {
|
|
this.metrics = this.metrics.slice(-this.maxMetrics / 2);
|
|
}
|
|
|
|
// 记录慢查询
|
|
if (duration > 1000) {
|
|
logger.warn("Slow operation detected", {
|
|
name,
|
|
duration,
|
|
metadata,
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取性能统计
|
|
*/
|
|
getStats(): {
|
|
totalMetrics: number;
|
|
averageDuration: number;
|
|
slowestOperations: PerformanceMetric[];
|
|
fastestOperations: PerformanceMetric[];
|
|
operationCounts: Record<string, number>;
|
|
} {
|
|
if (this.metrics.length === 0) {
|
|
return {
|
|
totalMetrics: 0,
|
|
averageDuration: 0,
|
|
slowestOperations: [],
|
|
fastestOperations: [],
|
|
operationCounts: {},
|
|
};
|
|
}
|
|
|
|
const totalDuration = this.metrics.reduce(
|
|
(sum, metric) => sum + metric.duration,
|
|
0
|
|
);
|
|
const averageDuration = totalDuration / this.metrics.length;
|
|
|
|
// 按名称分组统计
|
|
const operationCounts: Record<string, number> = {};
|
|
this.metrics.forEach((metric) => {
|
|
operationCounts[metric.name] = (operationCounts[metric.name] || 0) + 1;
|
|
});
|
|
|
|
// 获取最慢的操作
|
|
const slowestOperations = [...this.metrics]
|
|
.sort((a, b) => b.duration - a.duration)
|
|
.slice(0, 10);
|
|
|
|
// 获取最快的操作
|
|
const fastestOperations = [...this.metrics]
|
|
.sort((a, b) => a.duration - b.duration)
|
|
.slice(0, 10);
|
|
|
|
return {
|
|
totalMetrics: this.metrics.length,
|
|
averageDuration,
|
|
slowestOperations,
|
|
fastestOperations,
|
|
operationCounts,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 清理旧指标
|
|
*/
|
|
cleanup(maxAge: number = 24 * 60 * 60 * 1000): void {
|
|
const cutoff = Date.now() - maxAge;
|
|
this.metrics = this.metrics.filter((metric) => metric.timestamp > cutoff);
|
|
}
|
|
|
|
/**
|
|
* 导出性能报告
|
|
*/
|
|
generateReport(): string {
|
|
const stats = this.getStats();
|
|
|
|
return `
|
|
Performance Report
|
|
==================
|
|
|
|
Total Metrics: ${stats.totalMetrics}
|
|
Average Duration: ${stats.averageDuration.toFixed(2)}ms
|
|
|
|
Slowest Operations:
|
|
${stats.slowestOperations
|
|
.map((op) => ` ${op.name}: ${op.duration}ms`)
|
|
.join("\n")}
|
|
|
|
Operation Counts:
|
|
${Object.entries(stats.operationCounts)
|
|
.sort(([, a], [, b]) => b - a)
|
|
.map(([name, count]) => ` ${name}: ${count}`)
|
|
.join("\n")}
|
|
`.trim();
|
|
}
|
|
}
|
|
|
|
export const performanceMonitor = new PerformanceMonitor();
|
|
|
|
// 定期清理旧指标
|
|
if (typeof window === "undefined") {
|
|
setInterval(() => {
|
|
performanceMonitor.cleanup();
|
|
}, 60 * 60 * 1000); // 每小时清理一次
|
|
}
|