@mdf.js/service-registry
Version:
MMS - API - Service Registry
156 lines • 6.81 kB
JavaScript
"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