KNOWN-007: solarTime.ts 单元测试 KNOWN-008: 消除生产代码中 as any 类型断言 KNOWN-002: 工具层测试覆盖 KNOWN-003: lruCache 边界测试 KNOWN-004: templateService 分支覆盖(100%) KNOWN-001: 6个业务组件测试 额外修复: searchOptimizer memoize bug, templateService 常量修改 bug 测试覆盖率: 75.71% -> 90.22%, 测试用例: 412 -> 671
136 lines
3.4 KiB
TypeScript
136 lines
3.4 KiB
TypeScript
interface PerformanceMonitorMetric {
|
|
average: number
|
|
p95: number
|
|
p99: number
|
|
max: number
|
|
min: number
|
|
count: number
|
|
}
|
|
|
|
class PerformanceMonitor {
|
|
private metrics: Map<string, number[]> = new Map()
|
|
private readonly MAX_SAMPLES = 100
|
|
|
|
startMeasure(operation: string): () => void {
|
|
const startTime = performance.now()
|
|
|
|
return () => {
|
|
const endTime = performance.now()
|
|
const duration = endTime - startTime
|
|
|
|
this.recordMetric(operation, duration)
|
|
|
|
console.log(`[性能监控] ${operation}: ${duration.toFixed(2)}ms`)
|
|
}
|
|
}
|
|
|
|
recordMetric(operation: string, duration: number): void {
|
|
if (!this.metrics.has(operation)) {
|
|
this.metrics.set(operation, [])
|
|
}
|
|
|
|
const samples = this.metrics.get(operation)!
|
|
samples.push(duration)
|
|
|
|
if (samples.length > this.MAX_SAMPLES) {
|
|
samples.shift()
|
|
}
|
|
}
|
|
|
|
getAverage(operation: string): number {
|
|
const samples = this.metrics.get(operation)
|
|
if (!samples || samples.length === 0) {
|
|
return 0
|
|
}
|
|
|
|
const sum = samples.reduce((acc, val) => acc + val, 0)
|
|
return sum / samples.length
|
|
}
|
|
|
|
getP95(operation: string): number {
|
|
const samples = this.metrics.get(operation)
|
|
if (!samples || samples.length === 0) {
|
|
return 0
|
|
}
|
|
|
|
const sorted = [...samples].sort((a, b) => a - b)
|
|
const index = Math.floor(sorted.length * 0.95)
|
|
return sorted[index]
|
|
}
|
|
|
|
getP99(operation: string): number {
|
|
const samples = this.metrics.get(operation)
|
|
if (!samples || samples.length === 0) {
|
|
return 0
|
|
}
|
|
|
|
const sorted = [...samples].sort((a, b) => a - b)
|
|
const index = Math.floor(sorted.length * 0.99)
|
|
return sorted[index]
|
|
}
|
|
|
|
getMax(operation: string): number {
|
|
const samples = this.metrics.get(operation)
|
|
if (!samples || samples.length === 0) {
|
|
return 0
|
|
}
|
|
|
|
return Math.max(...samples)
|
|
}
|
|
|
|
getMin(operation: string): number {
|
|
const samples = this.metrics.get(operation)
|
|
if (!samples || samples.length === 0) {
|
|
return 0
|
|
}
|
|
|
|
return Math.min(...samples)
|
|
}
|
|
|
|
getMetrics(): Record<string, PerformanceMonitorMetric> {
|
|
const result: Record<string, PerformanceMonitorMetric> = {}
|
|
|
|
for (const [operation, samples] of this.metrics.entries()) {
|
|
result[operation] = {
|
|
average: this.getAverage(operation),
|
|
p95: this.getP95(operation),
|
|
p99: this.getP99(operation),
|
|
max: this.getMax(operation),
|
|
min: this.getMin(operation),
|
|
count: samples.length
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
clearMetrics(): void {
|
|
this.metrics.clear()
|
|
}
|
|
|
|
clearOperationMetrics(operation: string): void {
|
|
this.metrics.delete(operation)
|
|
}
|
|
|
|
printReport(): void {
|
|
console.log('========== 性能监控报告 ==========')
|
|
const metrics = this.getMetrics()
|
|
|
|
for (const [operation, data] of Object.entries(metrics)) {
|
|
console.log(`\n${operation}:`)
|
|
console.log(` 平均耗时: ${data.average.toFixed(2)}ms`)
|
|
console.log(` P95耗时: ${data.p95.toFixed(2)}ms`)
|
|
console.log(` P99耗时: ${data.p99.toFixed(2)}ms`)
|
|
console.log(` 最大耗时: ${data.max.toFixed(2)}ms`)
|
|
console.log(` 最小耗时: ${data.min.toFixed(2)}ms`)
|
|
console.log(` 采样次数: ${data.count}`)
|
|
}
|
|
|
|
console.log('==================================')
|
|
}
|
|
}
|
|
|
|
const performanceMonitor = new PerformanceMonitor()
|
|
|
|
export default performanceMonitor
|