UNPKG

fortify2-js

Version:

MOST POWERFUL JavaScript Security Library! Military-grade cryptography + 19 enhanced object methods + quantum-resistant algorithms + perfect TypeScript support. More powerful than Lodash with built-in security.

462 lines (456 loc) 16.3 kB
'use strict'; var events = require('events'); var http = require('http'); var pidusage = require('pidusage'); var errorHandler = require('../../../../utils/errorHandler.js'); var index = require('../../../../components/fortified-function/index.js'); var Logger = require('../../server/utils/Logger.js'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var http__namespace = /*#__PURE__*/_interopNamespaceDefault(http); /** * FortifyJS Health Monitor * Advanced health monitoring system with predictive failure detection */ /** * Intelligent health monitoring with predictive analytics and auto-recovery */ class HealthMonitor extends events.EventEmitter { constructor(config, errorLogger) { super(); this.workerHealthStatus = new Map(); this.healthHistory = new Map(); this.isMonitoring = false; this.config = config; this.errorLogger = errorLogger; // Initialize health checker configuration this.healthChecker = { enabled: config.healthCheck?.enabled !== false, interval: config.healthCheck?.interval || 30000, // 30 seconds timeout: config.healthCheck?.timeout || 5000, // 5 seconds checks: new Map(), customChecks: [], }; this.setupHealthChecker(); } /** * Setup health checker with fortified monitoring */ setupHealthChecker() { if (!this.healthChecker.enabled) return; // Add custom health check if provided if (this.config.healthCheck?.customCheck) { this.healthChecker.customChecks.push(this.config.healthCheck.customCheck); } // Setup default health checks this.addDefaultHealthChecks(); } /** * Add default health checks for comprehensive monitoring */ addDefaultHealthChecks() { // Memory usage check this.healthChecker.customChecks.push(async (workerId) => { const memoryThreshold = this.parseMemoryThreshold(this.config.processManagement?.memoryThreshold || "512MB"); try { const usage = await this.getWorkerMemoryUsage(workerId); return usage < memoryThreshold; } catch { return false; } }); // CPU usage check this.healthChecker.customChecks.push(async (workerId) => { const cpuThreshold = this.config.processManagement?.cpuThreshold || 80; try { const usage = await this.getWorkerCpuUsage(workerId); return usage < cpuThreshold; } catch { return false; } }); // Event loop delay check this.healthChecker.customChecks.push(async (workerId) => { try { const delay = await this.getWorkerEventLoopDelay(workerId); return delay < 100; // 100ms threshold } catch { return false; } }); } /** * Start health monitoring with intelligent scheduling */ startMonitoring() { if (this.isMonitoring || !this.healthChecker.enabled) return; this.isMonitoring = true; const fortifiedMonitor = index.func(async () => { await this.performHealthChecks(); }, { ultraFast: "maximum", auditLog: true, timeout: this.healthChecker.timeout * 2, errorHandling: "graceful", }); this.monitoringInterval = setInterval(() => fortifiedMonitor(), this.healthChecker.interval); Logger.logger.info("cluster", `Health monitoring started (interval: ${this.healthChecker.interval}ms)`); } /** * Stop health monitoring */ stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = undefined; } this.isMonitoring = false; Logger.logger.info("cluster", "Health monitoring stopped"); } /** * Perform comprehensive health checks on all workers */ async performHealthChecks() { const workers = this.getActiveWorkers(); const healthCheckPromises = workers.map(async (workerId) => { try { const isHealthy = await this.checkWorkerHealth(workerId); await this.updateWorkerHealthStatus(workerId, isHealthy); return { workerId, isHealthy }; } catch (error) { const securityError = errorHandler.createSecurityError(`Health check failed for worker ${workerId}: ${error.message}`, errorHandler.ErrorType.INTERNAL, errorHandler.ErrorSeverity.MEDIUM, "HEALTH_CHECK_ERROR", { operation: "health_check" }); this.errorLogger.logError(securityError); return { workerId, isHealthy: false }; } }); const results = await Promise.allSettled(healthCheckPromises); // Process results and trigger events results.forEach((result) => { if (result.status === "fulfilled") { const { workerId, isHealthy } = result.value; if (!isHealthy) { this.handleUnhealthyWorker(workerId); } } }); } /** * Check individual worker health with comprehensive metrics */ async checkWorkerHealth(workerId) { const worker = this.getWorkerById(workerId); if (!worker || worker.isDead()) { return false; } // Run all health checks const healthCheckResults = await Promise.allSettled(this.healthChecker.customChecks.map((check) => Promise.race([ check(workerId), new Promise((_, reject) => setTimeout(() => reject(new Error("Health check timeout")), this.healthChecker.timeout)), ]))); // Calculate health score based on passed checks const passedChecks = healthCheckResults.filter((result) => result.status === "fulfilled" && result.value === true).length; const totalChecks = healthCheckResults.length; const healthScore = totalChecks > 0 ? (passedChecks / totalChecks) * 100 : 0; // Update health check record const checkRecord = { lastCheck: new Date(), consecutiveFailures: 0, isHealthy: healthScore >= 70, // 70% threshold for healthy status lastError: undefined, }; // Update consecutive failures const existingRecord = this.healthChecker.checks.get(workerId); if (existingRecord && !checkRecord.isHealthy) { checkRecord.consecutiveFailures = existingRecord.consecutiveFailures + 1; } this.healthChecker.checks.set(workerId, checkRecord); // Store health history for trend analysis this.updateHealthHistory(workerId, checkRecord.isHealthy, healthScore); return checkRecord.isHealthy; } /** * Update worker health status and trigger appropriate events */ async updateWorkerHealthStatus(workerId, isHealthy) { const previousStatus = this.workerHealthStatus.get(workerId); this.workerHealthStatus.set(workerId, isHealthy); // Trigger events based on status changes if (previousStatus !== undefined && previousStatus !== isHealthy) { if (!isHealthy) { this.emit("worker:health:warning", workerId, this.getWorkerMetrics(workerId)); } else { this.emit("worker:health:recovered", workerId, this.getWorkerMetrics(workerId)); } } // Check for critical health issues const checkRecord = this.healthChecker.checks.get(workerId); const maxFailures = this.config.healthCheck?.maxFailures || 3; if (checkRecord && checkRecord.consecutiveFailures >= maxFailures) { this.emit("worker:health:critical", workerId, this.getWorkerMetrics(workerId)); } } /** * Handle unhealthy worker with intelligent recovery strategies */ async handleUnhealthyWorker(workerId) { const checkRecord = this.healthChecker.checks.get(workerId); const maxFailures = this.config.healthCheck?.maxFailures || 3; if (!checkRecord) return; if (checkRecord.consecutiveFailures >= maxFailures) { // Trigger worker restart this.emit("worker:restart:required", workerId, "health_check_failure"); } else if (checkRecord.consecutiveFailures >= Math.floor(maxFailures / 2)) { // Trigger warning this.emit("worker:health:degraded", workerId, this.getWorkerMetrics(workerId)); } } /** * Update health history for trend analysis */ updateHealthHistory(workerId, healthy, score) { if (!this.healthHistory.has(workerId)) { this.healthHistory.set(workerId, []); } const history = this.healthHistory.get(workerId); history.push({ timestamp: new Date(), healthy, score, }); // Keep only last 100 entries if (history.length > 100) { history.splice(0, history.length - 100); } } /** * Get worker memory usage in bytes using real process monitoring */ async getWorkerMemoryUsage(workerId) { try { const worker = this.getWorkerById(workerId); if (!worker || !worker.process.pid) return 0; const stats = await pidusage(worker.process.pid); return stats.memory; } catch (error) { return 0; } } /** * Get worker CPU usage percentage using real process monitoring */ async getWorkerCpuUsage(workerId) { try { const worker = this.getWorkerById(workerId); if (!worker || !worker.process.pid) return 0; const stats = await pidusage(worker.process.pid); return stats.cpu; } catch (error) { return 0; } } /** * Get worker event loop delay using HTTP health check */ async getWorkerEventLoopDelay(workerId) { try { const healthEndpoint = this.config.healthCheck?.endpoint || "/health"; const port = this.getWorkerPort(workerId); if (!port) return 0; const startTime = Date.now(); const isHealthy = await this.performHttpHealthCheck(`http://localhost:${port}${healthEndpoint}`); const responseTime = Date.now() - startTime; return isHealthy ? responseTime : 1000; // Return high delay if unhealthy } catch (error) { return 1000; // High delay indicates problems } } /** * Perform HTTP health check on worker endpoint */ async performHttpHealthCheck(url) { return new Promise((resolve) => { const timeout = setTimeout(() => resolve(false), this.healthChecker.timeout); const req = http__namespace.get(url, (res) => { clearTimeout(timeout); resolve(res.statusCode === 200); }); req.on("error", () => { clearTimeout(timeout); resolve(false); }); req.setTimeout(this.healthChecker.timeout, () => { req.destroy(); clearTimeout(timeout); resolve(false); }); }); } /** * Get worker port from configuration or environment */ getWorkerPort(workerId) { // Extract worker ID number and calculate port const match = workerId.match(/worker_(\d+)/); if (match) { const workerIndex = parseInt(match[1]); return 3000 + workerIndex; // Base port + worker index } return null; } /** * Parse memory threshold string to bytes */ parseMemoryThreshold(threshold) { const match = threshold.match(/^(\d+(?:\.\d+)?)\s*(MB|GB|KB)?$/i); if (!match) return 512 * 1024 * 1024; // Default 512MB const value = parseFloat(match[1]); const unit = (match[2] || "MB").toUpperCase(); switch (unit) { case "KB": return value * 1024; case "MB": return value * 1024 * 1024; case "GB": return value * 1024 * 1024 * 1024; default: return value; } } /** * Get active worker IDs from cluster workers */ getActiveWorkers() { const workers = []; // Get workers from cluster if available if (typeof require !== "undefined") { try { const clusterModule = require("cluster"); if (clusterModule.workers) { Object.keys(clusterModule.workers).forEach((id) => { const worker = clusterModule.workers[id]; if (worker && !worker.isDead()) { workers.push(`worker_${id}_${Date.now()}`); } }); } } catch (error) { // Fallback to empty array if cluster not available } } return workers; } /** * Get worker by ID from cluster workers */ getWorkerById(workerId) { try { const clusterModule = require("cluster"); const id = workerId.split("_")[1]; return clusterModule.workers?.[id]; } catch (error) { return undefined; } } /** * Get worker metrics from internal tracking */ getWorkerMetrics(workerId) { // Return basic metrics structure for health monitoring return { workerId, pid: 0, uptime: 0, restarts: 0, cpu: { usage: 0, average: 0, peak: 0 }, memory: { usage: 0, peak: 0, percentage: 0, heapUsed: 0, heapTotal: 0, external: 0, }, network: { connections: 0, bytesReceived: 0, bytesSent: 0, connectionsPerSecond: 0, }, requests: { total: 0, perSecond: 0, errors: 0, averageResponseTime: 0, p95ResponseTime: 0, p99ResponseTime: 0, activeRequests: 0, queuedRequests: 0, }, health: { status: "healthy", lastCheck: new Date(), consecutiveFailures: 0, healthScore: 100, }, gc: { collections: 0, timeSpent: 0, averageTime: 0 }, eventLoop: { delay: 0, utilization: 0 }, }; } /** * Get health status for all workers */ getHealthStatus() { const status = {}; this.workerHealthStatus.forEach((healthy, workerId) => { status[workerId] = healthy; }); return status; } /** * Get health history for a worker */ getHealthHistory(workerId) { return this.healthHistory.get(workerId) || []; } /** * Set worker manager reference for integration */ setWorkerManager(workerManager) { this.workerManager = workerManager; } } exports.HealthMonitor = HealthMonitor; //# sourceMappingURL=HealthMonitor.js.map