@mdf.js/logger
Version:
MMS - API Logger - Enhanced logger library for mms artifacts
364 lines • 14.3 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.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