@sailboat-computer/event-bus
Version:
Standardized event bus for sailboat computer v3 with resilience features and offline capabilities
308 lines • 10.6 kB
JavaScript
"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