@villedemontreal/logger
Version:
Logger and logging utilities
564 lines • 28.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.timeout = timeout;
const general_utils_1 = require("@villedemontreal/general-utils");
const chai_1 = require("chai");
const fs = require("fs-extra");
const _ = require("lodash");
const configs_1 = require("./config/configs");
const constants_1 = require("./config/constants");
const lazyLogger_1 = require("./lazyLogger");
const logger_1 = require("./logger");
const TESTING_CID = 'test-cid';
/**
* The "--no-timeouts" arg doesn't work to disable
* the Mocha timeouts (while debugging) if a timeout
* is specified in the code itself. Using this to set the
* timeouts does.
*/
function timeout(mocha, milliSec) {
mocha.timeout(process.argv.includes('--no-timeouts') ? 0 : milliSec);
}
// ==========================================
// Logger tests
// ==========================================
describe('Logger tests', () => {
describe('General tests', () => {
let loggerConfig;
let logger;
const stdoutWriteBackup = process.stdout.write;
let output;
before(async () => {
// ==========================================
// Tweaks the configs for this tests file.
// ==========================================
loggerConfig = new configs_1.LoggerConfigs(() => TESTING_CID);
loggerConfig.setLogHumanReadableinConsole(false);
loggerConfig.setLogSource(true);
loggerConfig.setLogLevel(logger_1.LogLevel.INFO);
// ==========================================
// Creates the logger
// ==========================================
(0, logger_1.initLogger)(loggerConfig, 'default', true);
logger = new logger_1.Logger('test');
});
let expectTwoCalls = false;
let keepSecondCallOnly = false;
let firstCallDone = false;
function restartCustomWriter() {
output = '';
expectTwoCalls = false;
keepSecondCallOnly = false;
firstCallDone = false;
// A stadard "function" is required here, because
// of the use of "arguments".
// tslint:disable-next-line:only-arrow-functions
process.stdout.write = function (...args) {
if (!expectTwoCalls) {
output += args[0];
process.stdout.write = stdoutWriteBackup;
return;
}
if (!firstCallDone) {
if (!keepSecondCallOnly) {
output += args[0];
}
firstCallDone = true;
}
else {
output += args[0];
process.stdout.write = stdoutWriteBackup;
}
};
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output));
}
beforeEach(async () => {
restartCustomWriter();
});
it('string message', async () => {
logger.error('allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.name, 'test');
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('string message and extra text message', async () => {
logger.error('allo', 'salut');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.msg, 'allo - salut');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('custom object message', async () => {
logger.error({
key1: {
key3: 'val3',
key4: 'val4',
},
key2: 'val2',
msg: 'blabla',
});
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.key2, 'val2');
chai_1.assert.strictEqual(jsonObj.msg, 'blabla');
chai_1.assert.deepEqual(jsonObj.key1, {
key3: 'val3',
key4: 'val4',
});
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('custom object message and extra text message', async () => {
logger.error({
key1: {
key3: 'val3',
key4: 'val4',
},
key2: 'val2',
msg: 'blabla',
}, 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.key2, 'val2');
chai_1.assert.strictEqual(jsonObj.msg, 'blabla - my text message');
chai_1.assert.deepEqual(jsonObj.key1, {
key3: 'val3',
key4: 'val4',
});
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('regular Error object and extra text message', async () => {
logger.error(new Error('my error message'), 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'my error message - my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(jsonObj.stack));
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(jsonObj.name));
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('null Error object and extra text message', async () => {
logger.error(null, 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(jsonObj.name));
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('undefined Error object and extra text message', async () => {
logger.error(undefined, 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'my text message');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(jsonObj.name));
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
});
it('array message', async () => {
// ==========================================
// Two calls to "process.stdout.write" will
// be made because an error for the invalid
// array message will be logged!
// ==========================================
expectTwoCalls = true;
keepSecondCallOnly = true;
logger.error([
'toto',
{
key1: 'val1',
key2: 'val2',
},
]);
const jsonObj = JSON.parse(output);
chai_1.assert.deepEqual(jsonObj._arrayMsg, [
'toto',
{
key1: 'val1',
key2: 'val2',
},
]);
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
});
it('array message and extra text message', async () => {
// ==========================================
// Two calls to "process.stdout.write" will
// be made because an error for the invalid
// array message will be logged!
// ==========================================
expectTwoCalls = true;
keepSecondCallOnly = true;
logger.error([
'toto',
{
key1: 'val1',
key2: 'val2',
},
], 'my text message');
const jsonObj = JSON.parse(output);
chai_1.assert.deepEqual(jsonObj._arrayMsg, [
'toto',
{
key1: 'val1',
key2: 'val2',
},
]);
chai_1.assert.strictEqual(jsonObj.msg, 'my text message');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
});
it('log level - debug', async () => {
logger.debug('allo');
chai_1.assert.strictEqual(output, '');
});
it('log level - info', async () => {
logger.info('allo');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.INFO));
});
it('log level - warning', async () => {
logger.warning('allo');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.WARNING));
});
it('log level - error', async () => {
logger.error('allo');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.ERROR));
});
it('log level - custom valid', async () => {
logger.log(logger_1.LogLevel.INFO, 'allo');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.INFO));
});
it('log level - custom invalid', async () => {
// ==========================================
// Two calls to "process.stdout.write" will
// be made because an error for the invalid
// level will be logged!
// ==========================================
expectTwoCalls = true;
keepSecondCallOnly = true;
logger.log('nope', 'allo');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.ERROR));
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
});
it('newline after each log - string', async () => {
expectTwoCalls = true;
logger.error('111');
logger.error('222');
const pos = output.indexOf('\n');
chai_1.assert.isTrue(pos > -1);
chai_1.assert.strictEqual(output.charAt(pos + 1), '{');
});
it('newline after each log - complexe objects', async () => {
expectTwoCalls = true;
logger.error({
key1: 'val1',
key2: 'val2',
});
logger.error({
key1: 'val1',
key2: 'val2',
});
const pos = output.indexOf('\n');
chai_1.assert.isTrue(pos > -1);
chai_1.assert.strictEqual(output.charAt(pos + 1), '{');
});
it('date message', async () => {
const someDate = new Date();
logger.error(someDate);
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.msg, someDate.toString());
chai_1.assert.isTrue(_.isDate(someDate));
});
// ==========================================
// Error controller log messages
// ==========================================
describe('Error controller log messages', () => {
it('app and version properties', async () => {
const packagePath = `${constants_1.constants.libRoot}/../../package.json`;
const packageJson = require(packagePath);
const appName = packageJson.name;
const appVersion = packageJson.version;
logger.error('allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.APP_NAME], appName);
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.APP_VERSION], appVersion);
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.CORRELATION_ID], TESTING_CID);
});
});
// ==========================================
// LazyLogger tests
// ==========================================
describe('LazyLogger tests', () => {
let lazyLogger;
beforeEach(async () => {
lazyLogger = new lazyLogger_1.LazyLogger('titi', (name) => {
const logger2 = new logger_1.Logger('titi');
return logger2;
});
});
it('debug', async () => {
lazyLogger.debug('allo');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output));
});
it('info', async () => {
lazyLogger.info('allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.INFO));
chai_1.assert.strictEqual(jsonObj.name, 'titi');
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.CORRELATION_ID], TESTING_CID);
});
it('warning', async () => {
lazyLogger.warning('allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.WARNING));
chai_1.assert.strictEqual(jsonObj.name, 'titi');
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.CORRELATION_ID], TESTING_CID);
});
it('error', async () => {
lazyLogger.error('allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.ERROR));
chai_1.assert.strictEqual(jsonObj.name, 'titi');
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.CORRELATION_ID], TESTING_CID);
});
it('log', async () => {
lazyLogger.log(logger_1.LogLevel.INFO, 'allo');
chai_1.assert.isTrue(!general_utils_1.utils.isBlank(output));
const jsonObj = JSON.parse(output);
chai_1.assert.isTrue(_.isObject(jsonObj));
chai_1.assert.strictEqual(jsonObj.level, (0, logger_1.convertLogLevelToPinoNumberLevel)(logger_1.LogLevel.INFO));
chai_1.assert.strictEqual(jsonObj.name, 'titi');
chai_1.assert.strictEqual(jsonObj.msg, 'allo');
chai_1.assert.isNotNull(jsonObj.src);
chai_1.assert.isNotNull(jsonObj.src.file);
chai_1.assert.isNotNull(jsonObj.src.line);
chai_1.assert.strictEqual(jsonObj.logType, constants_1.constants.logging.logType.MONTREAL);
chai_1.assert.strictEqual(jsonObj.logTypeVersion, '2');
chai_1.assert.strictEqual(jsonObj[constants_1.constants.logging.properties.CORRELATION_ID], TESTING_CID);
});
it('logger creator is required', async () => {
try {
const lazyLogger2 = new lazyLogger_1.LazyLogger('titi', null);
chai_1.assert.fail();
chai_1.assert.isNotOk(lazyLogger2);
}
catch (err) {
/* ok */
}
try {
const lazyLogger3 = new lazyLogger_1.LazyLogger('titi', undefined);
chai_1.assert.fail();
chai_1.assert.isNotOk(lazyLogger3);
}
catch (err) {
/* ok */
}
});
});
// ==========================================
// Global log level
// ==========================================
describe('Global log level', () => {
let childLogger;
before(() => {
childLogger = (0, logger_1.createLogger)('testing child logger');
});
afterEach(() => {
// Reset the default logging
(0, logger_1.setGlobalLogLevel)(loggerConfig.getLogLevel());
});
it('log debug message when global log level set to DEBUG', () => {
(0, logger_1.setGlobalLogLevel)(logger_1.LogLevel.DEBUG);
logger.debug('this is the debug message');
let jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the debug message');
restartCustomWriter();
childLogger.debug('this is the child logger debug message');
jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the child logger debug message');
restartCustomWriter();
logger.info('this is the info message');
jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the info message');
restartCustomWriter();
childLogger.info('this is the child logger info message');
jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the child logger info message');
});
it('filter debug message when global log level set to WARNING', () => {
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output));
(0, logger_1.setGlobalLogLevel)(logger_1.LogLevel.WARNING);
logger.debug('this is my filtered the debug message');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output), 'Did not filter debug message as expected.');
childLogger.debug('this is my filtered the debug message');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output), 'Did not filter debug message as expected.');
logger.info('this is my filtered the info message');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output), 'Did not filter info message as expected.');
childLogger.info('this is my filtered the info message');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output), 'Did not filter info message as expected.');
logger.warning('this is the warning message');
let jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the warning message');
restartCustomWriter();
childLogger.warning('this is the child logger warning message');
jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the child logger warning message');
});
it('apply current log level to new created logger - INFO', () => {
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output));
(0, logger_1.setGlobalLogLevel)(logger_1.LogLevel.WARNING);
const newLogger = (0, logger_1.createLogger)('testing logger');
newLogger.info('this is the info message for newly created logger');
chai_1.assert.isTrue(general_utils_1.utils.isBlank(output), 'Did not filter info message as expected.');
newLogger.warning('this is the warning message');
const jsonObj = JSON.parse(output);
chai_1.assert.strictEqual(jsonObj.msg, 'this is the warning message');
});
});
});
describe('Log to file', function () {
timeout(this, 30000);
let loggerConfig;
let logger;
let testingLogDir;
let testingLogFile;
before(async () => {
testingLogDir = `${constants_1.constants.libRoot}/output/testingLogs`;
if (general_utils_1.utils.isDir(testingLogDir)) {
await general_utils_1.utils.clearDir(testingLogDir);
}
else {
fs.mkdirsSync(testingLogDir);
}
testingLogFile = `${testingLogDir}/application.log`;
});
after(async () => {
await general_utils_1.utils.deleteDir(testingLogDir);
});
beforeEach(async () => {
if (fs.existsSync(testingLogFile)) {
fs.unlinkSync(testingLogFile);
}
chai_1.assert.isFalse(fs.existsSync(testingLogFile));
});
/**
* Logging with Pino is asynchronous.
* We need to make sure the log is written to file by waiting.
* @see https://github.com/pinojs/pino/blob/master/docs/asynchronous.md
*/
async function logAndWait(msg) {
logger.error(msg);
chai_1.assert.isFunction(logger[`pino`][`flush`], `The "flush method should exist on a Pino logger"`);
logger[`pino`][`flush`]();
const max = 5000;
let elapsed = 0;
const sleep = 200;
while (!fs.existsSync(testingLogFile) && elapsed < max) {
await general_utils_1.utils.sleep(sleep);
elapsed += sleep;
}
}
it('No log file - default', async () => {
loggerConfig = new configs_1.LoggerConfigs(() => TESTING_CID);
loggerConfig.setLogHumanReadableinConsole(false);
loggerConfig.setLogSource(true);
loggerConfig.setLogLevel(logger_1.LogLevel.DEBUG);
loggerConfig.setLogDirectory(testingLogDir);
(0, logger_1.initLogger)(loggerConfig, 'default', true);
logger = new logger_1.Logger('test');
await logAndWait('allo');
chai_1.assert.isFalse(fs.existsSync(testingLogFile));
});
it('No log file - explicit', async () => {
loggerConfig = new configs_1.LoggerConfigs(() => TESTING_CID);
loggerConfig.setLogHumanReadableinConsole(false);
loggerConfig.setLogSource(true);
loggerConfig.setLogLevel(logger_1.LogLevel.DEBUG);
loggerConfig.setLogDirectory(testingLogDir);
loggerConfig.setSlowerLogToFileToo(false);
(0, logger_1.initLogger)(loggerConfig, 'default', true);
logger = new logger_1.Logger('test');
await logAndWait('allo');
chai_1.assert.isFalse(fs.existsSync(testingLogFile));
});
it('Log file', async () => {
loggerConfig = new configs_1.LoggerConfigs(() => TESTING_CID);
loggerConfig.setLogHumanReadableinConsole(false);
loggerConfig.setLogSource(true);
loggerConfig.setLogLevel(logger_1.LogLevel.DEBUG);
loggerConfig.setLogDirectory(testingLogDir);
loggerConfig.setSlowerLogToFileToo(true);
(0, logger_1.initLogger)(loggerConfig, 'default', true);
logger = new logger_1.Logger('test');
await logAndWait('allo');
chai_1.assert.isTrue(fs.existsSync(testingLogFile));
const content = fs.readFileSync(testingLogFile, 'utf-8');
chai_1.assert.isOk(content);
chai_1.assert.isTrue(content.indexOf(`"msg":"allo"`) > -1);
});
});
});
//# sourceMappingURL=logger.test.js.map