@tehreet/conduit
Version:
LLM API gateway with intelligent routing, robust process management, and health monitoring
233 lines • 7.27 kB
JavaScript
;
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