205 lines
4.4 KiB
TypeScript
205 lines
4.4 KiB
TypeScript
|
|
/**
|
|||
|
|
* 性能监控工具
|
|||
|
|
* 用于测量和记录性能指标
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 性能测量结果
|
|||
|
|
*/
|
|||
|
|
export interface PerformanceResult {
|
|||
|
|
name: string;
|
|||
|
|
duration: number; // 毫秒
|
|||
|
|
timestamp: number;
|
|||
|
|
metadata?: Record<string, any>;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 性能监控类
|
|||
|
|
*/
|
|||
|
|
class PerformanceMonitor {
|
|||
|
|
private results: PerformanceResult[] = [];
|
|||
|
|
private maxResults = 1000; // 最多保存1000条记录
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 测量函数执行时间
|
|||
|
|
*/
|
|||
|
|
measure<T>(
|
|||
|
|
name: string,
|
|||
|
|
fn: () => T,
|
|||
|
|
metadata?: Record<string, any>,
|
|||
|
|
): T {
|
|||
|
|
const start = performance.now();
|
|||
|
|
try {
|
|||
|
|
const result = fn();
|
|||
|
|
const end = performance.now();
|
|||
|
|
this.record(name, end - start, metadata);
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
const end = performance.now();
|
|||
|
|
this.record(name, end - start, { ...metadata, error: String(error) });
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 异步测量函数执行时间
|
|||
|
|
*/
|
|||
|
|
async measureAsync<T>(
|
|||
|
|
name: string,
|
|||
|
|
fn: () => Promise<T>,
|
|||
|
|
metadata?: Record<string, any>,
|
|||
|
|
): Promise<T> {
|
|||
|
|
const start = performance.now();
|
|||
|
|
try {
|
|||
|
|
const result = await fn();
|
|||
|
|
const end = performance.now();
|
|||
|
|
this.record(name, end - start, metadata);
|
|||
|
|
return result;
|
|||
|
|
} catch (error) {
|
|||
|
|
const end = performance.now();
|
|||
|
|
this.record(name, end - start, { ...metadata, error: String(error) });
|
|||
|
|
throw error;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 记录性能结果
|
|||
|
|
*/
|
|||
|
|
private record(
|
|||
|
|
name: string,
|
|||
|
|
duration: number,
|
|||
|
|
metadata?: Record<string, any>,
|
|||
|
|
): void {
|
|||
|
|
const result: PerformanceResult = {
|
|||
|
|
name,
|
|||
|
|
duration,
|
|||
|
|
timestamp: Date.now(),
|
|||
|
|
metadata,
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
this.results.push(result);
|
|||
|
|
|
|||
|
|
// 限制结果数量
|
|||
|
|
if (this.results.length > this.maxResults) {
|
|||
|
|
this.results.shift();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 开发环境下输出到控制台
|
|||
|
|
if (import.meta.env.DEV) {
|
|||
|
|
const color = duration > 100 ? "🔴" : duration > 50 ? "🟡" : "🟢";
|
|||
|
|
console.log(
|
|||
|
|
`${color} [Performance] ${name}: ${duration.toFixed(2)}ms`,
|
|||
|
|
metadata || "",
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取性能统计
|
|||
|
|
*/
|
|||
|
|
getStats(name?: string): {
|
|||
|
|
count: number;
|
|||
|
|
total: number;
|
|||
|
|
average: number;
|
|||
|
|
min: number;
|
|||
|
|
max: number;
|
|||
|
|
results: PerformanceResult[];
|
|||
|
|
} {
|
|||
|
|
const filtered = name
|
|||
|
|
? this.results.filter(r => r.name === name)
|
|||
|
|
: this.results;
|
|||
|
|
|
|||
|
|
if (filtered.length === 0) {
|
|||
|
|
return {
|
|||
|
|
count: 0,
|
|||
|
|
total: 0,
|
|||
|
|
average: 0,
|
|||
|
|
min: 0,
|
|||
|
|
max: 0,
|
|||
|
|
results: [],
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const durations = filtered.map(r => r.duration);
|
|||
|
|
const total = durations.reduce((sum, d) => sum + d, 0);
|
|||
|
|
const average = total / filtered.length;
|
|||
|
|
const min = Math.min(...durations);
|
|||
|
|
const max = Math.max(...durations);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
count: filtered.length,
|
|||
|
|
total,
|
|||
|
|
average,
|
|||
|
|
min,
|
|||
|
|
max,
|
|||
|
|
results: filtered,
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取所有结果
|
|||
|
|
*/
|
|||
|
|
getAllResults(): PerformanceResult[] {
|
|||
|
|
return [...this.results];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 清空结果
|
|||
|
|
*/
|
|||
|
|
clear(): void {
|
|||
|
|
this.results = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 导出结果(用于分析)
|
|||
|
|
*/
|
|||
|
|
export(): string {
|
|||
|
|
return JSON.stringify(this.results, null, 2);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 创建全局性能监控实例
|
|||
|
|
export const performanceMonitor = new PerformanceMonitor();
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 性能测量装饰器(用于类方法)
|
|||
|
|
*/
|
|||
|
|
export function measurePerformance(name?: string) {
|
|||
|
|
return function (
|
|||
|
|
target: any,
|
|||
|
|
propertyKey: string,
|
|||
|
|
descriptor: PropertyDescriptor,
|
|||
|
|
) {
|
|||
|
|
const originalMethod = descriptor.value;
|
|||
|
|
const methodName = name || `${target.constructor.name}.${propertyKey}`;
|
|||
|
|
|
|||
|
|
if (typeof originalMethod === "function") {
|
|||
|
|
descriptor.value = function (...args: any[]) {
|
|||
|
|
return performanceMonitor.measure(methodName, () =>
|
|||
|
|
originalMethod.apply(this, args),
|
|||
|
|
);
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return descriptor;
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 性能测量Hook(用于React组件)
|
|||
|
|
* 注意:需要在React组件中使用,需要导入React
|
|||
|
|
*/
|
|||
|
|
export function usePerformanceMeasure(name: string) {
|
|||
|
|
// 注意:这个Hook需要在React组件中使用
|
|||
|
|
// 由于可能造成循环依赖,建议在组件中直接使用performanceMonitor.measure
|
|||
|
|
const startRef = { current: performance.now() };
|
|||
|
|
|
|||
|
|
// 返回清理函数
|
|||
|
|
return () => {
|
|||
|
|
if (startRef.current !== null) {
|
|||
|
|
const duration = performance.now() - startRef.current;
|
|||
|
|
performanceMonitor.record(name, duration);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|