@dyihoon90/glogging
Version:
HTTP request logging middleware & transaction function decorator for express, using winston
295 lines • 14.3 kB
JavaScript
"use strict";
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.__test__ = exports.GLogger = void 0;
/* eslint-disable @typescript-eslint/no-explicit-any */
var is_secret_1 = __importDefault(require("is-secret"));
var winston_1 = __importStar(require("winston"));
var GLogger_interface_1 = require("./domainModels/GLogger.interface");
var core_1 = require("@js-joda/core");
var ObjUtils_1 = require("./utils/ObjUtils");
var lodash_1 = require("lodash");
var json_stringify_safe_1 = __importDefault(require("json-stringify-safe"));
var serialize_error_1 = require("serialize-error");
var DEFAULT_CONFIG = { loggingMode: GLogger_interface_1.LoggingMode.PRODUCTION };
/**
* How to use this class:
* Initialize a new instance in your codebase. Use this instance as a singleton through the codebase.
* if loggingMode is not provided, defaults to LoggingMode.PRODUCTION
*/
var GLogger = /** @class */ (function () {
function GLogger(inputConfigs) {
var _a;
this.verboseMode = false;
this.loggingMode = GLogger_interface_1.LoggingMode.PRODUCTION;
this.loggingLevel = GLogger_interface_1.LoggingLevel.INFO;
var configs = __assign(__assign({}, DEFAULT_CONFIG), inputConfigs);
this.loggingMode = configs.loggingMode;
switch (this.loggingMode) {
case GLogger_interface_1.LoggingMode.LOCAL:
this.loggingLevel = GLogger_interface_1.LoggingLevel.DEBUG;
this.winstonLogger = winston_1.default.createLogger({
level: 'debug',
format: winston_1.format.combine(formatTimestamp(), sensitiveDataRedacter)
});
this.winstonLogger.add(new winston_1.transports.Console({
format: winston_1.format.combine(winston_1.format.printf(consoleMessageFormatterFactory(configs)))
}));
break;
case GLogger_interface_1.LoggingMode.DEV:
this.loggingLevel = GLogger_interface_1.LoggingLevel.INFO;
this.winstonLogger = winston_1.default.createLogger({
level: 'info',
format: winston_1.format.combine(formatTimestamp(), sensitiveDataRedacter)
});
this.winstonLogger.add(new winston_1.transports.Console({
format: winston_1.format.combine(winston_1.format.printf(consoleMessageFormatterFactory(configs)))
}));
break;
case GLogger_interface_1.LoggingMode.PRODUCTION:
default:
this.loggingLevel = GLogger_interface_1.LoggingLevel.INFO;
this.winstonLogger = winston_1.default.createLogger({
level: 'info',
format: winston_1.format.combine(formatTimestamp(), sensitiveDataRedacter)
});
if ((_a = configs.overrideDefault) === null || _a === void 0 ? void 0 : _a.alwaysWriteToConsole) {
this.winstonLogger.add(new winston_1.transports.Console({
format: winston_1.format.combine(winston_1.format.printf(consoleMessageFormatterFactory(configs)))
}));
}
}
}
GLogger.prototype.toggleVerboseModeOn = function () {
this.verboseMode = true;
};
/**
* Add a winston transport to this LogUtil instance
* @param transport a winston-transport Log Transport instance
*/
GLogger.prototype.addLogTransport = function (transport) {
this.winstonLogger.add(transport);
return this;
};
/**
* Creates a log object of level debug
* @example
* info('msg', {mydata: "data"})
* // creates the following log object
* {message: 'msg', level: 'debug', mydata: 'data'}
* @param data any additional relevant data, as a javascript object.
* If it contains a `message` property, the string is appended
* If it contains a `level` property, that is ignored
*/
GLogger.prototype.debug = function (message, data) {
if (this.verboseMode) {
logVerbose('debug', message, data);
}
if (this.loggingLevel <= GLogger_interface_1.LoggingLevel.DEBUG) {
this.winstonLogger.debug(message, data);
}
return this;
};
/**
* Creates a log object of level info
* @example
* info('msg', {mydata: "data"})
* // creates the following log object
* {message: 'msg', level: 'info', mydata: 'data'}
* @param data any additional relevant data, as a javascript object.
* If it contains a `message` property, the string is appended
* If it contains a `level` property, that is ignored
*/
GLogger.prototype.info = function (message, data) {
if (this.verboseMode) {
logVerbose('info', message, data);
}
if (this.loggingLevel <= GLogger_interface_1.LoggingLevel.INFO) {
this.winstonLogger.info(message, data);
}
return this;
};
/**
* Creates a log object of level warn
* @example
* warn('msg', new Error('error msg'), {mydata: "data"})
* // creates the following log object
* {message: 'msg', level: 'warn', mydata: 'data', additionalInfo: {error: {stack: 'errorstack!',message:'error msg',name:'Error'}}}
* @param data any additional relevant data, as a javascript object.
* If it contains a `message` property, the string is appended
* If it contains a `level` property, that is ignored
*/
GLogger.prototype.warn = function (message, error, data) {
if (this.verboseMode) {
logVerbose('warn', message, data, error);
}
var dataToLog = data ? __assign({}, data) : {};
if (error) {
dataToLog.additionalInfo = __assign(__assign({}, dataToLog.additionalInfo), { error: (0, serialize_error_1.serializeError)(error, { maxDepth: 5 }) });
}
if (this.loggingLevel <= GLogger_interface_1.LoggingLevel.WARN) {
this.winstonLogger.warn(message, dataToLog);
}
return this;
};
/**
* Creates a log object of level error
* @example
* error('msg', new Error('error msg'), {mydata: "data"})
* // creates the following log object
* {message: 'msg', level: 'error', mydata: 'data', additionalInfo: {error: {stack: 'errorstack!',message:'error msg',name:'Error'}}}
* @param data any additional relevant data, as a javascript object.
* If it contains a `message` property, the string is appended
* If it contains a `level` property, that is ignored
*/
GLogger.prototype.error = function (message, error, data) {
if (this.verboseMode) {
logVerbose('error', message, data, error);
}
var dataToLog = data ? __assign({}, data) : {};
if (error) {
dataToLog.additionalInfo = __assign(__assign({}, dataToLog.additionalInfo), { error: (0, serialize_error_1.serializeError)(error) });
}
if (this.loggingLevel <= GLogger_interface_1.LoggingLevel.ERROR) {
this.winstonLogger.error(message, dataToLog);
}
return this;
};
return GLogger;
}());
exports.GLogger = GLogger;
/**
* Formatter for console logging in GLogging
* Depending on whether trxCategory is passed in, either logs out basicLog or enrichedLog
* @param info
*/
var consoleMessageFormatterFactory = function (config) {
return function (info) {
var _a, _b, _c;
var sectionSeparator = ((_a = config.overrideDefault) === null || _a === void 0 ? void 0 : _a.consoleLogSectionSeparator) === '' || ((_b = config.overrideDefault) === null || _b === void 0 ? void 0 : _b.consoleLogSectionSeparator)
? (_c = config.overrideDefault) === null || _c === void 0 ? void 0 : _c.consoleLogSectionSeparator
: '\n';
var _d = info, level = _d.level, message = _d.message, timestamp = _d.timestamp, additionalInfo = _d.additionalInfo, filename = _d.filename, data = __rest(_d, ["level", "message", "timestamp", "additionalInfo", "filename"]);
var logString = "[".concat(timestamp, "][").concat(level.toUpperCase(), "]");
var trxCategory = data.trxCategory, trxId = data.trxId, trxModule = data.trxModule, trxName = data.trxName, trxStatus = data.trxStatus, timeTakenInMillis = data.timeTakenInMillis, userToken = data.userToken;
if (!trxCategory) {
var basicLog = logString
.concat("[".concat(message, "][").concat(data ? formatWithLinebreakAndIndent(data, config) : 'no data', "]"))
.concat(sectionSeparator)
.concat("[".concat(additionalInfo ? formatWithLinebreakAndIndent(additionalInfo, config) : 'no additionalInfo', "]"))
.concat(sectionSeparator);
return basicLog;
}
var enrichedLog = logString
.concat("[".concat(trxCategory, "][").concat(trxModule, "][").concat(trxId, "][").concat(trxName, "][").concat(trxStatus, "][").concat((timeTakenInMillis === null || timeTakenInMillis === void 0 ? void 0 : timeTakenInMillis.toString()) || 'time taken is not tracked', "ms]"))
.concat("[".concat(message, "]"))
.concat(sectionSeparator)
.concat("[".concat(userToken ? formatWithLinebreakAndIndent(userToken, config) : 'no user token', "]"))
.concat(sectionSeparator)
.concat("[".concat(additionalInfo ? formatWithLinebreakAndIndent(additionalInfo, config) : 'no additionalInfo', "]"))
.concat(sectionSeparator);
return filename ? enrichedLog.concat("[".concat(filename, "]")) : enrichedLog;
};
};
var formatTimestamp = winston_1.default.format(function (info) {
info.timestamp = core_1.ZonedDateTime.now().format(core_1.DateTimeFormatter.ISO_OFFSET_DATE_TIME);
return info;
});
function formatWithLinebreakAndIndent(obj, config) {
var _a, _b, _c, _d;
try {
var separator = ((_a = config === null || config === void 0 ? void 0 : config.overrideDefault) === null || _a === void 0 ? void 0 : _a.consoleLogSectionSeparator) === '' || ((_b = config === null || config === void 0 ? void 0 : config.overrideDefault) === null || _b === void 0 ? void 0 : _b.consoleLogSectionSeparator)
? (_c = config === null || config === void 0 ? void 0 : config.overrideDefault) === null || _c === void 0 ? void 0 : _c.consoleLogSectionSeparator
: '\n';
return (_d = (0, json_stringify_safe_1.default)(obj, null, 1)) === null || _d === void 0 ? void 0 : _d.replace(/\\n/g, separator).replace(/\n/g, separator);
}
catch (e) {
return 'Object cannot be stringified.';
}
}
function logVerbose(level, message, data, error) {
console.log("[GLogger] ".concat(level, "() received message: ").concat(message));
if (data) {
console.log("[GLogger] ".concat(level, "() received data: ").concat(formatWithLinebreakAndIndent(data)));
}
if (error) {
console.log("[GLogger] ".concat(level, "() received error: ").concat(error.toString()));
}
}
var sensitiveDataRedacter = winston_1.default.format(function (info) {
var clonedInfo = (0, lodash_1.cloneDeep)(info);
(0, ObjUtils_1.traverseAndMutateObject)(clonedInfo, redactSensitiveValue);
return clonedInfo;
})();
/**
* Redacts all sensitive values
* @param key property key of the object
* @param value value of the object's property
*/
function redactSensitiveValue(key, value) {
if (is_secret_1.default.key(key) || (typeof value === 'string' && is_secret_1.default.value(value))) {
return '[REDACTED]';
}
var nricRegex = /^([a-z]\d{7}[a-z])$/i;
if (typeof value === 'string' && nricRegex.test(value)) {
return '*****' + value.substring(5);
}
// for cases of array of nrics or uinfin, we redact the entire property
var nricKeyRegex = /(nric|uinfin)/i;
if (nricKeyRegex.test(key) && (0, lodash_1.isArray)(value)) {
return '[REDACTED DUE TO NRIC OR UINFIN KEY]';
}
return value;
}
// eslint-disable-next-line no-underscore-dangle
exports.__test__ = {
formatWithLinebreakAndIndent: formatWithLinebreakAndIndent
};
//# sourceMappingURL=GLogger.js.map