UNPKG

@yihuangdb/storage-object

Version:

A Node.js storage object layer library using Redis OM

224 lines • 8.35 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.profiler = exports.PerformanceProfiler = void 0; class PerformanceProfiler { static instance; metrics = []; enabled = false; slowThreshold = 100; // ms timers = new Map(); constructor() { // Enable profiling in development or when PROFILE env is set this.enabled = process.env.NODE_ENV === 'development' || process.env.PROFILE === 'true' || process.env.DEBUG_PERFORMANCE === 'true'; } static getInstance() { if (!PerformanceProfiler.instance) { PerformanceProfiler.instance = new PerformanceProfiler(); } return PerformanceProfiler.instance; } enable() { this.enabled = true; console.log('šŸ” Performance profiling enabled'); } disable() { this.enabled = false; } setSlowThreshold(ms) { this.slowThreshold = ms; } startTimer(operation, details) { if (!this.enabled) { return; } const key = `${operation}_${Date.now()}_${Math.random()}`; this.timers.set(key, performance.now()); if (details) { // Log operation start with details if (process.env.DEBUG_PERFORMANCE === 'true') { console.log(`ā±ļø START: ${operation}`, details); } } } endTimer(operation, details) { if (!this.enabled) { return 0; } // Find the most recent timer for this operation let timerKey = null; let startTime = null; for (const [key, time] of this.timers.entries()) { if (key.startsWith(`${operation}_`)) { timerKey = key; startTime = time; break; } } if (!timerKey || startTime === null) { console.warn(`No timer found for operation: ${operation}`); return 0; } const endTime = performance.now(); const duration = endTime - startTime; this.timers.delete(timerKey); const metric = { operation, duration, timestamp: new Date(), details, }; this.metrics.push(metric); // Log slow operations if (duration > this.slowThreshold) { console.warn(`🐌 SLOW OPERATION: ${operation} took ${duration.toFixed(2)}ms`, details || ''); } else if (process.env.DEBUG_PERFORMANCE === 'true') { console.log(`ā±ļø END: ${operation} took ${duration.toFixed(2)}ms`, details || ''); } return duration; } async measure(operation, fn, details) { if (!this.enabled) { return fn(); } const startTime = performance.now(); try { const result = await fn(); const duration = performance.now() - startTime; const metric = { operation, duration, timestamp: new Date(), details, }; this.metrics.push(metric); if (duration > this.slowThreshold) { console.warn(`🐌 SLOW: ${operation} took ${duration.toFixed(2)}ms`, details || ''); } return result; } catch (error) { const duration = performance.now() - startTime; console.error(`āŒ ERROR in ${operation} after ${duration.toFixed(2)}ms:`, error); throw error; } } measureSync(operation, fn, details) { if (!this.enabled) { return fn(); } const startTime = performance.now(); try { const result = fn(); const duration = performance.now() - startTime; const metric = { operation, duration, timestamp: new Date(), details, }; this.metrics.push(metric); if (duration > this.slowThreshold) { console.warn(`🐌 SLOW: ${operation} took ${duration.toFixed(2)}ms`, details || ''); } return result; } catch (error) { const duration = performance.now() - startTime; console.error(`āŒ ERROR in ${operation} after ${duration.toFixed(2)}ms:`, error); throw error; } } getMetrics() { return [...this.metrics]; } getAverageByOperation() { const operationStats = new Map(); for (const metric of this.metrics) { const existing = operationStats.get(metric.operation) || { avg: 0, count: 0, total: 0, max: 0, min: Infinity }; existing.count++; existing.total += metric.duration; existing.avg = existing.total / existing.count; existing.max = Math.max(existing.max, metric.duration); existing.min = Math.min(existing.min, metric.duration); operationStats.set(metric.operation, existing); } return operationStats; } getSlowestOperations(limit = 10) { return [...this.metrics] .sort((a, b) => b.duration - a.duration) .slice(0, limit); } getBottlenecks() { const totalTime = this.metrics.reduce((sum, m) => sum + m.duration, 0); const operationTotals = new Map(); for (const metric of this.metrics) { const current = operationTotals.get(metric.operation) || 0; operationTotals.set(metric.operation, current + metric.duration); } return Array.from(operationTotals.entries()) .map(([operation, time]) => ({ operation, totalTime: time, percentage: (time / totalTime) * 100, })) .sort((a, b) => b.totalTime - a.totalTime); } printReport() { if (!this.enabled || this.metrics.length === 0) { console.log('No performance metrics collected'); return; } console.log('\n' + '='.repeat(80)); console.log('šŸ“Š PERFORMANCE REPORT'); console.log('='.repeat(80)); // Overall stats const totalTime = this.metrics.reduce((sum, m) => sum + m.duration, 0); console.log('\nšŸ“ˆ Overall Statistics:'); console.log(` Total operations: ${this.metrics.length}`); console.log(` Total time: ${totalTime.toFixed(2)}ms`); console.log(` Average time: ${(totalTime / this.metrics.length).toFixed(2)}ms`); // Operation averages console.log('\nā±ļø Operation Averages:'); const averages = this.getAverageByOperation(); for (const [op, stats] of averages.entries()) { console.log(` ${op}:`); console.log(` Count: ${stats.count}`); console.log(` Avg: ${stats.avg.toFixed(2)}ms`); console.log(` Min: ${stats.min.toFixed(2)}ms`); console.log(` Max: ${stats.max.toFixed(2)}ms`); console.log(` Total: ${stats.total.toFixed(2)}ms`); } // Slowest operations console.log('\n🐌 Slowest Operations:'); const slowest = this.getSlowestOperations(5); for (const metric of slowest) { console.log(` ${metric.operation}: ${metric.duration.toFixed(2)}ms`); if (metric.details) { console.log(' Details:', metric.details); } } // Bottlenecks console.log('\nšŸ”„ Bottlenecks (% of total time):'); const bottlenecks = this.getBottlenecks(); for (const bottleneck of bottlenecks.slice(0, 5)) { const bar = 'ā–ˆ'.repeat(Math.round(bottleneck.percentage / 2)); console.log(` ${bottleneck.operation}: ${bar} ${bottleneck.percentage.toFixed(1)}% (${bottleneck.totalTime.toFixed(2)}ms)`); } console.log('\n' + '='.repeat(80)); } reset() { this.metrics = []; this.timers.clear(); } exportMetrics() { return JSON.stringify(this.metrics, null, 2); } } exports.PerformanceProfiler = PerformanceProfiler; // Export singleton instance for convenience exports.profiler = PerformanceProfiler.getInstance(); //# sourceMappingURL=performance-profiler.js.map