@noony-serverless/core
Version:
A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript
189 lines • 6.07 kB
JavaScript
/**
* 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
;