UNPKG

@noony-serverless/core

Version:

A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript

189 lines 6.07 kB
"use strict"; /** * Performance monitoring utilities for production use * Provides lightweight performance tracking without impacting application performance */ Object.defineProperty(exports, "__esModule", { value: true }); exports.performanceMonitor = void 0; exports.timed = timed; exports.timedSync = timedSync; const logger_1 = require("./logger"); class PerformanceMonitor { metrics = new Map(); maxMetricsPerOperation = 1000; // Limit memory usage isEnabled; constructor() { // Enable performance monitoring based on environment this.isEnabled = process.env.NODE_ENV === 'development' || process.env.PERFORMANCE_MONITORING === 'true'; } /** * Start timing an operation */ startTiming(operationName) { if (!this.isEnabled) { return () => { }; // No-op function for production } const startTime = process.hrtime.bigint(); return () => { const endTime = process.hrtime.bigint(); const duration = Number(endTime - startTime) / 1000000; // Convert to milliseconds this.recordMetric(operationName, duration); }; } /** * Time an async operation */ async timeAsync(operationName, operation) { if (!this.isEnabled) { return operation(); } const stopTiming = this.startTiming(operationName); try { const result = await operation(); return result; } finally { stopTiming(); } } /** * Time a synchronous operation */ timeSync(operationName, operation) { if (!this.isEnabled) { return operation(); } const stopTiming = this.startTiming(operationName); try { const result = operation(); return result; } finally { stopTiming(); } } /** * Record a metric manually */ recordMetric(operationName, duration, metadata) { if (!this.isEnabled) return; // Get or create metrics array for this operation let operationMetrics = this.metrics.get(operationName); if (!operationMetrics) { operationMetrics = []; this.metrics.set(operationName, operationMetrics); } // Add the metric operationMetrics.push(duration); // Maintain size limit to prevent memory leaks if (operationMetrics.length > this.maxMetricsPerOperation) { operationMetrics.shift(); // Remove oldest metric } // Log slow operations if (duration > 100) { // Log operations over 100ms logger_1.logger.logPerformance(operationName, duration, metadata); } } /** * Get aggregated metrics for an operation */ getMetrics(operationName) { const metrics = this.metrics.get(operationName); if (!metrics || metrics.length === 0) { return null; } const sortedMetrics = [...metrics].sort((a, b) => a - b); const count = metrics.length; const totalDuration = metrics.reduce((sum, duration) => sum + duration, 0); const p95Index = Math.ceil(count * 0.95) - 1; return { count, totalDuration, averageDuration: totalDuration / count, minDuration: sortedMetrics[0], maxDuration: sortedMetrics[count - 1], p95Duration: sortedMetrics[p95Index], }; } /** * Get all metrics summary */ getAllMetrics() { const summary = {}; for (const [operationName] of this.metrics) { const metrics = this.getMetrics(operationName); if (metrics) { summary[operationName] = metrics; } } return summary; } /** * Reset all metrics */ reset() { this.metrics.clear(); } /** * Get performance summary for health checks */ getHealthSummary() { const allMetrics = this.getAllMetrics(); const slowOperations = Object.entries(allMetrics) .filter(([_, metrics]) => metrics.averageDuration > 50) // Operations over 50ms average .map(([name, metrics]) => ({ name, avgDuration: metrics.averageDuration, })) .sort((a, b) => b.avgDuration - a.avgDuration); const totalMetrics = Array.from(this.metrics.values()).reduce((sum, metrics) => sum + metrics.length, 0); return { isEnabled: this.isEnabled, trackedOperations: this.metrics.size, totalMetrics, slowOperations, }; } /** * Enable or disable monitoring */ setEnabled(enabled) { this.isEnabled = enabled; if (!enabled) { this.reset(); } } } // Global performance monitor instance exports.performanceMonitor = new PerformanceMonitor(); /** * Decorator for timing method calls */ function timed(operationName) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const name = operationName || `${target.constructor.name}.${String(propertyKey)}`; descriptor.value = async function (...args) { return exports.performanceMonitor.timeAsync(name, () => originalMethod.apply(this, args)); }; return descriptor; }; } /** * Decorator for timing synchronous method calls */ function timedSync(operationName) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const name = operationName || `${target.constructor.name}.${String(propertyKey)}`; descriptor.value = function (...args) { return exports.performanceMonitor.timeSync(name, () => originalMethod.apply(this, args)); }; return descriptor; }; } //# sourceMappingURL=performanceMonitor.js.map