/** * Health Check Utilities * Agent 4: Production Deployment * * Provides health check functionality for the application. */ import { env } from '../config'; interface HealthCheckResult { status: 'healthy' | 'unhealthy' | 'degraded'; message?: string; latencyMs?: number; } interface ComponentHealth { name: string; status: 'healthy' | 'unhealthy' | 'degraded'; message?: string; latencyMs?: number; } interface HealthStatus { status: 'healthy' | 'unhealthy' | 'degraded'; version: string; timestamp: string; uptime: number; environment: string; checks: ComponentHealth[]; } type HealthChecker = () => Promise; /** * Health check registry */ class HealthCheckRegistry { private checks: Map = new Map(); private startTime: number = Date.now(); /** * Register a health check */ register(name: string, checker: HealthChecker): void { this.checks.set(name, checker); } /** * Unregister a health check */ unregister(name: string): void { this.checks.delete(name); } /** * Run all health checks */ async runAll(): Promise { const results: ComponentHealth[] = []; let overallStatus: 'healthy' | 'unhealthy' | 'degraded' = 'healthy'; for (const [name, checker] of this.checks) { const start = Date.now(); try { const result = await Promise.race([ checker(), new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5000) ), ]); results.push({ name, status: result.status, message: result.message, latencyMs: result.latencyMs ?? (Date.now() - start), }); if (result.status === 'unhealthy') { overallStatus = 'unhealthy'; } else if (result.status === 'degraded' && overallStatus !== 'unhealthy') { overallStatus = 'degraded'; } } catch (error) { results.push({ name, status: 'unhealthy', message: error instanceof Error ? error.message : 'Unknown error', latencyMs: Date.now() - start, }); overallStatus = 'unhealthy'; } } return { status: overallStatus, version: process.env.npm_package_version || '1.0.0', timestamp: new Date().toISOString(), uptime: Math.floor((Date.now() - this.startTime) / 1000), environment: env.nodeEnv, checks: results, }; } /** * Run liveness check (is the process alive?) */ async checkLiveness(): Promise<{ status: 'ok' | 'error' }> { return { status: 'ok' }; } /** * Run readiness check (is the application ready to serve traffic?) */ async checkReadiness(): Promise { return this.runAll(); } /** * Get uptime in seconds */ getUptime(): number { return Math.floor((Date.now() - this.startTime) / 1000); } } // Create singleton instance export const healthChecks = new HealthCheckRegistry(); // Register default checks healthChecks.register('memory', async () => { const used = process.memoryUsage(); const heapUsedMB = Math.round(used.heapUsed / 1024 / 1024); const heapTotalMB = Math.round(used.heapTotal / 1024 / 1024); const heapUsagePercent = (used.heapUsed / used.heapTotal) * 100; if (heapUsagePercent > 90) { return { status: 'unhealthy', message: `High memory usage: ${heapUsedMB}MB / ${heapTotalMB}MB (${heapUsagePercent.toFixed(1)}%)`, }; } else if (heapUsagePercent > 75) { return { status: 'degraded', message: `Elevated memory usage: ${heapUsedMB}MB / ${heapTotalMB}MB (${heapUsagePercent.toFixed(1)}%)`, }; } return { status: 'healthy', message: `Memory usage: ${heapUsedMB}MB / ${heapTotalMB}MB (${heapUsagePercent.toFixed(1)}%)`, }; }); healthChecks.register('eventloop', async () => { const start = Date.now(); await new Promise((resolve) => setImmediate(resolve)); const lag = Date.now() - start; if (lag > 100) { return { status: 'unhealthy', message: `Event loop lag: ${lag}ms`, latencyMs: lag, }; } else if (lag > 50) { return { status: 'degraded', message: `Event loop lag: ${lag}ms`, latencyMs: lag, }; } return { status: 'healthy', message: `Event loop lag: ${lag}ms`, latencyMs: lag, }; }); // Export types export type { HealthCheckResult, ComponentHealth, HealthStatus, HealthChecker };