@ptkdev/logger
Version:
Beautiful Logger for Node.js: the best alternative to the console.log statement
412 lines (361 loc) • 16.2 kB
JavaScript
/**
* Logger: write log
* =====================
*
* @contributors: Patryk Rzucidło [@ptkdev] <support@ptkdev.io> (https://ptk.dev)
* Ilya Chubarov [@agoalofalife] <agoalofalife@gmail.com>
*
* @license: MIT License
*
*/
const path = require("path");
const fse = require("fs-extra");
const chalk = require("chalk");
const ansi = require("strip-ansi");
const rfs = require("rotating-file-stream");
const lowdb = require("lowdb");
const FileSync = require("lowdb/adapters/FileSync");
const languages = {
de: require("../translations/de"),
en: require("../translations/en"),
es: require("../translations/es"),
fr: require("../translations/fr"),
it: require("../translations/it"),
pl: require("../translations/pl"),
pt: require("../translations/pt"),
ru: require("../translations/ru")
};
const logger = console;
let Types = require("./types");
class Log {
constructor(options = new Object) {
if (typeof options.language === "undefined" || options.language === null) {
options.language = "en";
}
Types.INFO.label = languages[options.language]["INFO"];
Types.WARNING.label = languages[options.language]["WARNING"];
Types.ERROR.label = languages[options.language]["ERROR"];
Types.DEBUG.label = languages[options.language]["DEBUG"];
Types.DOCS.label = languages[options.language]["DOCS"];
Types.STACKOVERFLOW.label = languages[options.language]["STACKOVERFLOW"];
Types.SPONSOR.label = languages[options.language]["SPONSOR"];
if (typeof options.palette === "undefined" || options.palette === null) {
options.palette = null;
} else {
if (typeof options.palette.info !== "undefined" && options.palette.info !== null) {
Types.INFO.bgcolor = (typeof options.palette.info.background === "undefined" || options.palette.info.background === null) ? Types.INFO.bgcolor : chalk.bgHex(options.palette.info.background).hex(options.palette.info.label);
Types.INFO.color = (typeof options.palette.info.text === "undefined" || options.palette.info.text === null) ? Types.INFO.color : chalk.hex(options.palette.info.text);
}
if (typeof options.palette.warning !== "undefined" && options.palette.warning !== null) {
Types.WARNING.bgcolor = (typeof options.palette.warning.background === "undefined" || options.palette.warning.background === null) ? Types.WARNING.bgcolor : chalk.bgHex(options.palette.warning.background).hex(options.palette.warning.label);
Types.WARNING.color = (typeof options.palette.warning.text === "undefined" || options.palette.warning.text === null) ? Types.WARNING.color : chalk.hex(options.palette.warning.text);
}
if (typeof options.palette.error !== "undefined" && options.palette.error !== null) {
Types.ERROR.bgcolor = (typeof options.palette.error.background === "undefined" || options.palette.error.background === null) ? Types.ERROR.bgcolor : chalk.bgHex(options.palette.error.background).hex(options.palette.error.label);
Types.ERROR.color = (typeof options.palette.error.text === "undefined" || options.palette.error.text === null) ? Types.ERROR.color : chalk.hex(options.palette.error.text);
}
if (typeof options.palette.debug !== "undefined" && options.palette.debug !== null) {
Types.DEBUG.bgcolor = (typeof options.palette.debug.background === "undefined" || options.palette.debug.background === null) ? Types.DEBUG.bgcolor : chalk.bgHex(options.palette.debug.background).hex(options.palette.debug.label);
Types.DEBUG.color = (typeof options.palette.debug.text === "undefined" || options.palette.debug.text === null) ? Types.DEBUG.color : chalk.hex(options.palette.debug.text);
}
if (typeof options.palette.docs !== "undefined" && options.palette.docs !== null) {
Types.DOCS.bgcolor = (typeof options.palette.docs.background === "undefined" || options.palette.docs.background === null) ? Types.DOCS.bgcolor : chalk.bgHex(options.palette.docs.background).hex(options.palette.docs.label);
Types.DOCS.color = (typeof options.palette.docs.text === "undefined" || options.palette.docs.text === null) ? Types.DOCS.color : chalk.hex(options.palette.docs.text);
}
if (typeof options.palette.stackoverflow !== "undefined" && options.palette.stackoverflow !== null) {
Types.STACKOVERFLOW.bgcolor = (typeof options.palette.stackoverflow.background === "undefined" || options.palette.stackoverflow.background === null) ? Types.STACKOVERFLOW.bgcolor : chalk.bgHex(options.palette.stackoverflow.background).hex(options.palette.stackoverflow.label);
Types.STACKOVERFLOW.color = (typeof options.palette.stackoverflow.text === "undefined" || options.palette.stackoverflow.text === null) ? Types.STACKOVERFLOW.color : chalk.hex(options.palette.stackoverflow.text);
}
if (typeof options.palette.sponsor !== "undefined" && options.palette.sponsor !== null) {
Types.SPONSOR.bgcolor = (typeof options.palette.sponsor.background === "undefined" || options.palette.sponsor.background === null) ? Types.SPONSOR.bgcolor : chalk.bgHex(options.palette.sponsor.background).hex(options.palette.sponsor.label);
Types.SPONSOR.color = (typeof options.palette.sponsor.text === "undefined" || options.palette.sponsor.text === null) ? Types.SPONSOR.color : chalk.hex(options.palette.sponsor.text);
}
if (typeof options.palette.time !== "undefined" && options.palette.time !== null) {
Types.TIME.bgcolor = (typeof options.palette.time.background === "undefined" || options.palette.time.background === null) ? Types.TIME.bgcolor : chalk.bgHex(options.palette.time.background).hex(options.palette.time.label);
Types.TIME.color = (typeof options.palette.time.text === "undefined" || options.palette.time.text === null) ? Types.TIME.color : chalk.hex(options.palette.time.text);
}
}
if (typeof options.colors === "undefined" || options.colors === null) {
options.colors = true;
}
if (typeof options.debug === "undefined" || options.debug === null) {
options.debug = true;
}
if (typeof options.info === "undefined" || options.info === null) {
options.info = true;
}
if (typeof options.warning === "undefined" || options.warning === null) {
options.warning = true;
}
if (typeof options.error === "undefined" || options.error === null) {
options.error = true;
}
if (typeof options.type === "undefined" || options.type === null) {
options.type = "log";
}
if (typeof options.write === "undefined" || options.write === null) {
options.write = false;
} else if (typeof options.path === "undefined" || options.path === null) {
if (typeof options.debug_log === "undefined" || options.debug_log === null) {
options.debug_log = "./debug.log";
}
if (typeof options.error_log === "undefined" || options.error_log === null) {
options.error_log = "./errors.log";
}
}
if (typeof options.rotate === "undefined" || options.rotate === null) {
options.rotate = {
size: "10M",
encoding: "utf8"
};
}
this.config = options;
if (this.config.write === "enabled" || this.config.write === true) {
const pad = num => (num > 9 ? "" : "0") + num;
rfs.createStream((time, index) => {
if (!time) {
return this.config.path.debug_log;
}
return `${path.parse(this.config.path.debug_log).base.split(".")[0]}.${time.getFullYear()}${pad(time.getMonth() + 1)}${pad(time.getDate())}-${index}.${path.parse(this.config.path.debug_log).base.split(".")[1]}`;
}, this.config.rotate);
rfs.createStream((time, index) => {
if (!time) {
return this.config.path.error_log;
}
return `${path.parse(this.config.path.error_log).base.split(".")[0]}.${time.getFullYear()}${pad(time.getMonth() + 1)}${pad(time.getDate())}-${index}.${path.parse(this.config.path.error_log).base.split(".")[1]}`;
}, this.config.rotate);
}
this.TYPES_LOG = Types;
}
/**
* Date now
* =====================
* Current (now) date and time for prefix of logs. Timezone is supported.
*
* @param {string} format - format of date: json, timestamp or string (optional, deafult: string)
* @return {string} time - current Date.now()
*
*/
currentTime(format = "string") {
let tz_offset = (new Date()).getTimezoneOffset() * 60000;
if (format === "json") {
return (new Date(Date.now() - tz_offset)).toISOString();
}
if (format === "timestamp") {
return (new Date(Date.now() - tz_offset)).getTime();
}
return (new Date(Date.now() - tz_offset)).toISOString().slice(0, -5).replace("T", " ");
}
/**
* Write the output of console.log() to file
* =====================
* Write messages to debug.log and error.log in /logs folder
*
* @param {string} type - example: INFO/WARNING/ERROR/DEBUG or other valid type string (see ./types.js) (mandatory)
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
appendFile(type = "INFO", tag = "", message = "") {
if (this.config.write === "enabled" || this.config.write === true) {
if (this.config.type === "log") {
if (tag !== "") {
tag = `${tag}: `;
}
let log_text = `[${this.currentTime()}] [${type.id}] ${tag}${message}\n`;
fse.appendFile(this.config.path.debug_log, ansi(log_text), (err) => {
if (err) {
logger.log(err);
}
});
if (type.id === "ERROR") {
fse.appendFile(this.config.path.error_log, ansi(log_text), (err) => {
if (err) {
logger.err(err);
}
});
}
} else {
const debug_adapter = new FileSync(this.config.path.debug_log);
const debug_db = lowdb(debug_adapter);
const error_adapter = new FileSync(this.config.path.error_log);
const error_db = lowdb(error_adapter);
let level = 0;
switch (type.id) {
case "ERROR":
level = 1;
break;
case "WARNING":
level = 2;
break;
case "INFO":
level = 3;
break;
case "DEBUG":
level = 4;
break;
default:
level = 5;
break;
}
debug_db.defaults({logs: []}).write();
error_db.defaults({logs: []}).write();
debug_db.get("logs").push({level: level, time: this.currentTime("timestamp"), date: this.currentTime("json"), msg: ansi(message), tag: ansi(tag), v: 1}).write();
if (type.id === "ERROR") {
error_db.get("logs").push({level: level, time: this.currentTime("timestamp"), date: this.currentTime("json"), msg: ansi(message), tag: ansi(tag), v: 1}).write();
}
}
}
}
/**
* Write to stdout
* =====================
* Stdout manager - don't use this directly. Use info() error() debug() warning()
*
* @param {string} type - example: INFO/WARNING/ERROR/DEBUG or other valid type string (see ./types.js) (mandatory)
* @param {string} tag - func unique tag (optional)
* @param {string} message - error, warning or info description (mandatory)
*
*/
stdout(type = "INFO", tag = "", message = "") {
let time = this.TYPES_LOG.TIME;
if (tag !== "") {
tag = ` ${tag}:`;
}
if (this.config.colors === "enabled" || this.config.colors === true) {
logger.log(chalk`${type.bgcolor(type.label)}${time.bgcolor(` ${this.currentTime()} `)}${type.bgcolor(" ")}${type.color(tag)} ${type.color(message)}`);
} else {
logger.log(ansi(chalk`${type.bgcolor(type.label)}${time.bgcolor(` ${this.currentTime()} `)}${type.bgcolor(" ")}${type.color(tag)} ${type.color(message)}`));
}
}
/**
* Write to stderr
* =====================
* Stderr manager - don't use this directly. Use info() error() debug() warning()
*
* @param {string} type - example: INFO/WARNING/ERROR/DEBUG or other valid type string (see ./types.js) (mandatory)
* @param {string} tag - func unique tag (optional)
* @param {string} message - error, warning or info description (mandatory)
*
*/
stderr(type = "ERROR", tag = "", message = "") {
let time = this.TYPES_LOG.TIME;
if (tag !== "") {
tag = ` ${tag}:`;
}
if (this.config.colors === "enabled" || this.config.colors === true) {
logger.error(chalk`${type.bgcolor(type.label)}${time.bgcolor(` ${this.currentTime()} `)}${type.bgcolor(" ")}${type.color(tag)} ${type.color(message)}`);
} else {
logger.error(ansi(chalk`${type.bgcolor(type.label)}${time.bgcolor(` ${this.currentTime()} `)}${type.bgcolor(" ")}${type.color(tag)} ${type.color(message)}`));
}
}
/**
* Logging of the info message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
info(message = "", tag = "") {
if (this.config.info === "enabled" || this.config.info === true) {
this.stdout(this.TYPES_LOG.INFO, tag, `${message}`);
this.appendFile(this.TYPES_LOG.INFO, tag, message);
}
}
/**
* Logging of the warning message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
warning(message = "", tag = "") {
if (this.config.warning === "enabled" || this.config.warning === true) {
this.stdout(this.TYPES_LOG.WARNING, tag, `${message}`);
this.appendFile(this.TYPES_LOG.WARNING, tag, message);
}
}
/**
* Logging of the error message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
error(message = "", tag = "") {
if (this.config.error === "enabled" || this.config.error === true) {
this.stderr(this.TYPES_LOG.ERROR, tag, `${message}`);
this.appendFile(this.TYPES_LOG.ERROR, tag, message);
}
}
/**
* Logging of the debug message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
debug(message = "", tag = "") {
if (this.config.debug === "enabled" || this.config.debug === true) {
this.stdout(this.TYPES_LOG.DEBUG, tag, `${message}`);
this.appendFile(this.TYPES_LOG.DEBUG, tag, message);
}
}
/**
* Logging of the docs message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} url - url of documentation (optional)
* @param {string} tag - func unique tag (optional)
*
*/
docs(message = "", url = "", tag = "") {
let docs = this.TYPES_LOG.DOCS;
this.stdout(this.TYPES_LOG.DOCS, tag, `${message} - ${docs.color.underline.italic(url)}`);
this.appendFile(this.TYPES_LOG.DOCS, tag, `${message} - ${docs.color.underline.italic(url)}`);
}
/**
* Logging of the stackoverflow message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
* @param {string} error_message - error message to stackoverflow (optional)
*
*/
stackoverflow(message = "", tag = "", error_message = null) {
if (typeof error_message === "undefined" || error_message === null) {
error_message = message;
}
let stackoverflow = this.TYPES_LOG.STACKOVERFLOW;
let url = `https://stackoverflow.com/search?q=${encodeURI(error_message)}`;
this.stdout(this.TYPES_LOG.STACKOVERFLOW, tag, `${message} - ${stackoverflow.color.underline.italic(url)}`);
this.appendFile(this.TYPES_LOG.STACKOVERFLOW, tag, `${message} - ${stackoverflow.color.underline.italic(url)}`);
}
/**
* Logging of the sponsor message
* =====================
* This method show message on terminal and/or write message on file/json
*
* @param {string} message - description of issue (mandatory)
* @param {string} tag - func unique tag (optional)
*
*/
sponsor(message = "", tag = "") {
this.stdout(this.TYPES_LOG.SPONSOR, tag, message);
this.appendFile(this.TYPES_LOG.SPONSOR, tag, message);
}
}
module.exports = Log;
module.exports.default = Log;