@kcutils/logger
Version:
Another Logger Service
553 lines • 22.8 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;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (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 __spreadArrays = (this && this.__spreadArrays) || function () {
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
for (var r = Array(s), k = 0, i = 0; i < il; i++)
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
r[k] = a[j];
return r;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Logger = void 0;
var util_1 = require("util");
var path_1 = require("path");
var readline_1 = require("readline");
var helper_1 = require("@kcutils/helper");
var chalk_1 = require("chalk");
var Badge = __importStar(require("figures"));
var settings_1 = require("../../constants/settings");
var types_1 = require("../../constants/types");
var levels_1 = require("../../constants/levels");
var LoggerOptionBuilder_1 = require("../../builder/LoggerOptionBuilder");
var longestLabel = "longest-label";
//* [ metadata ] [ prefix ] [ data ] [suffix]
//* [datetime] [scope] [filename] [separator] [badge] [label] [message] [suffix]
//* [YYYY-MM-DD] global text.js ↦ ¤ [label] this this a message (suffix)
var Logger = /** @class */ (function () {
/**
* Never create Logger object directly,
* Please use LoggerBuilder or Logger.create() instead
*
* @param option - logger option
*/
function Logger(option) {
this._id = Logger.counter;
Logger.counter++;
this._option = option;
this._types = helper_1.json.deepMerge(types_1.types, this._option.getTypes());
this._setting = helper_1.json.deepMerge(settings_1.settings, this._option.getSettings());
this.idebug("create new logger (id = " + this._id + ")");
this.idebug("option: ", JSON.stringify(this._option));
this.idebug("setting: ", JSON.stringify(this._setting));
this.idebug("types: ", JSON.stringify(this._types));
this._timers = new Map();
this._color = new chalk_1.Instance(this._option.isColor() ? {} : { level: 0 });
this._isPreviousLogInteractive = false;
this._parameters = new Map();
this._parameters.set(longestLabel, this.getLongestLabel());
}
Logger.create = function (builder) {
if (builder === void 0) { builder = LoggerOptionBuilder_1.LoggerOptionBuilder.initial(); }
return new Logger(builder.get());
};
Object.defineProperty(Logger.prototype, "id", {
get: function () {
return this._id;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "levels", {
/**
* all log levels
*/
get: function () {
return levels_1.levels;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "scopes", {
/**
* current scopes
*/
get: function () {
return this._option.getScopes();
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "option", {
/**
* get current options as readonly
*/
get: function () {
return this._option.toStrictOption();
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "setting", {
get: function () {
return Object.assign({}, this._setting);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "type", {
get: function () {
return Object.assign({}, this._types);
},
enumerable: false,
configurable: true
});
// print logger message to stream
Logger.prototype.print = function (_type, data) {
var type = this._types[_type];
if (!this.shouldLog(type))
return;
var level = levels_1.toLevel(type.level);
var inputOption = typeof data === "string" ? { message: data } : data;
var _stream = this._option.isOverrideStream() ? this._option.getOverrideStream() : level.stream;
var _streams = helper_1.array.toArray(_stream);
if (inputOption.stream) {
var streams = helper_1.array.toArray(inputOption.stream);
_streams = inputOption.appendStream ? _streams.concat.apply(_streams, streams) : streams;
}
var message = this.build(_type, data);
this.writing(_streams, message);
};
// build logger message
Logger.prototype.build = function (_type, input) {
var type = this._types[_type];
if (!this.shouldLog(type))
return "";
var inputOption = typeof input === "string" ? { message: input } : input;
var metadata = this.buildMetadata(inputOption.timestamp, inputOption.scopes);
var prefix = this.buildPrefix(type, inputOption.label, inputOption.prefix);
var data = this.buildMessage(inputOption.message);
var suffix = this.buildSuffix(inputOption.suffix);
if (this._option.isJson())
return this.buildAsJson(type, { metadata: metadata, prefix: prefix, data: data, suffix: suffix });
else
return this.buildAsString(type, { metadata: metadata, prefix: prefix, data: data, suffix: suffix });
};
Logger.prototype.startTimer = function (_label, message) {
var label = _label !== null && _label !== void 0 ? _label : "timer_" + this._timers.size;
this._timers.set(label, Date.now());
this.print("start", { message: message !== null && message !== void 0 ? message : "Initialized timer...", label: label });
return label;
};
Logger.prototype.endTimer = function (label, message) {
if (this._timers.size < 1)
return { label: label, timestamp: 0 };
var _label = label;
if (!_label) {
var is_1 = function (s) { return s && s.includes("timer_"); };
_label = Array.from(this._timers.keys()).reduceRight(function (x, y) {
return is_1(x) ? x : is_1(y) ? y : "";
}, "");
}
if (this._timers.has(_label)) {
var previous = this._timers.get(_label);
var current = Date.now();
var timestamp = current - previous;
var time = timestamp < 1000 ? timestamp + " ms" : (timestamp / 1000).toFixed(2) + " s";
this._timers.delete(_label);
this.print("stop", {
message: message !== null && message !== void 0 ? message : "Timer run for: ",
suffix: time,
label: _label,
});
return { label: _label, timestamp: timestamp };
}
return { label: _label, timestamp: 0 };
};
/**
* remove all secret found in input string
*
* @param message - input string
*/
Logger.prototype.censor = function (message) {
var _this = this;
var secrets = this._option.getSecrets();
if (secrets.length === 0)
return message;
return secrets.reduce(function (msg, secret) {
var regex = new RegExp(secret, "gi");
var s = _this._option.onCensor(secret);
var formatting = _this.format(s, _this._setting.secret, undefined, ["censor"]);
return msg.replace(regex, formatting);
}, message);
};
Logger.prototype.isContainSecret = function (message) {
var secrets = this._option.getSecrets();
if (secrets.length === 0)
return false;
return secrets.some(function (secret) {
var regex = new RegExp(secret, "g");
return regex.test(message);
});
};
/**
* merge and replace new options
* @param option - new partial option
*/
Logger.prototype.options = function (option) {
this.idebug("options parameters: ", option);
this._option = this._option.copy(option);
this.idebug("new options: ", option);
return this;
};
/**
* merge and replace new settings
* @param option - new partial setting
*/
Logger.prototype.settings = function (setting) {
this.idebug("settings parameters: ", setting);
this._setting = helper_1.json.deepMerge(this._setting, setting);
this.idebug("new settings: ", setting);
return this;
};
/**
* create new logger base on current configuration
* @param option - new option merged with current option
* @param setting - new setting merged with current setting
* @param type - new type merged with current type
*/
Logger.prototype.copy = function (option, setting, type) {
var options = this._option.copy(option, { types: type, settings: setting });
return new Logger(options);
};
Logger.prototype.isEnabled = function () {
return !this._option.isDisabled();
};
/**
* remove all scope on this logger
*/
Logger.prototype.unscope = function () {
this._option.set("scopes", []);
return this;
};
/**
* remove all secret on this logger
*/
Logger.prototype.unsecret = function () {
this._option.set("secrets", []);
return this;
};
/**
* enable color
*/
Logger.prototype.color = function () {
this._option.set("color", true);
this._color.level = chalk_1.level;
return this;
};
/**
* disable color
*/
Logger.prototype.uncolor = function () {
this._option.set("color", false);
this._color.level = 0;
return this;
};
/**
* is color enabled
*/
Logger.prototype.isColor = function () {
return this._color.level > 0 && this._option.isColor();
};
Logger.prototype.equals = function (l) {
var _this = this;
var keys = Object.keys(this.importantData());
return keys.every(function (k) {
var cond = helper_1.json.equals(l.option[k], _this.option[k]);
if (!cond)
_this.idebug("input logger is not equals because '%s'(%s !== %s)", k, _this.option[k], l.option[k]);
return cond;
});
};
Logger.prototype.toString = function () {
var data = this.importantData();
return "Logger(" + Object.keys(data).map(function (key) { return key + "=" + data[key]; }).join(", ") + ")";
};
Logger.prototype.importantData = function () {
return {
debug: this.option.debug,
level: this.option.level,
interactive: this.option.interactive,
color: this.option.color,
scopes: this.option.scopes,
};
};
Object.defineProperty(Logger.prototype, "filename", {
/**
* get current filename
*/
get: function () {
var _ = Error.prepareStackTrace;
Error.prepareStackTrace = function (_error, stack) { return stack; };
var error = new Error();
var callers = error.stack.map(function (x) { return x.getFileName(); });
var firstExternalFilePath = callers.find(function (x) { return x !== callers[0]; });
Error.prepareStackTrace = _;
return firstExternalFilePath ? path_1.basename(firstExternalFilePath) : "anonymous";
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "date", {
get: function () {
var _ = new Date();
var year = helper_1.string.padStart(_.getFullYear().toFixed(0), 2, "0");
var month = helper_1.string.padStart((_.getMonth() + 1).toFixed(0), 2, "0");
var day = helper_1.string.padStart(_.getDate().toFixed(0), 2, "0");
return [year, month, day].join("-");
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "time", {
get: function () {
var _ = new Date();
var hour = helper_1.string.padStart(_.getHours().toFixed(0), 2, "0");
var min = helper_1.string.padStart(_.getMinutes().toFixed(0), 2, "0");
var sec = helper_1.string.padStart(_.getSeconds().toFixed(0), 2, "0");
var ms = helper_1.string.padStart(_.getMilliseconds().toFixed(0), 3, "0");
return [hour, min, sec].join(":").concat(".", ms);
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "datetime", {
get: function () {
return this.date + " " + this.time;
},
enumerable: false,
configurable: true
});
Object.defineProperty(Logger.prototype, "timestamp", {
get: function () {
var timestamp = +new Date();
return timestamp.toFixed(0);
},
enumerable: false,
configurable: true
});
Logger.prototype.buildAsJson = function (_type, output) {
var level = levels_1.toLevel(_type.level);
return JSON.stringify(__assign(__assign({}, output), { level: { name: level.name, code: level.level } }));
};
Logger.prototype.buildAsString = function (type, output) {
var _this = this;
var _a;
var withSpace = function (i) {
if (helper_1.string.isNotEmpty(i))
return " " + i;
else
return i;
};
var datetime = this.format(output.metadata.datetime.data, this._setting.datetime, this._color.gray);
var scopes = output.metadata.scopes.data
.map(function (scope) { return _this.format(scope, _this._setting.scope, _this._color.gray); })
.filter(function (v) { return helper_1.string.isNotEmpty(v); })
.join(" ");
var filename = this.format(output.metadata.filename.data, this._setting.filename);
var separator = this.format(output.metadata.seperator.data, this._setting.seperator);
var _paddingBadge = helper_1.string.padEnd(output.prefix.badge.data, 2);
var badge = this.format(_paddingBadge, this._setting.badge, type.color(this._color));
var _longestLabelLength = ((_a = this._parameters.get(longestLabel)) !== null && _a !== void 0 ? _a : "").length;
var _paddingLabel = helper_1.string.padEnd(output.prefix.label.data, _longestLabelLength + 1);
var label = this.format(_paddingLabel, this._setting.label, type.color(this._color));
var customPrefix = this.format(output.prefix.custom.data, this._setting.prefix);
var message = this.format(output.data.messages.data, this._setting.message);
var suffix = this.format(output.suffix.custom.data, this._setting.suffix);
var metadata = "" + datetime + withSpace(scopes) + withSpace(filename) + withSpace(separator);
var prefix = "" + badge + withSpace(label) + withSpace(customPrefix);
return "" + metadata + withSpace(prefix) + withSpace(message) + withSpace(suffix);
};
Logger.prototype.buildMetadata = function (timestamp, cusScope) {
var d;
var datetimeType = this._option.getDatetime();
if (timestamp)
d = timestamp;
else if (datetimeType === "time")
d = this.time;
else if (datetimeType === "date")
d = this.date;
else if (datetimeType === "datetime")
d = this.datetime;
else if (datetimeType === "timestamp")
d = this.timestamp;
else
d = this.date; // default
var datetime = { index: 1, data: d };
var scopes = { index: 2, data: this._option.getScopes().concat(cusScope !== null && cusScope !== void 0 ? cusScope : []) };
var filename = { index: 3, data: this.filename };
var seperator = { index: 4, data: this._option.getSeparator() };
var metadata = { datetime: datetime, scopes: scopes, filename: filename, seperator: seperator };
this.idebug("build metadata object: ", metadata);
return metadata;
};
Logger.prototype.buildPrefix = function (type, overrideLabel, customPrefix) {
var _label = helper_1.string.isNotEmpty(overrideLabel) ? overrideLabel : type.label;
var label = { index: 1, data: _label };
var _badge = type.badge(Badge);
var badge = { index: 2, data: _badge };
var custom = { index: 3, data: helper_1.array.toArray(customPrefix !== null && customPrefix !== void 0 ? customPrefix : "") };
var prefix = { label: label, badge: badge, custom: custom };
this.idebug("build prefix object: ", prefix);
return prefix;
};
Logger.prototype.buildMessage = function (msg) {
var messages = { index: 1, data: helper_1.array.toArray(msg) };
var data = { messages: messages };
this.idebug("build data object: ", data);
return data;
};
Logger.prototype.buildSuffix = function (customSuffix) {
var custom = { index: 1, data: helper_1.array.toArray(customSuffix !== null && customSuffix !== void 0 ? customSuffix : "") };
var suffix = { custom: custom };
this.idebug("build metadata object: ", suffix);
return suffix;
};
/**
* check log type should be log or not
* @param type - log type
*/
Logger.prototype.shouldLog = function (type) {
var level = levels_1.toLevel(this._option.getLevel());
var typeLevel = levels_1.toLevel(type.level);
var msg = type.label + "(" + typeLevel.name + ") type for log level " + level.name;
var res = typeLevel.level <= level.level;
if (res)
msg += " is runnable";
else
msg += " is disabled";
this.idebug(msg);
return res;
};
/**
* Never call this method outside constructor. use parameters instead
*/
Logger.prototype.getLongestLabel = function () {
var type = this._types;
var keys = Object.keys(type);
return keys.reduce(function (p, key) {
var label = type[key].label;
return p.length > label.length ? p : label;
}, "");
};
/**
* format msg with settings
*
* @param msg - message to format
* @param settings - formatting settings
* @param color - with color format
*/
Logger.prototype.format = function (input, settings, color, disabledList) {
var _this = this;
if (disabledList === void 0) { disabledList = []; }
var msg = helper_1.array.toArray(input).join(" ");
this.idebug("formatting " + msg + " by", settings);
if (settings === undefined || settings === false)
return "";
var executes = [
["uppercase", settings.uppercase, function (m) { return m.toUpperCase(); }],
["append", settings.prefix !== "" || settings.suffix !== "", function (m) { return settings.prefix + m + settings.suffix; }],
["underline", settings.underline, function (m) { return _this._color.underline(m); }],
["bold", settings.bold, function (m) { return _this._color.bold(m); }],
["italic", settings.italic, function (m) { return _this._color.italic(m); }],
["color", color !== undefined, function (m) { return color(m); }],
["censor", this.isContainSecret(msg), function (m) { return _this.censor(m); }],
];
return executes.reduce(function (p, c) {
var name = c[0];
var condition = c[1];
// disabled
if (disabledList.includes(name)) {
_this.idebug("disabled execute " + name);
return p;
}
var fn = c[2];
if (condition && p !== "") {
_this.idebug("start execute " + name);
return fn(p);
}
else {
_this.idebug("skip execute " + name);
return p;
}
}, msg);
};
/**
* write message to stream
* @param stream - writable stream
* @param message - message
*/
Logger.prototype.writing = function (stream, message) {
var _this = this;
var streams = helper_1.array.toArray(stream);
this.idebug("write message to " + streams.length + " output");
streams.forEach(function (stream) {
if (_this._option.isInteractive() && _this._isPreviousLogInteractive) {
readline_1.moveCursor(stream, 0, -1);
readline_1.clearLine(stream, 0);
readline_1.cursorTo(stream, 0);
}
stream.write(message + "\n");
_this._isPreviousLogInteractive = _this._option.isInteractive();
});
};
/**
* internal debug log with debug is enabled
*
* @param formatter - format string
* @param data - message data
*/
Logger.prototype.idebug = function (formatter) {
var data = [];
for (var _i = 1; _i < arguments.length; _i++) {
data[_i - 1] = arguments[_i];
}
if (this._option.isDebug())
if (data.length === 1)
console.log(util_1.format(formatter, util_1.inspect(data[0], false, 1, false)));
else
console.log(util_1.format.apply(void 0, __spreadArrays([formatter], data)));
};
Logger.counter = 0;
return Logger;
}());
exports.Logger = Logger;
//# sourceMappingURL=Logger.js.map