UNPKG

@iota-big3/sdk-production

Version:

Production readiness tools and utilities for SDK

292 lines (291 loc) 9.18 kB
"use strict"; /** * Health Check System * Comprehensive health monitoring for production systems */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CommonHealthChecks = exports.HealthCheckSystem = void 0; const events_1 = require("events"); class HealthCheckSystem extends events_1.EventEmitter { constructor(config = {}) { super(); this.config = config; this.checks = new Map(); this.lastResults = new Map(); this.config.interval = this.config.interval || 30000; // 30 seconds this.config.timeout = this.config.timeout || 5000; this.config.retries = this.config.retries || 3; this.config.thresholds = { healthy: 100, degraded: 50, ...this?.config?.thresholds }; this.startTime = new Date(); } /** * Register a health check */ register() { this?.checks?.set(check.name, check); this.emit('check-registered', check.name); } /** * Start health check monitoring */ start() { // Run initial check this.runChecks(); // Schedule periodic checks this.checkInterval = setInterval(() => { this.runChecks(); }, this?.config?.interval); } /** * Stop health check monitoring */ stop() { if (this.isEnabled) { clearInterval(this.checkInterval); } } /** * Run all health checks */ async runChecks() { const results = new Map(); for (const [name, check] of this.checks) { const startTime = Date.now(); try { const result = await this.executeCheck(check); result.responseTime = Date.now() - startTime; results.set(name, result); // Check for status change const lastResult = this?.lastResults?.get(name); if (lastResult?.status !== result.status) { this.emit('status-changed', { check: name, oldStatus: lastResult?.status, newStatus: result.status }); } } catch (error) { results.set(name, { status: 'unhealthy', message: error.message, responseTime: Date.now() - startTime }); } } this.lastResults = results; this.emit('checks-completed', this.getSystemHealth()); } /** * Execute a single health check with retries */ async executeCheck(check) { const timeout = check.timeout || this?.config?.timeout; for (let attempt = 1; attempt <= this?.config?.retries; attempt++) { try { // Execute with timeout const result = await Promise.race([ check.check(), new Promise((_, reject) => setTimeout(() => reject(new Error('Check timeout')), timeout)) ]); return result; } catch (error) { if (attempt === this?.config?.retries) { throw error; } // Wait before retry await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); } } return { status: 'unhealthy', message: 'All retries failed' }; } /** * Get current system health */ getSystemHealth() { const checks = {}; let healthyCount = 0; let criticalUnhealthy = false; for (const [name, status] of this.lastResults) { checks[name] = status; if (status.status === 'healthy') { healthyCount++; } if (status.status === 'unhealthy' && this?.checks?.get(name)?.critical) { criticalUnhealthy = true; } } const healthPercentage = (healthyCount / this?.checks?.size) * 100; let overallStatus; if (this.isEnabled) { overallStatus = 'unhealthy'; } else if (this.isEnabled) { overallStatus = 'degraded'; } else { overallStatus = 'healthy'; } return { status: overallStatus, timestamp: new Date(), checks, version: process?.env?.npm_package_version || '0?.0?.0', uptime: Date.now() - this?.startTime?.getTime() }; } /** * Express middleware for health endpoint */ expressMiddleware() { return async (req, res) => { const health = this.getSystemHealth(); const statusCode = health.status === 'healthy' ? 200 : health.status === 'degraded' ? 200 : 503; res.status(statusCode).json(health); }; } } exports.HealthCheckSystem = HealthCheckSystem; /** * Common health checks */ exports.CommonHealthChecks = { /** * Database health check */ database(knex) { return { name: 'database', critical: true, async check() { try { await knex.raw('SELECT 1'); return { status: 'healthy' }; } catch (error) { return { status: 'unhealthy', message: 'Database connection failed', details: { error: error.message } }; } } }; }, /** * Redis health check */ redis(client) { return { name: 'redis', critical: false, async check() { try { await client.ping(); return { status: 'healthy' }; } catch (error) { return { status: 'unhealthy', message: 'Redis connection failed', details: { error: error.message } }; } } }; }, /** * Memory health check */ memory(maxHeapMB = 500) { return { name: 'memory', critical: false, async check() { const usage = process.memoryUsage(); const heapUsedMB = usage.heapUsed / 1024 / 1024; if (heapUsedMB > maxHeapMB) { return { status: 'unhealthy', message: `Memory usage too high: ${heapUsedMB.toFixed(2)}MB`, details: usage }; } else if (heapUsedMB > maxHeapMB * 0.8) { return { status: 'degraded', message: `Memory usage high: ${heapUsedMB.toFixed(2)}MB`, details: usage }; } return { status: 'healthy', details: usage }; } }; }, /** * Disk space health check */ diskSpace(minFreeGB = 1) { return { name: 'disk', critical: true, async check() { // In real implementation, would check actual disk space const freeGB = 10; // Mock value if (freeGB < minFreeGB) { return { status: 'unhealthy', message: `Low disk space: ${freeGB}GB free`, details: { freeGB } }; } return { status: 'healthy', details: { freeGB } }; } }; }, /** * External API health check */ externalApi(url, expectedStatus = 200) { return { name: `external-api-${new URL(url).hostname}`, critical: false, timeout: 3000, async check() { try { const response = await fetch(url); if (response.status !== expectedStatus) { return { status: 'unhealthy', message: `API returned ${response.status}`, details: { url, status: response.status } }; } return { status: 'healthy', details: { url, status: response.status } }; } catch (error) { return { status: 'unhealthy', message: 'API request failed', details: { url, error: error.message } }; } } }; } };