UNPKG

@knath2000/codebase-indexing-mcp

Version:

MCP server for codebase indexing with Voyage AI embeddings and Qdrant vector storage

254 lines 8.58 kB
export class HealthMonitorService { constructor(config, voyageClient, qdrantClient) { this.healthCheckInterval = null; this.config = config; this.startTime = new Date(); this.voyageClient = voyageClient; this.qdrantClient = qdrantClient; this.lastHealthCheck = new Date(); // Start periodic health checks this.startHealthChecks(); } /** * Get comprehensive health status */ async getHealthStatus() { console.log('🏥 [HealthMonitor] Performing health check...'); const [qdrantHealth, voyageHealth, fileWatcherHealth] = await Promise.allSettled([ this.checkQdrantHealth(), this.checkVoyageHealth(), this.checkFileWatcherHealth() ]); const services = { qdrant: qdrantHealth.status === 'fulfilled' ? qdrantHealth.value : this.getFailedServiceHealth('Qdrant check failed'), voyage: voyageHealth.status === 'fulfilled' ? voyageHealth.value : this.getFailedServiceHealth('Voyage check failed'), fileWatcher: fileWatcherHealth.status === 'fulfilled' ? fileWatcherHealth.value : this.getFailedServiceHealth('File watcher check failed') }; // Determine overall status const serviceStatuses = Object.values(services).map(s => s.status); let overallStatus; if (serviceStatuses.every(s => s === 'healthy')) { overallStatus = 'healthy'; } else if (serviceStatuses.some(s => s === 'unhealthy')) { overallStatus = 'unhealthy'; } else { overallStatus = 'degraded'; } const metrics = await this.getSystemMetrics(); const healthStatus = { status: overallStatus, timestamp: new Date(), services, metrics, version: '1.0.0', // TODO: Get from package.json mcpSchemaVersion: this.config.mcpSchemaVersion }; this.lastHealthCheck = new Date(); console.log(`✅ [HealthMonitor] Health check complete: ${overallStatus}`); return healthStatus; } /** * Check Qdrant service health */ async checkQdrantHealth() { const startTime = Date.now(); try { const isConnected = await this.qdrantClient.testConnection(); const latency = Date.now() - startTime; if (!isConnected) { return { status: 'unhealthy', latency, lastCheck: new Date(), message: 'Qdrant connection test failed' }; } // Additional health checks try { await this.qdrantClient.getCollectionInfo(); return { status: 'healthy', latency, lastCheck: new Date(), message: 'Qdrant operational' }; } catch (collectionError) { return { status: 'degraded', latency, lastCheck: new Date(), message: 'Qdrant connected but collection not found' }; } } catch (error) { return { status: 'unhealthy', latency: Date.now() - startTime, lastCheck: new Date(), message: `Qdrant error: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Check Voyage AI service health */ async checkVoyageHealth() { const startTime = Date.now(); try { const isConnected = await this.voyageClient.testConnection(); const latency = Date.now() - startTime; return { status: isConnected ? 'healthy' : 'unhealthy', latency, lastCheck: new Date(), message: isConnected ? 'Voyage AI operational' : 'Voyage AI connection failed' }; } catch (error) { return { status: 'unhealthy', latency: Date.now() - startTime, lastCheck: new Date(), message: `Voyage AI error: ${error instanceof Error ? error.message : String(error)}` }; } } /** * Check file watcher service health */ async checkFileWatcherHealth() { // For now, assume file watcher is healthy if the process is running // In a real implementation, you'd check the actual watcher status return { status: 'healthy', latency: 0, lastCheck: new Date(), message: 'File watcher operational' }; } /** * Get system metrics */ async getSystemMetrics() { const uptime = Date.now() - this.startTime.getTime(); // Get memory usage const memoryUsage = process.memoryUsage(); const memoryUsageMB = Math.round(memoryUsage.heapUsed / 1024 / 1024); return { uptime: Math.round(uptime / 1000), // Convert to seconds memoryUsage: memoryUsageMB // CPU and disk usage would require additional libraries }; } /** * Create a failed service health object */ getFailedServiceHealth(message) { return { status: 'unhealthy', lastCheck: new Date(), message }; } /** * Start periodic health checks */ startHealthChecks() { // Run health checks every 5 minutes this.healthCheckInterval = setInterval(async () => { try { await this.getHealthStatus(); } catch (error) { console.error('❌ [HealthMonitor] Periodic health check failed:', error); } }, 5 * 60 * 1000); console.log('🏥 [HealthMonitor] Started periodic health checks (5 min intervals)'); } /** * Stop periodic health checks */ stopHealthChecks() { if (this.healthCheckInterval) { clearInterval(this.healthCheckInterval); this.healthCheckInterval = null; console.log('🏥 [HealthMonitor] Stopped periodic health checks'); } } /** * Get simple health status for quick checks */ async getSimpleHealth() { try { const health = await this.getHealthStatus(); return { status: health.status, timestamp: health.timestamp }; } catch (error) { return { status: 'unhealthy', timestamp: new Date() }; } } /** * Check if the service is ready to serve requests */ async isReady() { try { const health = await this.getHealthStatus(); return health.status !== 'unhealthy'; } catch (error) { return false; } } /** * Check if the service is alive (basic liveness check) */ isAlive() { // Basic check - if we can execute this function, the service is alive return true; } /** * Get detailed service statistics */ async getDetailedStats() { const health = await this.getHealthStatus(); return { health, performance: { averageResponseTime: 0, // TODO: Implement performance tracking requestCount: 0, // TODO: Implement request counting errorRate: 0 // TODO: Implement error rate tracking }, resources: { memoryUsage: health.metrics.memoryUsage, uptime: health.metrics.uptime, lastHealthCheck: this.lastHealthCheck } }; } /** * Get health summary for logging/monitoring */ getHealthSummary() { const uptime = Math.round((Date.now() - this.startTime.getTime()) / 1000); const memory = Math.round(process.memoryUsage().heapUsed / 1024 / 1024); return `Uptime: ${uptime}s, Memory: ${memory}MB, Last Check: ${this.lastHealthCheck.toISOString()}`; } /** * Cleanup resources */ destroy() { this.stopHealthChecks(); console.log('🏥 [HealthMonitor] Destroyed health monitor'); } } //# sourceMappingURL=health-monitor.js.map