refactor(@embark/core): make logger async when writing to log file

This commit is contained in:
Pascal Precht 2020-01-17 13:10:40 +01:00 committed by Michael Bradley
parent 8c9e6b11c8
commit 60cbb9fa3e
3 changed files with 110 additions and 32 deletions

View File

@ -45,6 +45,7 @@
"extends": "../../../.eslintrc.json"
},
"dependencies": {
"async": "2.6.1",
"@babel/runtime-corejs3": "7.8.3",
"colors": "1.4.0",
"core-js": "3.6.4",

View File

@ -1,3 +1,4 @@
const async = require('async');
require('colors');
const fs = require('fs');
const date = require('date-and-time');
@ -19,10 +20,11 @@ export class Logger {
this.events = options.events || {emit: function(){}};
this.logLevel = options.logLevel || 'info';
this._logFunction = options.logFunction || console.log;
this.fs = options.fs || fs;
this.logFunction = function(...args) {
const color = args[args.length - 1];
args.splice(args.length - 1, 1);
this._logFunction(...args.filter(arg => arg !== undefined && arg !== null).map(arg => {
this._logFunction(...args.filter(arg => arg ?? false).map(arg => {
if (color) {
return typeof arg === 'object' ? util.inspect(arg, 2)[color] : arg[color];
}
@ -30,11 +32,28 @@ export class Logger {
}));
};
this.logFile = options.logFile;
this.writeToFile = async.cargo((tasks, callback) => {
if (!this.logFile) {
return callback();
}
let logs = '';
let origin = "[" + ((new Error().stack).split("at ")[3]).trim() + "]";
tasks.forEach(task => {
logs += `[${date.format(new Date(), DATE_FORMAT)}] ${task.prefix} ${task.args}\n`;
});
this.fs.appendFile(this.logFile, `\n${origin} ${logs}`, err => {
if (err) {
this.logFunction(`There was an error writing to the log file: ${err}`, 'red');
return callback(err);
}
callback();
});
});
}
registerAPICall(plugins) {
let plugin = plugins.createPlugin('dashboard', {});
let plugin = plugins.createPlugin('logger', {});
plugin.registerAPICall(
'ws',
'/embark-api/logs',
@ -47,68 +66,99 @@ export class Logger {
);
}
writeToFile(...args) {
if (!this.logFile) {
return;
}
let origin = "[" + ((new Error().stack).split("at ")[3]).trim() + "]";
const formattedDate = [`[${date.format(new Date(), DATE_FORMAT)}]`]; // adds a timestamp to the logs in the logFile
fs.appendFileSync(this.logFile, "\n" + formattedDate.concat(origin, args).join(' '));
}
error(...args) {
if (!args.length || !(this.shouldLog('error'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "error", args);
this.logFunction(...Array.from(args), 'red');
this.writeToFile("[error]: ", args);
this.logFunction(...args, 'red');
this.writeToFile.push({ prefix: "[error]: ", args }, callback);
}
warn(...args) {
if (!args.length || !(this.shouldLog('warn'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "warn", args);
this.logFunction(...Array.from(args), 'yellow');
this.writeToFile("[warning]: ", args);
this.logFunction(...args, 'yellow');
this.writeToFile.push({ prefix: "[warning]: ", args }, callback);
}
info(...args) {
if (!args.length || !(this.shouldLog('info'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "info", args);
this.logFunction(...Array.from(args), 'green');
this.writeToFile("[info]: ", args);
this.logFunction(...args, 'green');
this.writeToFile.push({ prefix: "[info]: ", args }, callback);
}
consoleOnly(...args) {
if (!args.length || !(this.shouldLog('info'))) {
return;
}
this.logFunction(...Array.from(args), 'green');
this.writeToFile("[consoleOnly]: ", args);
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.logFunction(...args, 'green');
this.writeToFile.push({prefix: "[consoleOnly]: ", args }, callback);
}
debug(...args) {
if (!args.length || !(this.shouldLog('debug'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "debug", args);
this.logFunction(args, null);
this.writeToFile("[debug]: ", args);
this.logFunction(...args, null);
this.writeToFile.push({ prefix: "[debug]: ", args }, callback);
}
trace(...args) {
if (!args.length || !(this.shouldLog('trace'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "trace", args);
this.logFunction(args, null);
this.writeToFile("[trace]: ", args);
this.logFunction(...args, null);
this.writeToFile.push({ prefix: "[trace]: ", args }, callback);
}
dir(...args) {
@ -116,9 +166,16 @@ export class Logger {
if (!txt || !(this.shouldLog('info'))) {
return;
}
let callback = () => {};
if (typeof args[args.length - 1] === 'function') {
callback = args[args.length - 1];
args.splice(args.length - 1, 1);
}
this.events.emit("log", "dir", txt);
this.logFunction(txt, null);
this.writeToFile("[dir]: ", args);
this.writeToFile({ prefix: "[dir]: ", args }, callback);
}
shouldLog(level) {
@ -126,3 +183,4 @@ export class Logger {
return (logLevels.indexOf(level) <= logLevels.indexOf(this.logLevel));
}
}

View File

@ -11,14 +11,27 @@ describe('core/logger', () => {
let logger, testLogFn, logFile;
const fsMock = {
logs: {},
appendFile: (file, content, callback) => {
if (!fsMock.logs[file]) {
fsMock.logs[file] = [];
}
fsMock.logs[file].push(content);
callback();
}
};
beforeEach(() => {
logFile = tmp.fileSync();
fsMock[logFile.name] = [];
testLogFn = sinon.fake();
logger = new Logger({
events: embark.events,
logFunction: testLogFn,
logFile: logFile.name
logFile: logFile.name,
fs: fsMock
});
});
@ -41,10 +54,16 @@ describe('core/logger', () => {
assert(testLogFn.calledWith('\u001b[31mHello world\u001b[39m'));
});
test('it should write logs to log file', () => {
logger.info('Some test log');
const logs = fs.readFileSync(logFile.name, 'utf8')
assert.ok(logs.indexOf('[info]: Some test log') > -1);
test('it should write logs to log file', (done) => {
const stats = fs.statSync(logFile.name);
logger.info('Some test log', () => {
fsMock.logs[logFile.name].forEach(entry => {
if (entry.indexOf('[info]: Some test log') > -1) {
assert.ok(true);
}
});
done();
});
});
test('it should not log if log method level is higher than configured log level', () => {