UNPKG

@tehreet/conduit

Version:

LLM API gateway with intelligent routing, robust process management, and health monitoring

233 lines 7.27 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HealthMonitor = void 0; const events_1 = require("events"); const log_1 = require("../utils/log"); /** * Health monitoring system for Conduit */ class HealthMonitor extends events_1.EventEmitter { constructor() { super(); this.checks = new Map(); this.status = { healthy: true, checks: {}, uptime: 0, startTime: new Date() }; this.interval = null; this.isRunning = false; this.registerDefaultChecks(); } /** * Start health monitoring */ start(intervalMs = 30000) { if (this.isRunning) { (0, log_1.log)('Health monitoring is already running'); return; } (0, log_1.log)('Starting health monitoring...'); this.isRunning = true; this.status.startTime = new Date(); // Run initial health check this.runHealthChecks(); // Set up periodic health checks this.interval = setInterval(() => { this.runHealthChecks(); }, intervalMs); this.emit('started'); } /** * Stop health monitoring */ stop() { if (!this.isRunning) { (0, log_1.log)('Health monitoring is not running'); return; } (0, log_1.log)('Stopping health monitoring...'); this.isRunning = false; if (this.interval) { clearInterval(this.interval); this.interval = null; } this.emit('stopped'); } /** * Register a health check */ registerCheck(name, check) { this.checks.set(name, check); (0, log_1.log)(`Registered health check: ${name}`); } /** * Unregister a health check */ unregisterCheck(name) { const removed = this.checks.delete(name); if (removed) { (0, log_1.log)(`Unregistered health check: ${name}`); delete this.status.checks[name]; } return removed; } /** * Run all health checks */ async runHealthChecks() { const startTime = Date.now(); try { // Run all checks concurrently const checkPromises = Array.from(this.checks.entries()).map(async ([name, check]) => { try { const result = await Promise.race([ check(), this.timeoutPromise(5000) // 5 second timeout ]); return { name, result }; } catch (error) { return { name, result: { healthy: false, error: error instanceof Error ? error.message : String(error), timestamp: new Date() } }; } }); const results = await Promise.all(checkPromises); // Update status results.forEach(({ name, result }) => { this.status.checks[name] = result; }); // Determine overall health this.status.healthy = Object.values(this.status.checks) .every(check => check.healthy); this.status.uptime = Date.now() - this.status.startTime.getTime(); // Emit events this.emit('health-check', this.status); if (!this.status.healthy) { this.emit('health-warning', this.status); } } catch (error) { (0, log_1.log)('Error running health checks:', error); this.emit('error', error); } } /** * Get current health status */ getStatus() { return { ...this.status, uptime: Date.now() - this.status.startTime.getTime(), checks: { ...this.status.checks } }; } /** * Check if system is healthy */ isHealthy() { return this.status.healthy; } /** * Get specific check result */ getCheckResult(name) { return this.status.checks[name]; } /** * Get all registered check names */ getCheckNames() { return Array.from(this.checks.keys()); } /** * Register default health checks */ registerDefaultChecks() { // Memory usage check this.registerCheck('memory', async () => { const memUsage = process.memoryUsage(); const heapUsedMB = memUsage.heapUsed / 1024 / 1024; const heapTotalMB = memUsage.heapTotal / 1024 / 1024; const usage = heapUsedMB / heapTotalMB; return { healthy: usage < 0.9, // Unhealthy if using >90% of heap timestamp: new Date(), duration: 0, metadata: { heapUsedMB: Math.round(heapUsedMB), heapTotalMB: Math.round(heapTotalMB), usage: Math.round(usage * 100) + '%' } }; }); // Process uptime check this.registerCheck('uptime', async () => { const uptime = process.uptime(); return { healthy: true, timestamp: new Date(), duration: 0, metadata: { uptime: Math.round(uptime), uptimeFormatted: this.formatUptime(uptime) } }; }); // Event loop lag check this.registerCheck('eventloop', async () => { const startTime = Date.now(); return new Promise((resolve) => { setImmediate(() => { const lag = Date.now() - startTime; resolve({ healthy: lag < 100, // Unhealthy if >100ms lag timestamp: new Date(), duration: lag, metadata: { lagMs: lag } }); }); }); }); } /** * Create a timeout promise */ timeoutPromise(ms) { return new Promise((_, reject) => { setTimeout(() => { reject(new Error(`Health check timeout after ${ms}ms`)); }, ms); }); } /** * Format uptime in human readable format */ formatUptime(seconds) { const days = Math.floor(seconds / 86400); const hours = Math.floor((seconds % 86400) / 3600); const minutes = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); const parts = []; if (days > 0) parts.push(`${days}d`); if (hours > 0) parts.push(`${hours}h`); if (minutes > 0) parts.push(`${minutes}m`); if (secs > 0 || parts.length === 0) parts.push(`${secs}s`); return parts.join(' '); } } exports.HealthMonitor = HealthMonitor; //# sourceMappingURL=HealthMonitor.js.map