Skip to content

Instantly share code, notes, and snippets.

@nebarf
Last active June 5, 2021 15:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nebarf/ccfd71cadd5545284b1f344109602e8f to your computer and use it in GitHub Desktop.
Save nebarf/ccfd71cadd5545284b1f344109602e8f to your computer and use it in GitHub Desktop.
const { createWriteStream } = require('fs');
const { join } = require('path');
const { EOL } = require('os');
const logLevels = {
off: -1,
debug: 0,
info: 1,
warn: 2,
error: 3,
};
class ConsoleLogStrategy {
_logLevelToActionMap = {
[logLevels.debug]: 'debug',
[logLevels.info]: 'info',
[logLevels.warn]: 'warn',
[logLevels.error]: 'error'
};
write(logLevel, ...args) {
const action = this._logLevelToActionMap[logLevel];
console[action](...args);
}
}
class FileLogStrategy {
_logLevelToFileMap = {
[logLevels.debug]: 'debug.log',
[logLevels.info]: 'info.log',
[logLevels.warn]: 'warn.log',
[logLevels.error]: 'error.log'
};
_streamsCache = {};
constructor(logDir) {
this._logDir = logDir;
}
_getWriteStream(logLevel) {
const streamCacheKey = `stream_${logLevel}`;
if (!this._streamsCache[streamCacheKey]) {
const fileName = this._logLevelToFileMap[logLevel];
const logPath = this._logDir ? join(this._logDir, fileName) : fileName;
const writeStream = createWriteStream(logPath, {
// Append mode
flags: 'a',
});
this._streamsCache[streamCacheKey] = writeStream;
}
return this._streamsCache[streamCacheKey];
}
_stringifyArgs(...args) {
return args.map((arg) => {
let stringifiedArg = arg;
if (typeof arg === 'object') {
return JSON.stringify(arg);
}
return stringifiedArg;
});
}
write(logLevel, ...args) {
const stringifiedArgs = this._stringifyArgs(...args);
const writeStream = this._getWriteStream(logLevel);
const writeRes = writeStream.write(stringifiedArgs.join(' ') + EOL);
// Internal buffer is full, retry writing when it is drained.
if (!writeRes) {
writeStream.once('drain', this.write(logLevel, ...args));
}
}
}
class Logger {
constructor(strategy, globalLevel) {
if (this._globalLevel < logLevels.off || this._globalLevel > logLevels.error) {
throw new Error('The provided log level is wrong.');
}
this._strategy = strategy;
this._globalLevel = globalLevel || logLevels.debug;
}
_writeLog(logLevel, ...args) {
if (logLevel < this._globalLevel || this._globalLevel === logLevels.off) {
return;
}
this._strategy.write(logLevel, ...args);
}
debug(...args) {
this._writeLog(logLevels.debug, ...args);
}
info(...args) {
this._writeLog(logLevels.info, ...args);
}
warn(...args) {
this._writeLog(logLevels.warn, ...args);
}
error(...args) {
this._writeLog(logLevels.error, ...args);
}
}
const consoleLogger = new Logger(new ConsoleLogStrategy());
const fileLogger = new Logger(new FileLogStrategy(), logLevels.info);
consoleLogger.debug('Debug log', { level: 'debug' });
consoleLogger.info('Info log', { level: 'info' });
consoleLogger.warn('Warn log', { level: 'warn' });
consoleLogger.error('Error log', { level: 'error' });
fileLogger.debug('Debug log', { level: 'debug' });
fileLogger.info('Info log', { level: 'info' });
fileLogger.warn('Warn log', { level: 'warn' });
fileLogger.error('Error log', { level: 'error' });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment