diff --git a/lib/winston/create-logger.js b/lib/winston/create-logger.js index e868aeaad..1259ba80e 100644 --- a/lib/winston/create-logger.js +++ b/lib/winston/create-logger.js @@ -7,14 +7,8 @@ 'use strict'; -const { LEVEL } = require('triple-beam'); const config = require('./config'); const Logger = require('./logger'); -const debug = require('@dabh/diagnostics')('winston:create-logger'); - -function isLevelEnabledFunctionName(level) { - return 'is' + level.charAt(0).toUpperCase() + level.slice(1) + 'Enabled'; -} /** * Create a new instance of a winston Logger. Creates a new @@ -47,58 +41,7 @@ module.exports = function (opts = {}) { const logger = new DerivedLogger(opts); - // - // Create the log level methods for the derived logger. - // - Object.keys(opts.levels).forEach(function (level) { - debug('Define prototype method for "%s"', level); - if (level === 'log') { - // eslint-disable-next-line no-console - console.warn('Level "log" not defined: conflicts with the method "log". Use a different level name.'); - return; - } - - // - // Define prototype methods for each log level e.g.: - // logger.log('info', msg) implies these methods are defined: - // - logger.info(msg) - // - logger.isInfoEnabled() - // - // Remark: to support logger.child this **MUST** be a function - // so it'll always be called on the instance instead of a fixed - // place in the prototype chain. - // - DerivedLogger.prototype[level] = function (...args) { - // Prefer any instance scope, but default to "root" logger - const self = this || logger; - - // Optimize the hot-path which is the single object. - if (args.length === 1) { - const [msg] = args; - const info = msg && msg.message && msg || { message: msg }; - info.level = info[LEVEL] = level; - self._addDefaultMeta(info); - self.write(info); - return (this || logger); - } - - // When provided nothing assume the empty string - if (args.length === 0) { - self.log(level, ''); - return self; - } - - // Otherwise build argument list which could potentially conform to - // either: - // . v3 API: log(obj) - // 2. v1/v2 API: log(level, msg, ... [string interpolate], [{metadata}], [callback]) - return self.log(level, ...args); - }; - - DerivedLogger.prototype[isLevelEnabledFunctionName(level)] = function () { - return (this || logger).isLevelEnabled(level); - }; - }); + logger.createLogLevelMethods(logger, opts.levels); return logger; }; diff --git a/lib/winston/logger.js b/lib/winston/logger.js index 5f8758c89..158441bd0 100644 --- a/lib/winston/logger.js +++ b/lib/winston/logger.js @@ -17,6 +17,7 @@ const LegacyTransportStream = require('winston-transport/legacy'); const Profiler = require('./profiler'); const { warn } = require('./common'); const config = require('./config'); +const debug = require('@dabh/diagnostics')('winston:create-logger'); /** * Captures the number of format (i.e. %s strings) in a given string. @@ -149,6 +150,57 @@ class Logger extends Transform { } } + // + // Create the log level methods for the derived logger. + // + createLogLevelMethods(logger, levels) { + Object.keys(levels).forEach(function (level) { + debug('Define prototype method for "%s"', level); + if (level === 'log') { + // eslint-disable-next-line no-console + console.warn('Level "log" not defined: conflicts with the method "log". Use a different level name.'); + return; + } + // + // Define prototype methods for each log level e.g.: + // logger.log('info', msg) implies these methods are defined: + // - logger.info(msg) + // - logger.isInfoEnabled() + // + // Remark: to support logger.child this **MUST** be a function + // so it'll always be called on the instance instead of a fixed + // place in the prototype chain. + // + Logger.prototype[level] = function (...args) { + // Prefer any instance scope, but default to "root" logger + const self = this || logger; + // Optimize the hot-path which is the single object. + if (args.length === 1) { + const [msg] = args; + const info = msg && msg.message && msg || { message: msg }; + info.level = info[LEVEL] = level; + self._addDefaultMeta(info); + self.write(info); + return (this || logger); + } + // When provided nothing assume the empty string + if (args.length === 0) { + self.log(level, ''); + return self; + } + // Otherwise build argument list which could potentially conform to + // either: + // . v3 API: log(obj) + // 2. v1/v2 API: log(level, msg, ... [string interpolate], [{metadata}], [callback]) + return self.log(level, ...args); + }; + const isLevelEnabledFunctionName = 'is' + level.charAt(0).toUpperCase() + level.slice(1) + 'Enabled'; + Logger.prototype[isLevelEnabledFunctionName] = function () { + return (this || logger).isLevelEnabled(level); + }; + }); + } + isLevelEnabled(level) { const givenLevelValue = getLevelValue(this.levels, level); if (givenLevelValue === null) { diff --git a/test/unit/winston/logger.test.js b/test/unit/winston/logger.test.js index 83504f594..1e2db6cf9 100755 --- a/test/unit/winston/logger.test.js +++ b/test/unit/winston/logger.test.js @@ -201,6 +201,32 @@ describe('Logger Instance', function () { }); describe('Log Levels', function () { + it('create log level methods using Logger class', function (done) { + stdMocks.use(); + const logger = new winston.Logger({ + level: 'info', + transports: [new winston.transports.Console()], + format: winston.format.combine(winston.format.splat(), winston.format.colorize({ all: true }), winston.format.simple()) + }) + logger.error('test'); + logger.warn('test'); + logger.info('test'); + logger.verbose('test'); + logger.debug('test'); + logger.silly('test'); + + stdMocks.restore(); + + assume(logger.error).is.a('function'); + assume(logger.warn).is.a('function'); + assume(logger.info).is.a('function'); + assume(logger.verbose).is.a('function'); + assume(logger.debug).is.a('function'); + assume(logger.silly).is.a('function'); + + done(); + }); + it('report unknown levels', function (done) { stdMocks.use(); let logger = helpers.createLogger(function (info) {