Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 78 additions & 64 deletions lib/winston/create-logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,79 +12,93 @@ const config = require('./config');
const Logger = require('./logger');
const debug = require('diagnostics')('winston:create-logger');

function isLevelEnabledFunctionName(level) {
return 'is' + level.charAt(0).toUpperCase() + level.slice(1) + 'Enabled';
}

/**
* DerivedLogger to attach the logs level methods.
* @type {DerivedLogger}
* @extends {Logger}
* Create a new instance of a winston Logger. Creates a new
* prototype for each instance.
* @param {!Object} opts - Options for the created logger.
* @returns {Logger} - A newly created logger instance.
*/
class DerivedLogger extends Logger {
module.exports = function (opts = {}) {
//
// Default levels: npm
//
opts.levels = opts.levels || config.npm.levels;

/**
* Create a new class derived logger for which the levels can be attached to
* the prototype of. This is a V8 optimization that is well know to increase
* performance of prototype functions.
* @param {!Object} options - Options for the created logger.
* DerivedLogger to attach the logs level methods.
* @type {DerivedLogger}
* @extends {Logger}
*/
constructor(options) {
super(options);
this._setupLevels();
class DerivedLogger extends Logger {
/**
* Create a new class derived logger for which the levels can be attached to
* the prototype of. This is a V8 optimization that is well know to increase
* performance of prototype functions.
* @param {!Object} options - Options for the created logger.
*/
constructor(options) {
super(options);
}
}

/**
* Create the log level methods for the derived logger.
* @returns {undefined}
* @private
*/
_setupLevels() {
Object.keys(this.levels).forEach(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;
}
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) <––> logger.info(msg) & logger.isInfoEnabled()
// this is not an arrow function so it'll always be called on the instance instead of a fixed place in the prototype chain.
this[level] = function (...args) {
// 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;
this._addDefaultMeta(info);
this.write(info);
return this;
}
//
// 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;

// When provided nothing assume the empty string
if (args.length === 0) {
this.log(level, '');
return this;
}
// 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);
}

// 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 this.log(level, ...args);
};
// When provided nothing assume the empty string
if (args.length === 0) {
self.log(level, '');
return self;
}

this[isLevelEnabledFunctionName(level)] = () => this.isLevelEnabled(level);
});
}
}
// 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);
};

function isLevelEnabledFunctionName(level) {
return 'is' + level.charAt(0).toUpperCase() + level.slice(1) + 'Enabled';
}
DerivedLogger.prototype[isLevelEnabledFunctionName(level)] = function () {
return (this || logger).isLevelEnabled(level);
};
});

/**
* Create a new instance of a winston Logger. Creates a new
* prototype for each instance.
* @param {!Object} opts - Options for the created logger.
* @returns {Logger} - A newly created logger instance.
*/
module.exports = (opts = { levels: config.npm.levels }) => (
new DerivedLogger(opts)
);
return logger;
};
Loading