Initial commit
This commit is contained in:
193
lib/utils/performance.ts
Normal file
193
lib/utils/performance.ts
Normal file
@ -0,0 +1,193 @@
|
||||
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); // 每小时清理一次
|
||||
}
|
||||
Reference in New Issue
Block a user