UNPKG

@mdf.js/logger

Version:

MMS - API Logger - Enhanced logger library for mms artifacts

364 lines 14.3 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.Logger = exports.LoggerSchema = void 0; const tslib_1 = require("tslib"); const crash_1 = require("@mdf.js/crash"); const debug_1 = tslib_1.__importDefault(require("debug")); const fs_1 = tslib_1.__importDefault(require("fs")); const joi_1 = tslib_1.__importDefault(require("joi")); const uuid_1 = require("uuid"); const winston_1 = tslib_1.__importDefault(require("winston")); const console_1 = require("./console"); const file_1 = require("./file"); const fluentd_1 = require("./fluentd"); /** Logger transports validation schema */ exports.LoggerSchema = joi_1.default.object({ console: console_1.ConsoleTransportSchema.optional(), file: file_1.FileTransportSchema.optional(), fluentd: fluentd_1.FluentdTransportSchema.optional(), }).default(); /** Class Logger, manage the event and log register process for netin artifacts */ class Logger { constructor(label = 'mdf-app', configuration) { this.label = label; /** Default config */ this.defaultConfig = exports.LoggerSchema.validate({}).value; /** Transports */ this.transports = []; /** Number of actual configured transports */ this.numberOfTransports = 0; /** Actual process id */ this.pid = process.pid.toString(); /** Logger configuration was wrong flag */ this.errorInConfig = false; /** Logger instance UUID */ this.uuid = (0, uuid_1.v4)(); // Stryker disable all this._debug = (0, debug_1.default)('mdf:logger'); this._debug(`${process.pid} - New instance of logger for ${label}`); this._debug(`${process.pid} - Configuration in the constructor %O`, configuration); // Stryker enable all this._config = this.initialize(label, configuration); this.numberOfTransports = this.transports.length; // Stryker disable next-line all this._debug(`${process.pid} - Number of transports configured ${this.numberOfTransports}`); this.logger = winston_1.default.createLogger({ transports: [...this.transports], }); if (this.errorInConfig && this._configError) { this.error(this._configError.message, this.uuid, 'logger', this._configError); } } /** * Establish the logger configuration * @param label - Logger label * @param configuration - logger configuration */ initialize(label, configuration) { const validation = exports.LoggerSchema.validate(configuration); if (validation.error) { // Stryker disable next-line all this._debug(`${process.pid} - Error in the configuration, default will be applied`); this._configError = new crash_1.Multi(`Logger configuration Error`, this.uuid); this._configError.Multify(validation.error); this.errorInConfig = true; // Stryker disable next-line all this._debug(`${process.pid} - Error: %O`, this._configError.toJSON()); this._config = this.defaultConfig; } else { // Stryker disable next-line all this._debug(`${process.pid} - The configuration is valid`); this.label = label; this._config = validation.value; } this.atLeastOne(this._config, configuration); // Stryker disable next-line all this._debug(`${process.pid} - Final configuration %O`, this._config); if (this._config.file) { this.setFileTransport(label, this._config.file); } if (this._config.console) { this.setConsoleTransport(label, this._config.console); } if (this._config.fluentd) { this.setFluentdTransport(label, this._config.fluentd); } return this._config; } /** * Establish the logger configuration * @param label - Logger label * @param configuration - logger configuration */ setConfig(label, configuration) { // Stryker disable next-line all this._debug(`${process.pid} - Setting the configuration by the process`); if (this.numberOfTransports > 0) { // Stryker disable next-line all this._debug(`${process.pid} - The are ${this.numberOfTransports} transport to remove`); this.logger.transports.forEach(transport => { this.logger.remove(transport); }); this.transports = []; } this._config = this.initialize(label, configuration); this.numberOfTransports = this.transports.length; // Stryker disable next-line all this._debug(`${process.pid} - Number of transports configured ${this.numberOfTransports}`); this.transports.forEach(transport => this.logger.add(transport)); } /** * Establish the file transport configuration * @param label - Logger label * @param configuration - logger configuration */ setFileTransport(label, config) { if (config.enabled) { const _file = new file_1.FileTransport(label, this.uuid, config); if (_file.transport) { this.transports.push(_file.transport); } } } /** * Establish the console transport configuration * @param label - Logger label * @param configuration - logger configuration */ setConsoleTransport(label, config) { if (config.enabled) { const _console = new console_1.ConsoleTransport(label, this.uuid, config); if (_console.transport) { this.transports.push(_console.transport); } } } /** * Establish the fluentd transport configuration * @param label - Logger label * @param config - logger configuration */ setFluentdTransport(label, config) { if (config.enabled) { const _fluentd = new fluentd_1.FluentdTransport(label, this, this.uuid, config); if (_fluentd.transport) { this.transports.push(_fluentd.transport); } } } /** * Log events in the SILLY level: all the information in a very detailed way. * This level used to be necessary only in the development process, and the meta data used to be * the results of the operations. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ silly(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.silly(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the DEBUG level: all the information in a detailed way. * This level used to be necessary only in the debugging process, so not all the data is * reported, only the related with the main processes and tasks. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ debug(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.debug(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the VERBOSE level: trace information without details. * This level used to be necessary only in system configuration process, so information about * the settings and startup process used to be reported. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ verbose(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.verbose(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the INFO level: only relevant events are reported. * This level is the default level. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ info(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.info(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the WARN level: information about possible problems or dangerous situations. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ warn(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.warn(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the ERROR level: all the errors and problems with detailed information. * @param message - human readable information to log * @param uuid - unique identifier for the actual job/task/request process * @param context - context (class/function) where this logger is logging * @param meta - extra information */ error(message, uuid, context, ...meta) { if (this.numberOfTransports > 0) { this.logger.error(message, { timestamp: new Date().toISOString(), pid: this.pid, uuid, context, meta, }); } } /** * Log events in the ERROR level: all the information in a very detailed way. * This level used to be necessary only in the development process. * @param error - crash error instance * @param context - context (class/function) where this logger is logging */ crash(error, context) { if (this.numberOfTransports > 0) { this.logger.error(error.message, { timestamp: new Date().toISOString(), pid: this.pid, uuid: error.uuid, context, cause: error.toJSON(), info: error.info, }); } } /** Stream de escritura del propio logger */ get stream() { const _pid = this.pid; const _label = this.label; const _logger = this.logger; return { write: function (message) { const jsonObject = JSON.parse(message); jsonObject['pid'] = _pid; jsonObject['label'] = _label; _logger.log(jsonObject); }, }; } /** Logger config */ get config() { return this._config; } /** Logger configuration error flag */ get hasError() { return this.errorInConfig; } /** Logger configuration errors, if exist */ get configError() { return this._configError; } /** Determine if the component is running in a docker instance or not */ isDocker() { if (this._isDocker === undefined) { let hasDockerEnv = true; let hasDockerCGroup = true; try { fs_1.default.statSync('./.dockerenv'); } catch (error) { hasDockerEnv = false; } try { return fs_1.default.readFileSync('/proc/self/cgroup', 'utf8').includes('docker'); } catch (error) { hasDockerCGroup = false; } this._isDocker = hasDockerEnv || hasDockerCGroup; } return this._isDocker; } /** * Set at least one transport to true if no transport is set but a configuration has been * indicated * @param finalConfig - Final configuration * @param configuration - Initial configuration */ atLeastOne(finalConfig, configuration) { var _a, _b, _c; if (((_a = finalConfig.console) === null || _a === void 0 ? void 0 : _a.enabled) || ((_b = finalConfig.file) === null || _b === void 0 ? void 0 : _b.enabled) || ((_c = finalConfig.fluentd) === null || _c === void 0 ? void 0 : _c.enabled)) { return; } if (configuration !== undefined) { // Stryker disable next-line all this._debug(`Configuration error with not empty configuration: %O`, configuration); if (this.isDocker()) { // Stryker disable next-line all this._debug(`Running in docker instance, console will be enabled`); finalConfig.console = { enabled: true }; } else { // Stryker disable next-line all this._debug(`Running in a NOT docker instance, file will be enabled`); finalConfig.file = { enabled: true }; } } } } exports.Logger = Logger; //# sourceMappingURL=logger.js.map