/** * System Monitoring Utilities * Provides monitoring and metrics collection */ const logger = require('./logger'); const { isConnected } = require('./database'); /** * Monitor system health periodically */ class HealthMonitor { constructor(intervalMs = 60000) { // Default: 1 minute this.intervalMs = intervalMs; this.interval = null; this.metrics = { uptime: 0, memory: {}, database: {}, lastCheck: null, }; } /** * Start monitoring */ start() { if (this.interval) { logger.warn('Health monitor already running'); return; } logger.info(`Starting health monitor (interval: ${this.intervalMs}ms)`); // Run initial check this.check(); // Schedule periodic checks this.interval = setInterval(() => { this.check(); }, this.intervalMs); } /** * Stop monitoring */ stop() { if (this.interval) { clearInterval(this.interval); this.interval = null; logger.info('Health monitor stopped'); } } /** * Perform health check */ check() { try { // Update metrics this.metrics.uptime = process.uptime(); this.metrics.lastCheck = new Date().toISOString(); // Memory metrics const memory = process.memoryUsage(); this.metrics.memory = { heapUsed: Math.round(memory.heapUsed / 1024 / 1024), heapTotal: Math.round(memory.heapTotal / 1024 / 1024), rss: Math.round(memory.rss / 1024 / 1024), external: Math.round(memory.external / 1024 / 1024), percentage: Math.round((memory.heapUsed / memory.heapTotal) * 100), }; // Database metrics this.metrics.database = { connected: isConnected(), }; // Log warnings for high resource usage if (this.metrics.memory.percentage > 85) { logger.warn(`High memory usage: ${this.metrics.memory.percentage}%`, { heapUsed: `${this.metrics.memory.heapUsed}MB`, heapTotal: `${this.metrics.memory.heapTotal}MB`, }); } if (!this.metrics.database.connected) { logger.error('Database connection lost'); } // Log periodic health status (only in development) if (process.env.NODE_ENV === 'development') { logger.debug('Health check', this.metrics); } } catch (error) { logger.error('Health check failed', error); } } /** * Get current metrics */ getMetrics() { return { ...this.metrics }; } } /** * Request metrics tracker */ class RequestMetrics { constructor() { this.metrics = { total: 0, success: 0, errors: 0, byMethod: {}, byStatus: {}, averageResponseTime: 0, totalResponseTime: 0, }; } /** * Record a request */ record(method, statusCode, responseTime) { this.metrics.total++; this.metrics.totalResponseTime += responseTime; this.metrics.averageResponseTime = Math.round( this.metrics.totalResponseTime / this.metrics.total ); // Count by method this.metrics.byMethod[method] = (this.metrics.byMethod[method] || 0) + 1; // Count by status const statusGroup = `${Math.floor(statusCode / 100)}xx`; this.metrics.byStatus[statusGroup] = (this.metrics.byStatus[statusGroup] || 0) + 1; // Count success/errors if (statusCode >= 200 && statusCode < 400) { this.metrics.success++; } else { this.metrics.errors++; } } /** * Get metrics */ getMetrics() { return { ...this.metrics, successRate: this.metrics.total > 0 ? Math.round((this.metrics.success / this.metrics.total) * 100) : 0, errorRate: this.metrics.total > 0 ? Math.round((this.metrics.errors / this.metrics.total) * 100) : 0, }; } /** * Reset metrics */ reset() { this.metrics = { total: 0, success: 0, errors: 0, byMethod: {}, byStatus: {}, averageResponseTime: 0, totalResponseTime: 0, }; } } /** * Request tracking middleware */ const requestTracker = (requestMetrics) => { return (req, res, next) => { const startTime = Date.now(); // Capture response res.on('finish', () => { const responseTime = Date.now() - startTime; requestMetrics.record(req.method, res.statusCode, responseTime); // Log slow requests if (responseTime > 1000) { logger.warn('Slow request detected', { method: req.method, path: req.path, responseTime: `${responseTime}ms`, statusCode: res.statusCode, }); } }); next(); }; }; // Export singleton instances const healthMonitor = new HealthMonitor(); const requestMetrics = new RequestMetrics(); module.exports = { HealthMonitor, RequestMetrics, healthMonitor, requestMetrics, requestTracker, };