UNPKG

@sailboat-computer/event-bus

Version:

Standardized event bus for sailboat computer v3 with resilience features and offline capabilities

308 lines 10.6 kB
"use strict"; /** * Event bus monitoring and alerting */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createAlertManager = exports.AlertManager = void 0; const uuid_1 = require("uuid"); const types_1 = require("./types"); const utils_1 = require("./utils"); /** * Alert manager for event bus monitoring */ class AlertManager { /** * Create a new alert manager * * @param thresholds - Alert thresholds */ constructor(thresholds) { /** * Alert handlers */ this.handlers = new Map(); /** * Active alerts */ this.activeAlerts = new Map(); /** * Alert history */ this.alertHistory = []; /** * Maximum number of alerts to keep in history */ this.maxHistorySize = 1000; /** * Alert thresholds */ this.thresholds = { failedPublishesThreshold: 5, // 5% deadLetterQueueSizeThreshold: 10, reconnectionAttemptsThreshold: 3, processingTimeThreshold: 1000 // 1 second }; if (thresholds) { this.thresholds = { ...this.thresholds, ...thresholds }; } } /** * Register an alert handler * * @param handler - Alert handler * @returns Handler ID */ registerHandler(handler) { const handlerId = (0, uuid_1.v4)(); this.handlers.set(handlerId, handler); return handlerId; } /** * Unregister an alert handler * * @param handlerId - Handler ID */ unregisterHandler(handlerId) { this.handlers.delete(handlerId); } /** * Check metrics for alerts * * @param metrics - Event bus metrics */ checkMetrics(metrics) { // Check failed publishes if (metrics.publishedEvents > 0) { const failedPublishesPercentage = (metrics.failedPublishes / metrics.publishedEvents) * 100; if (failedPublishesPercentage >= this.thresholds.failedPublishesThreshold) { this.createOrUpdateAlert(types_1.AlertType.FAILED_PUBLISHES, failedPublishesPercentage >= this.thresholds.failedPublishesThreshold * 2 ? types_1.AlertSeverityMapping.ERROR : types_1.AlertSeverityMapping.WARNING, `Failed publishes rate of ${failedPublishesPercentage.toFixed(2)}% exceeds threshold of ${this.thresholds.failedPublishesThreshold}%`, { failedPublishes: metrics.failedPublishes, publishedEvents: metrics.publishedEvents, percentage: failedPublishesPercentage }); } else { this.resolveAlert(types_1.AlertType.FAILED_PUBLISHES); } } // Check dead letter queue size if ('deadLetterQueueSize' in metrics && metrics.deadLetterQueueSize > 0) { if (metrics.deadLetterQueueSize >= this.thresholds.deadLetterQueueSizeThreshold) { this.createOrUpdateAlert(types_1.AlertType.DEAD_LETTER_QUEUE_SIZE, metrics.deadLetterQueueSize >= this.thresholds.deadLetterQueueSizeThreshold * 2 ? types_1.AlertSeverityMapping.ERROR : types_1.AlertSeverityMapping.WARNING, `Dead letter queue size of ${metrics.deadLetterQueueSize} exceeds threshold of ${this.thresholds.deadLetterQueueSizeThreshold}`, { deadLetterQueueSize: metrics.deadLetterQueueSize, deadLetterQueueEvents: metrics.deadLetterQueueEvents }); } else { this.resolveAlert(types_1.AlertType.DEAD_LETTER_QUEUE_SIZE); } } // Check reconnection attempts if (metrics.reconnectionAttempts > 0) { if (metrics.reconnectionAttempts >= this.thresholds.reconnectionAttemptsThreshold) { this.createOrUpdateAlert(types_1.AlertType.RECONNECTION_ATTEMPTS, metrics.reconnectionAttempts >= this.thresholds.reconnectionAttemptsThreshold * 2 ? types_1.AlertSeverityMapping.ERROR : types_1.AlertSeverityMapping.WARNING, `Reconnection attempts of ${metrics.reconnectionAttempts} exceeds threshold of ${this.thresholds.reconnectionAttemptsThreshold}`, { reconnectionAttempts: metrics.reconnectionAttempts, lastReconnectionTime: metrics.lastReconnectionTime }); } else { this.resolveAlert(types_1.AlertType.RECONNECTION_ATTEMPTS); } } // Check processing time if (metrics.averageProcessingTime > 0) { if (metrics.averageProcessingTime >= this.thresholds.processingTimeThreshold) { this.createOrUpdateAlert(types_1.AlertType.PROCESSING_TIME, metrics.averageProcessingTime >= this.thresholds.processingTimeThreshold * 2 ? types_1.AlertSeverityMapping.ERROR : types_1.AlertSeverityMapping.WARNING, `Average processing time of ${metrics.averageProcessingTime.toFixed(2)}ms exceeds threshold of ${this.thresholds.processingTimeThreshold}ms`, { averageProcessingTime: metrics.averageProcessingTime }); } else { this.resolveAlert(types_1.AlertType.PROCESSING_TIME); } } } /** * Check connection status * * @param connected - Whether the adapter is connected */ checkConnectionStatus(connected) { if (!connected) { this.createOrUpdateAlert(types_1.AlertType.CONNECTION_STATUS, types_1.AlertSeverityMapping.ERROR, 'Event bus adapter is disconnected', { connected: false, timestamp: new Date() }); } else { this.resolveAlert(types_1.AlertType.CONNECTION_STATUS); } } /** * Create or update an alert * * @param type - Alert type * @param severity - Alert severity * @param message - Alert message * @param details - Alert details */ createOrUpdateAlert(type, severity, message, details) { // Check if alert already exists const existingAlert = this.getActiveAlertByType(type); if (existingAlert) { // Update existing alert existingAlert.severity = severity; existingAlert.message = message; if (details) { existingAlert.details = details; } else if ('details' in existingAlert) { delete existingAlert.details; } utils_1.logger.warn(`Updated alert: ${message}`); } else { // Create new alert const alert = { id: (0, uuid_1.v4)(), type, severity, message, timestamp: new Date(), ...(details ? { details } : {}), resolved: false }; // Add to active alerts this.activeAlerts.set(alert.id, alert); // Add to history this.addToHistory(alert); // Notify handlers this.notifyHandlers(alert); utils_1.logger.warn(`New alert: ${message}`); } } /** * Resolve an alert * * @param type - Alert type */ resolveAlert(type) { // Find alert by type const alert = this.getActiveAlertByType(type); if (alert) { // Mark as resolved alert.resolved = true; alert.resolvedAt = new Date(); // Remove from active alerts this.activeAlerts.delete(alert.id); // Add to history this.addToHistory(alert); // Notify handlers this.notifyHandlers(alert); utils_1.logger.info(`Resolved alert: ${alert.message}`); } } /** * Get active alert by type * * @param type - Alert type * @returns Alert or undefined */ getActiveAlertByType(type) { for (const alert of this.activeAlerts.values()) { if (alert.type === type) { return alert; } } return undefined; } /** * Add alert to history * * @param alert - Alert */ addToHistory(alert) { this.alertHistory.push(alert); // Trim history if needed if (this.alertHistory.length > this.maxHistorySize) { this.alertHistory = this.alertHistory.slice(-this.maxHistorySize); } } /** * Notify handlers of an alert * * @param alert - Alert */ notifyHandlers(alert) { for (const handler of this.handlers.values()) { try { handler(alert); } catch (error) { utils_1.logger.error(`Error in alert handler: ${error instanceof Error ? error.message : String(error)}`, error); } } } /** * Get active alerts * * @param type - Optional alert type to filter by * @returns Active alerts */ getActiveAlerts(type) { const alerts = Array.from(this.activeAlerts.values()); if (type) { return alerts.filter(alert => alert.type === type); } return alerts; } /** * Get alert history * * @param limit - Maximum number of alerts to return * @param type - Optional alert type to filter by * @returns Alert history */ getAlertHistory(limit, type) { let alerts = [...this.alertHistory]; // Filter by type if specified if (type) { alerts = alerts.filter(alert => alert.type === type); } // Sort by timestamp (newest first) alerts.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime()); // Limit if specified if (limit && limit > 0) { alerts = alerts.slice(0, limit); } return alerts; } /** * Clear alert history */ clearAlertHistory() { this.alertHistory = []; } } exports.AlertManager = AlertManager; /** * Create a new alert manager * * @param thresholds - Alert thresholds * @returns Alert manager */ function createAlertManager(thresholds) { return new AlertManager(thresholds); } exports.createAlertManager = createAlertManager; //# sourceMappingURL=monitoring.js.map