@elbanby/loggy
Version:
Simple and fast JSON logger
158 lines (139 loc) • 4.85 kB
JavaScript
/*
* Logger is an attempt to standardize logging in your JS applications.
* It is in the very early stages of development, and not ready for large scale productions yet.
* The point of logger is to make sure that whenever an error occurs, you would be able to easily identify
* where the error is propagated from. Also, there will be different log levels.
*
* Since Loggy returns a singleton, there will be no way to configure it during require time. Hence I had
* to come up with config(cfg) function. which takes an object with the customized configs and overwrite the default ones
* I consider this approach a LAZY one, since I should be doing some more validation to the options passed. I added it
* to Todos
*
*
* configs = {
* outStream: process.stdout,
* errorStream: process.stdin,
* isJson: false,
* pretty: true
* }
*
* TODO: I don't like the format function as of now since it handles:
* 1- Determining when to use colors
* 2- Determines if to display output as pure json or a string with json
*
* */
const LOG_LEVELS = {
log: {
type: "log",
color: '\x1b[32m'
},
warn: {
type: "warn",
color: '\x1b[33m'
},
error: {
type: "error",
color: '\x1b[31m'
},
info: {
type: "info",
color: '\x1b[37m'
}
};
// This is the default configuration
const default_config = {
outStream: process.stdout,
errorStream: process.stderr,
isJson: false,
pretty: true
};
class Loggy {
constructor() {
this.outStream = default_config.outStream;
this.errorStream = default_config.errorStream;
this.isJson = default_config.isJson;
this.pretty = default_config.pretty;
}
// TODO: improve config validation and customization mechanism
config(cfgs) {
let cfgsKeys = Object.keys(cfgs);
for (let i = 0; i < cfgsKeys.length; i++) {
let key = cfgsKeys[i];
if (default_config[key] === undefined)
throw new Error(`This configuration "${key}" is invalid for /Loggy configuration`);
this[key] = cfgs[key];
}
}
setOutStream(cb) {
this.outStream = cb;
}
setErrorStream(cb) {
this.errorStream = cb;
}
log(message, module) {
const l = {message, module};
this.write(LOG_LEVELS.log, l);
return l;
}
warn(message, module) {
const l = {message, module};
this.write(LOG_LEVELS.warn, l);
return l;
}
error(message, stacktrace, module) {
const l = {message, stacktrace, module};
this.writeError(LOG_LEVELS.error, l);
return l;
}
info(message, module) {
const l = {message, module};
this.write(LOG_LEVELS.info, l);
return l;
}
write(logLevel, logData) {
if (this.outStream === process.stdout && logLevel) {
let stdout = console[logLevel.type];
stdout(this.format(logLevel, logData));
// Reset terminal color
console.log("\x1b[0m");
return;
}
this.outStream(this.format(logLevel, logData));
}
writeError(logLevel, logData) {
if ((this.errorStream === process.stdout || this.errorStream === process.stderr) && logLevel) {
let stdout = console[logLevel.type];
stdout(this.format(logLevel, logData));
// Reset terminal color
console.log("\x1b[0m");
return;
}
this.errorStream(this.format(logLevel, logData));
}
format(logLevel, data) {
let formattedStr = "";
const date = new Date().toUTCString();
if (this.isJson) {
formattedStr = this.pretty && this.outStream === process.stdout && this.errorStream === process.stderr ?
`${logLevel.color}` + JSON.stringify({logLevel: logLevel.type, date: date, ...data}) :
JSON.stringify({logLevel: logLevel.type, date: date, ...data});
return formattedStr;
}
let dataStr = "\n{";
for (let prop in data) {
dataStr += `\n\t${prop}: ${data[prop]}`;
}
dataStr += "\n}\n";
/*
* If you are using stdout adn stderr, we can simply add colors. However,
* if you are using a custom callback function it will be hard to produce colored output
*
* TODO: Separate stdout and stderr formatting so we can allow granular customization
*/
formattedStr = this.pretty && this.outStream === process.stdout && this.errorStream === process.stderr ?
`${logLevel.color} [${logLevel.type}] - [${date}] ${dataStr}\n` :
`[${logLevel.type}] - [${date}] ${dataStr}\n`;
return formattedStr
}
}
module.exports = new Loggy();