UNPKG

@mdf.js/service-registry

Version:

MMS - API - Service Registry

156 lines 6.81 kB
"use strict"; /** * Copyright 2024 Mytra Control S.L. All rights reserved. * * Use of this source code is governed by an MIT-style license that can be found in the LICENSE file * or at https://opensource.org/licenses/MIT. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Aggregator = void 0; const tslib_1 = require("tslib"); const crash_1 = require("@mdf.js/crash"); const events_1 = tslib_1.__importDefault(require("events")); const types_1 = require("../types"); /** * The Aggregator class is responsible for aggregating and managing error events * from various components within an application. It allows for centralized error * handling, supporting a structured approach to error logging and potentially * error recovery strategies. * * It extends EventEmitter to emit error events, enabling other parts of the * application to listen and respond to these error events as needed. */ class Aggregator extends events_1.default { /** * Creates an instance of Aggregator. * @param logger - Logger instance for logging error registration and handling. * @param maxSize - Maximum number of errors to keep in the registry. Older errors are removed as * new ones are added. * @param includeStack - Flag to determine if stack traces should be included in the error * records. */ constructor(logger, maxSize = types_1.DEFAULT_CONFIG_REGISTER_MAX_LIST_SIZE, includeStack = types_1.DEFAULT_CONFIG_REGISTER_INCLUDE_STACK) { super(); this.logger = logger; this.maxSize = maxSize; this.includeStack = includeStack; /** Hold registered errors */ this._errors = []; /** Hold registered errors from workers */ this._workersErrors = []; /** Timestamp of the last update */ this._lastUpdate = new Date().toISOString(); /** Map to keep track of registered components */ this.components = new Map(); /** * Generates an error handling function for a specific component. * @param subject - The name of the component. * @returns A function that takes an error, converts it, and adds it to the registry. */ this.errorEventHandler = (subject) => { return (rawError) => { const error = crash_1.Crash.from(rawError); error.subject = error.subject === 'common' ? subject : error.subject; this.push(error); }; }; // Stryker disable next-line all this.logger.debug(`New error aggregator instance created: ${JSON.stringify({ maxSize, includeStack })}`); if (maxSize < 1) { this.maxSize = types_1.DEFAULT_CONFIG_REGISTER_MAX_LIST_SIZE; // Ensure maxSize is at least 1 // Stryker disable next-line all this.logger.warn(`Error aggregator maxSize must be greater than 0. Defaulting to ${types_1.DEFAULT_CONFIG_REGISTER_MAX_LIST_SIZE}`); } } /** * Register a component or a list of components to monitor for errors. * @param component - Component or list of components to be registered */ register(component) { const _components = Array.isArray(component) ? component : [component]; for (const entry of _components) { if (this.isValidComponent(entry) && !this.components.has(entry.name)) { // Stryker disable next-line all this.logger.debug(`Registering error handler for component: ${entry.name}`); entry.on('error', this.errorEventHandler(entry.name)); // Attach error handler this.components.set(entry.name, entry); if ('error' in entry && entry.error instanceof Error) { // Stryker disable next-line all this.logger.debug(`Registering pre-existing error from component: ${entry.name}`); this.errorEventHandler(entry.name)(entry.error); // Register pre-existing error } } } } /** * Adds an error to the registry, converting it to a structured format. * @param error - The error to register. */ push(error) { const validatedError = crash_1.Crash.from(error); // Convert to Crash instance let errorRecord; if (this.includeStack) { errorRecord = { ...validatedError.toJSON(), stack: validatedError.fullStack(), // Include full stack trace }; } else { errorRecord = validatedError.toJSON(); } this._errors.push(errorRecord); // Add error to registry if (this._errors.length > this.maxSize) { this._errors.shift(); // Remove oldest error if registry is full } this._lastUpdate = new Date().toISOString(); // Update last update timestamp // Stryker disable next-line all this.logger.debug(`Error registered: ${errorRecord.uuid}`); if (this.listenerCount('error') > 0) { this.emit('error', error); // Emit error event } } /** * Updates the registry with errors from worker threads/processes. * @param errors - Array of errors from workers. */ updateWorkersErrors(errors) { this._workersErrors = errors; this._lastUpdate = new Date().toISOString(); // Update last update timestamp // Stryker disable next-line all this.logger.debug(`Worker errors updated: ${JSON.stringify(errors)}`); } /** @returns Last update date */ get lastUpdate() { return this._lastUpdate; } /** @returns Returns a combined list of all the registered errors */ get errors() { return [...this._errors, ...this._workersErrors]; } /** @returns The current number of registered errors */ get size() { return this._errors.length + this._workersErrors.length; } /** Clear the error registry */ clear() { this._errors = []; } /** Cleans up by removing error event listeners and clearing the registry. */ close() { for (const [, component] of this.components) { component.off('error', this.errorEventHandler(component.name)); // Stryker disable next-line all this.logger.debug(`Error handler removed for component: ${component.name}`); } this.components.clear(); this.clear(); // Stryker disable next-line all this.logger.debug('Error aggregator closed'); } /** Check if the check is valid to be monitored */ isValidComponent(component) { return 'name' in component && 'on' in component && 'off' in component; } } exports.Aggregator = Aggregator; //# sourceMappingURL=Aggregator.js.map