From 908c300f305803a372f96d0396eb7d7ca1bd590b Mon Sep 17 00:00:00 2001 From: Konstantin Lutovich Date: Mon, 27 Aug 2018 19:39:45 +0200 Subject: [PATCH] Added `isLevelEnabled(string)` & `isXXXEnabled()` (#1352) Make logger expose functions to check if a particular level is enabled. This could be useful if either message or parameter creation is an expensive operation, which should be avoided when log level is disabled. Logger will contain `isLevelEnabled(level: string)` function and multiple `isXXXEnabled()` where "XXX" is the capitalized name of the configured log level. E.g. `isDebugEnabled()`, `isInfoEnabled()`, etc. --- lib/winston/create-logger.js | 8 +- lib/winston/logger.js | 33 +++++++ test/logger.test.js | 179 +++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+), 1 deletion(-) diff --git a/lib/winston/create-logger.js b/lib/winston/create-logger.js index d8cc4b184..c17780640 100644 --- a/lib/winston/create-logger.js +++ b/lib/winston/create-logger.js @@ -44,7 +44,7 @@ class DerivedLogger extends Logger { } // Define prototype methods for each log level - // e.g. logger.log('info', msg) <––> logger.info(msg) + // e.g. logger.log('info', msg) <––> logger.info(msg) & logger.isInfoEnabled() this[level] = (...args) => { // Optimize the hot-path which is the single object. if (args.length === 1) { @@ -61,10 +61,16 @@ class DerivedLogger extends Logger { // 2. v1/v2 API: log(level, msg, ... [string interpolate], [{metadata}], [callback]) return this.log(level, ...args); }; + + this[isLevelEnabledFunctionName(level)] = () => this.isLevelEnabled(level); }); } } +function isLevelEnabledFunctionName(level) { + return 'is' + level.charAt(0).toUpperCase() + level.slice(1) + 'Enabled'; +} + /** * Create a new instance of a winston Logger. Creates a new * prototype for each instance. diff --git a/lib/winston/logger.js b/lib/winston/logger.js index 2f606ee84..f206f1a5a 100644 --- a/lib/winston/logger.js +++ b/lib/winston/logger.js @@ -95,6 +95,31 @@ class Logger extends stream.Transform { } } + isLevelEnabled(level) { + const givenLevelValue = getLevelValue(this.levels, level); + if (givenLevelValue === null) { + return false; + } + + const configuredLevelValue = getLevelValue(this.levels, this.level); + if (configuredLevelValue === null) { + return false; + } + + if (!this.transports || this.transports.length === 0) { + return configuredLevelValue >= givenLevelValue; + } + + const index = this.transports.findIndex(transport => { + let transportLevelValue = getLevelValue(this.levels, transport.level); + if (transportLevelValue === null) { + transportLevelValue = configuredLevelValue; + } + return transportLevelValue >= givenLevelValue; + }); + return index !== -1; + } + /* eslint-disable valid-jsdoc */ /** * Ensure backwards compatibility with a `log` method @@ -511,6 +536,14 @@ class Logger extends stream.Transform { } } +function getLevelValue(levels, level) { + const value = levels[level]; + if (!value && value !== 0) { + return null; + } + return value; +} + /** * Represents the current readableState pipe targets for this Logger instance. * @type {Array|Object} diff --git a/test/logger.test.js b/test/logger.test.js index 306f6f317..53efd22f4 100755 --- a/test/logger.test.js +++ b/test/logger.test.js @@ -360,6 +360,185 @@ describe('Logger (levels)', function () { }); }); +describe('Logger (level enabled/disabled)', function () { + it('default levels', function () { + var logger = winston.createLogger({ + level: 'verbose', + levels: winston.config.npm.levels, + transports: [new winston.transports.Console()] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).false(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).false(); + assume(logger.isSillyEnabled()).false(); + }); + + it('default levels, transport override', function () { + var transport = new winston.transports.Console(); + transport.level = 'debug'; + + var logger = winston.createLogger({ + level: 'info', + levels: winston.config.npm.levels, + transports: [transport] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).true(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).true(); + assume(logger.isSillyEnabled()).false(); + }); + + it('default levels, no transports', function () { + var logger = winston.createLogger({ + level: 'verbose', + levels: winston.config.npm.levels, + transports: [] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isErrorEnabled).is.a('function'); + assume(logger.isWarnEnabled).is.a('function'); + assume(logger.isInfoEnabled).is.a('function'); + assume(logger.isVerboseEnabled).is.a('function'); + assume(logger.isDebugEnabled).is.a('function'); + assume(logger.isSillyEnabled).is.a('function'); + + assume(logger.isLevelEnabled('error')).true(); + assume(logger.isLevelEnabled('warn')).true(); + assume(logger.isLevelEnabled('info')).true(); + assume(logger.isLevelEnabled('verbose')).true(); + assume(logger.isLevelEnabled('debug')).false(); + assume(logger.isLevelEnabled('silly')).false(); + + assume(logger.isErrorEnabled()).true(); + assume(logger.isWarnEnabled()).true(); + assume(logger.isInfoEnabled()).true(); + assume(logger.isVerboseEnabled()).true(); + assume(logger.isDebugEnabled()).false(); + assume(logger.isSillyEnabled()).false(); + }); + + it('custom levels', function () { + var logger = winston.createLogger({ + level: 'test', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [new winston.transports.Console()] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).false(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).false(); + }); + + it('custom levels, no transports', function () { + var logger = winston.createLogger({ + level: 'test', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).false(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).false(); + }); + + it('custom levels, transport override', function () { + var transport = new winston.transports.Console(); + transport.level = 'ok'; + + var logger = winston.createLogger({ + level: 'bad', + levels: { + bad: 0, + test: 1, + ok: 2 + }, + transports: [transport] + }); + + assume(logger.isLevelEnabled).is.a('function'); + + assume(logger.isBadEnabled).is.a('function'); + assume(logger.isTestEnabled).is.a('function'); + assume(logger.isOkEnabled).is.a('function'); + + assume(logger.isLevelEnabled('bad')).true(); + assume(logger.isLevelEnabled('test')).true(); + assume(logger.isLevelEnabled('ok')).true(); + + assume(logger.isBadEnabled()).true(); + assume(logger.isTestEnabled()).true(); + assume(logger.isOkEnabled()).true(); + }); +}); + describe('Logger (stream semantics)', function () { it(`'finish' event awaits transports to emit 'finish'`, function (done) { const transports = [