mirror of
https://github.com/log4js-node/log4js-node.git
synced 2025-12-08 19:26:01 +00:00
fix(tests): tests failing - config listeners not working in sandbox
This commit is contained in:
parent
da703cdebb
commit
8084e80027
13
examples/layouts.js
Normal file
13
examples/layouts.js
Normal file
@ -0,0 +1,13 @@
|
||||
const log4js = require('../lib/log4js');
|
||||
|
||||
log4js.configure({
|
||||
appenders: {
|
||||
out: { type: 'stdout', layout: { type: 'messagePassThrough' } }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['out'], level: 'info' }
|
||||
}
|
||||
});
|
||||
|
||||
const logger = log4js.getLogger('thing');
|
||||
logger.info('This should not have a timestamp');
|
||||
@ -1,81 +1,80 @@
|
||||
const CircularJSON = require('circular-json');
|
||||
const levels = require('./levels');
|
||||
|
||||
module.exports = (levels) => {
|
||||
/**
|
||||
* @name LoggingEvent
|
||||
* @namespace Log4js
|
||||
*/
|
||||
class LoggingEvent {
|
||||
/**
|
||||
* @name LoggingEvent
|
||||
* @namespace Log4js
|
||||
* Models a logging event.
|
||||
* @constructor
|
||||
* @param {String} categoryName name of category
|
||||
* @param {Log4js.Level} level level of message
|
||||
* @param {Array} data objects to log
|
||||
* @author Seth Chisamore
|
||||
*/
|
||||
class LoggingEvent {
|
||||
/**
|
||||
* Models a logging event.
|
||||
* @constructor
|
||||
* @param {String} categoryName name of category
|
||||
* @param {Log4js.Level} level level of message
|
||||
* @param {Array} data objects to log
|
||||
* @author Seth Chisamore
|
||||
*/
|
||||
constructor(categoryName, level, data, context) {
|
||||
this.startTime = new Date();
|
||||
this.categoryName = categoryName;
|
||||
this.data = data;
|
||||
this.level = level;
|
||||
this.context = Object.assign({}, context);
|
||||
this.pid = process.pid;
|
||||
// if (cluster && cluster.isWorker) {
|
||||
// this.cluster = {
|
||||
// workerId: cluster.worker.id,
|
||||
// worker: process.pid
|
||||
// };
|
||||
// }
|
||||
}
|
||||
constructor(categoryName, level, data, context) {
|
||||
this.startTime = new Date();
|
||||
this.categoryName = categoryName;
|
||||
this.data = data;
|
||||
this.level = level;
|
||||
this.context = Object.assign({}, context);
|
||||
this.pid = process.pid;
|
||||
// if (cluster && cluster.isWorker) {
|
||||
// this.cluster = {
|
||||
// workerId: cluster.worker.id,
|
||||
// worker: process.pid
|
||||
// };
|
||||
// }
|
||||
}
|
||||
|
||||
serialise() {
|
||||
// JSON.stringify(new Error('test')) returns {}, which is not really useful for us.
|
||||
// The following allows us to serialize errors correctly.
|
||||
// Validate that we really are in this case
|
||||
try {
|
||||
const logData = this.data.map((e) => {
|
||||
if (e && e.stack && CircularJSON.stringify(e) === '{}') {
|
||||
e = { message: e.message, stack: e.stack };
|
||||
}
|
||||
return e;
|
||||
});
|
||||
this.data = logData;
|
||||
return CircularJSON.stringify(this);
|
||||
} catch (e) {
|
||||
return new LoggingEvent(
|
||||
'log4js',
|
||||
levels.ERROR,
|
||||
['Unable to serialise log event due to :', e]
|
||||
).serialise();
|
||||
}
|
||||
}
|
||||
|
||||
static deserialise(serialised) {
|
||||
let event;
|
||||
try {
|
||||
event = CircularJSON.parse(serialised);
|
||||
event.startTime = new Date(event.startTime);
|
||||
event.level = levels.getLevel(event.level.levelStr);
|
||||
event.data = event.data.map((e) => {
|
||||
if (e && e.stack) {
|
||||
const fakeError = new Error(e.message);
|
||||
fakeError.stack = e.stack;
|
||||
e = fakeError;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
} catch (e) {
|
||||
event = new LoggingEvent(
|
||||
'log4js',
|
||||
levels.ERROR,
|
||||
['Unable to parse log:', serialised, 'because: ', e]
|
||||
);
|
||||
}
|
||||
|
||||
return event;
|
||||
serialise() {
|
||||
// JSON.stringify(new Error('test')) returns {}, which is not really useful for us.
|
||||
// The following allows us to serialize errors correctly.
|
||||
// Validate that we really are in this case
|
||||
try {
|
||||
const logData = this.data.map((e) => {
|
||||
if (e && e.stack && CircularJSON.stringify(e) === '{}') {
|
||||
e = { message: e.message, stack: e.stack };
|
||||
}
|
||||
return e;
|
||||
});
|
||||
this.data = logData;
|
||||
return CircularJSON.stringify(this);
|
||||
} catch (e) {
|
||||
return new LoggingEvent(
|
||||
'log4js',
|
||||
levels.ERROR,
|
||||
['Unable to serialise log event due to :', e]
|
||||
).serialise();
|
||||
}
|
||||
}
|
||||
|
||||
return LoggingEvent;
|
||||
};
|
||||
static deserialise(serialised) {
|
||||
let event;
|
||||
try {
|
||||
event = CircularJSON.parse(serialised);
|
||||
event.startTime = new Date(event.startTime);
|
||||
event.level = levels.getLevel(event.level.levelStr);
|
||||
event.data = event.data.map((e) => {
|
||||
if (e && e.stack) {
|
||||
const fakeError = new Error(e.message);
|
||||
fakeError.stack = e.stack;
|
||||
e = fakeError;
|
||||
}
|
||||
return e;
|
||||
});
|
||||
} catch (e) {
|
||||
event = new LoggingEvent(
|
||||
'log4js',
|
||||
levels.ERROR,
|
||||
['Unable to parse log:', serialised, 'because: ', e]
|
||||
);
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LoggingEvent;
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const debug = require('debug')('log4js:categoryFilter');
|
||||
|
||||
function categoryFilter(excludes, appender) {
|
||||
if (typeof excludes === 'string') excludes = [excludes];
|
||||
return (logEvent) => {
|
||||
debug(`Checking ${logEvent.categoryName} against ${excludes}`);
|
||||
if (excludes.indexOf(logEvent.categoryName) === -1) {
|
||||
debug('Not excluded, sending to appender');
|
||||
appender(logEvent);
|
||||
}
|
||||
};
|
||||
|
||||
97
lib/appenders/index.js
Normal file
97
lib/appenders/index.js
Normal file
@ -0,0 +1,97 @@
|
||||
const path = require('path');
|
||||
const debug = require('debug')('log4js:appenders');
|
||||
const configuration = require('../configuration');
|
||||
const clustering = require('../clustering');
|
||||
const levels = require('../levels');
|
||||
const layouts = require('../layouts');
|
||||
|
||||
const appenders = new Map();
|
||||
|
||||
const tryLoading = (modulePath, config) => {
|
||||
debug('Loading module from ', modulePath);
|
||||
try {
|
||||
return require(modulePath); //eslint-disable-line
|
||||
} catch (e) {
|
||||
// if the module was found, and we still got an error, then raise it
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
e.code !== 'MODULE_NOT_FOUND',
|
||||
`appender "${modulePath}" could not be loaded (error was: ${e})`
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const loadAppenderModule = (type, config) => tryLoading(`./${type}`, config) ||
|
||||
tryLoading(type, config) ||
|
||||
tryLoading(path.join(path.dirname(require.main.filename), type), config) ||
|
||||
tryLoading(path.join(process.cwd(), type), config);
|
||||
|
||||
const createAppender = (name, config) => {
|
||||
const appenderConfig = config.appenders[name];
|
||||
const appenderModule = loadAppenderModule(appenderConfig.type, config);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(appenderModule),
|
||||
`appender "${name}" is not valid (type "${appenderConfig.type}" could not be found)`
|
||||
);
|
||||
if (appenderModule.appender) {
|
||||
debug(`DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`);
|
||||
}
|
||||
if (appenderModule.shutdown) {
|
||||
debug(`DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`);
|
||||
}
|
||||
|
||||
debug(`${name}: clustering.isMaster ? ${clustering.isMaster()}`);
|
||||
debug(`${name}: appenderModule is ${require('util').inspect(appenderModule)}`); // eslint-disable-line
|
||||
return clustering.onlyOnMaster(() => {
|
||||
debug(`calling appenderModule.configure for ${name} / ${appenderConfig.type}`);
|
||||
return appenderModule.configure(
|
||||
appenderConfig,
|
||||
layouts,
|
||||
appender => appenders.get(appender),
|
||||
levels
|
||||
);
|
||||
}, () => {});
|
||||
};
|
||||
|
||||
const setup = (config) => {
|
||||
appenders.clear();
|
||||
|
||||
Object.keys(config.appenders).forEach((name) => {
|
||||
debug(`Creating appender ${name}`);
|
||||
appenders.set(name, createAppender(name, config));
|
||||
});
|
||||
};
|
||||
|
||||
// setup({
|
||||
// appenders: {
|
||||
// stdout: { type: 'stdout' }
|
||||
// }
|
||||
// });
|
||||
|
||||
configuration.addListener((config) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.anObject(config.appenders)),
|
||||
'must have a property "appenders" of type object.'
|
||||
);
|
||||
const appenderNames = Object.keys(config.appenders);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(appenderNames.length),
|
||||
'must define at least one appender.'
|
||||
);
|
||||
|
||||
appenderNames.forEach((name) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(config.appenders[name].type),
|
||||
`appender "${name}" is not valid (must be an object with property "type")`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
configuration.addListener(setup);
|
||||
|
||||
module.exports = appenders;
|
||||
125
lib/categories.js
Normal file
125
lib/categories.js
Normal file
@ -0,0 +1,125 @@
|
||||
const configuration = require('./configuration');
|
||||
const levels = require('./levels');
|
||||
const appenders = require('./appenders');
|
||||
const debug = require('debug')('log4js:categories');
|
||||
|
||||
const categories = new Map();
|
||||
|
||||
configuration.addListener((config) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.anObject(config.categories)),
|
||||
'must have a property "categories" of type object.'
|
||||
);
|
||||
|
||||
const categoryNames = Object.keys(config.categories);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(categoryNames.length),
|
||||
'must define at least one category.'
|
||||
);
|
||||
|
||||
categoryNames.forEach((name) => {
|
||||
const category = config.categories[name];
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
[
|
||||
configuration.not(category.appenders),
|
||||
configuration.not(category.level)
|
||||
],
|
||||
`category "${name}" is not valid (must be an object with properties "appenders" and "level")`
|
||||
);
|
||||
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(Array.isArray(category.appenders)),
|
||||
`category "${name}" is not valid (appenders must be an array of appender names)`
|
||||
);
|
||||
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(category.appenders.length),
|
||||
`category "${name}" is not valid (appenders must contain at least one appender name)`
|
||||
);
|
||||
|
||||
category.appenders.forEach((appender) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(appenders.get(appender)),
|
||||
`category "${name}" is not valid (appender "${appender}" is not defined)`
|
||||
);
|
||||
});
|
||||
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(levels.getLevel(category.level)),
|
||||
`category "${name}" is not valid (level "${category.level}" not recognised;` +
|
||||
` valid levels are ${levels.levels.join(', ')})`
|
||||
);
|
||||
});
|
||||
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(config.categories.default),
|
||||
'must define a "default" category.'
|
||||
);
|
||||
});
|
||||
|
||||
const setup = (config) => {
|
||||
categories.clear();
|
||||
|
||||
const categoryNames = Object.keys(config.categories);
|
||||
categoryNames.forEach((name) => {
|
||||
const category = config.categories[name];
|
||||
const categoryAppenders = [];
|
||||
category.appenders.forEach((appender) => {
|
||||
categoryAppenders.push(appenders.get(appender));
|
||||
debug(`Creating category ${name}`);
|
||||
categories.set(
|
||||
name,
|
||||
{ appenders: categoryAppenders, level: levels.getLevel(category.level) }
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// setup({
|
||||
// categories: { default: { appenders: ['stdout'], level: 'OFF' } }
|
||||
// });
|
||||
configuration.addListener(setup);
|
||||
|
||||
const configForCategory = (category) => {
|
||||
debug(`configForCategory: searching for config for ${category}`);
|
||||
if (categories.has(category)) {
|
||||
debug(`configForCategory: ${category} exists in config, returning it`);
|
||||
return categories.get(category);
|
||||
}
|
||||
if (category.indexOf('.') > 0) {
|
||||
debug(`configForCategory: ${category} has hierarchy, searching for parents`);
|
||||
return configForCategory(category.substring(0, category.lastIndexOf('.')));
|
||||
}
|
||||
debug('configForCategory: returning config for default category');
|
||||
return configForCategory('default');
|
||||
};
|
||||
|
||||
const appendersForCategory = category => configForCategory(category).appenders;
|
||||
const getLevelForCategory = category => configForCategory(category).level;
|
||||
|
||||
const setLevelForCategory = (category, level) => {
|
||||
let categoryConfig = categories.get(category);
|
||||
debug(`setLevelForCategory: found ${categoryConfig} for ${category}`);
|
||||
if (!categoryConfig) {
|
||||
const sourceCategoryConfig = configForCategory(category);
|
||||
debug('setLevelForCategory: no config found for category, ' +
|
||||
`found ${sourceCategoryConfig} for parents of ${category}`);
|
||||
categoryConfig = { appenders: sourceCategoryConfig.appenders };
|
||||
}
|
||||
categoryConfig.level = level;
|
||||
categories.set(category, categoryConfig);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
appendersForCategory,
|
||||
getLevelForCategory,
|
||||
setLevelForCategory
|
||||
};
|
||||
@ -1,47 +1,60 @@
|
||||
const debug = require('debug')('log4js:clustering');
|
||||
const LoggingEvent = require('./LoggingEvent');
|
||||
const configuration = require('./configuration');
|
||||
const cluster = require('cluster');
|
||||
|
||||
let cluster;
|
||||
try {
|
||||
cluster = require('cluster'); // eslint-disable-line global-require
|
||||
} catch (e) {
|
||||
debug('Clustering support disabled because require(cluster) threw an error: ', e);
|
||||
}
|
||||
const listeners = [];
|
||||
|
||||
module.exports = (config) => {
|
||||
const disabled = config.disableClustering || !cluster;
|
||||
const pm2 = config.pm2;
|
||||
const pm2InstanceVar = config.pm2InstanceVar || 'NODE_APP_INSTANCE';
|
||||
const listeners = [];
|
||||
let disabled = false;
|
||||
let pm2 = false;
|
||||
let pm2InstanceVar = 'NODE_APP_INSTANCE';
|
||||
|
||||
const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === '0';
|
||||
const isMaster = () => disabled || cluster.isMaster || isPM2Master();
|
||||
const isWorker = () => !isMaster();
|
||||
|
||||
const sendToListeners = (logEvent) => {
|
||||
listeners.forEach(l => l(logEvent));
|
||||
};
|
||||
|
||||
// in a multi-process node environment, worker loggers will use
|
||||
// process.send
|
||||
const receiver = (worker, message) => {
|
||||
// prior to node v6, the worker parameter was not passed (args were message, handle)
|
||||
debug('cluster message received from worker ', worker, ': ', message);
|
||||
if (worker.topic && worker.data) {
|
||||
message = worker;
|
||||
worker = undefined;
|
||||
}
|
||||
if (message && message.topic && message.topic === 'log4js:message') {
|
||||
debug('received message: ', message.data);
|
||||
const logEvent = LoggingEvent.deserialise(message.data);
|
||||
sendToListeners(logEvent);
|
||||
}
|
||||
};
|
||||
|
||||
configuration.addListener((config) => {
|
||||
// clear out the listeners, because configure has been called.
|
||||
listeners.length = 0;
|
||||
|
||||
disabled = config.disableClustering;
|
||||
pm2 = config.pm2;
|
||||
pm2InstanceVar = config.pm2InstanceVar || 'NODE_APP_INSTANCE';
|
||||
|
||||
debug(`clustering disabled ? ${disabled}`);
|
||||
debug(`cluster.isMaster ? ${cluster && cluster.isMaster}`);
|
||||
debug(`cluster.isMaster ? ${cluster.isMaster}`);
|
||||
debug(`pm2 enabled ? ${pm2}`);
|
||||
debug(`pm2InstanceVar = ${pm2InstanceVar}`);
|
||||
debug(`process.env[${pm2InstanceVar}] = ${process.env[pm2InstanceVar]}`);
|
||||
|
||||
const isPM2Master = () => pm2 && process.env[pm2InstanceVar] === '0';
|
||||
const isMaster = () => disabled || cluster.isMaster || isPM2Master();
|
||||
const isWorker = () => !isMaster();
|
||||
|
||||
// in a multi-process node environment, worker loggers will use
|
||||
// process.send
|
||||
const receiver = (worker, message) => {
|
||||
// prior to node v6, the worker parameter was not passed (args were message, handle)
|
||||
debug('cluster message received from worker ', worker, ': ', message);
|
||||
if (worker.topic && worker.data) {
|
||||
message = worker;
|
||||
worker = undefined;
|
||||
}
|
||||
if (message && message.topic && message.topic === 'log4js:message') {
|
||||
debug('received message: ', message.data);
|
||||
const logEvent = LoggingEvent.deserialise(message.data);
|
||||
listeners.forEach(l => l(logEvent));
|
||||
}
|
||||
};
|
||||
|
||||
// just in case configure is called after shutdown
|
||||
pm2 && process.removeListener('message', receiver);
|
||||
cluster.removeListener('message', receiver);
|
||||
if (pm2) {
|
||||
process.removeListener('message', receiver);
|
||||
}
|
||||
if (cluster.removeListener) {
|
||||
cluster.removeListener('message', receiver);
|
||||
}
|
||||
|
||||
if (config.disableClustering) {
|
||||
debug('Not listening for cluster messages, because clustering disabled.');
|
||||
} else if (isPM2Master()) {
|
||||
@ -56,26 +69,29 @@ module.exports = (config) => {
|
||||
} else {
|
||||
debug('not listening for messages, because we are not a master process');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
return {
|
||||
onlyOnMaster: (fn) => {
|
||||
if (isMaster()) {
|
||||
fn();
|
||||
module.exports = {
|
||||
onlyOnMaster: (fn, notMaster) => (isMaster() ? fn() : notMaster),
|
||||
onlyOnWorker: (fn, notWorker) => (isWorker() ? fn() : notWorker),
|
||||
isMaster: isMaster,
|
||||
isWorker: isWorker,
|
||||
send: (msg) => {
|
||||
if (isWorker()) {
|
||||
if (pm2) {
|
||||
process.send({ type: 'log4js:message', data: msg.serialise() });
|
||||
} else {
|
||||
msg.cluster = {
|
||||
workerId: cluster.worker.id,
|
||||
worker: process.pid
|
||||
};
|
||||
cluster.send({ type: 'log4js:message', data: msg.serialise() });
|
||||
}
|
||||
},
|
||||
onlyOnWorker: (fn) => {
|
||||
if (isWorker()) {
|
||||
fn();
|
||||
}
|
||||
},
|
||||
isMaster: isMaster,
|
||||
isWorker: isWorker,
|
||||
send: (msg) => {
|
||||
|
||||
},
|
||||
onMessage: (listener) => {
|
||||
listeners.push(listener);
|
||||
} else {
|
||||
sendToListeners(msg);
|
||||
}
|
||||
};
|
||||
},
|
||||
onMessage: (listener) => {
|
||||
listeners.push(listener);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,201 +1,49 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
const path = require('path');
|
||||
const levels = require('./levels');
|
||||
const layouts = require('./layouts');
|
||||
const clustering = require('./clustering');
|
||||
const debug = require('debug')('log4js:configuration');
|
||||
|
||||
const validColours = [
|
||||
'white', 'grey', 'black',
|
||||
'blue', 'cyan', 'green',
|
||||
'magenta', 'red', 'yellow'
|
||||
];
|
||||
const listeners = [];
|
||||
|
||||
function not(thing) {
|
||||
return !thing;
|
||||
}
|
||||
const not = thing => !thing;
|
||||
|
||||
function anObject(thing) {
|
||||
return thing && typeof thing === 'object' && !Array.isArray(thing);
|
||||
}
|
||||
const anObject = thing => thing && typeof thing === 'object' && !Array.isArray(thing);
|
||||
|
||||
function validIdentifier(thing) {
|
||||
return /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing);
|
||||
}
|
||||
const validIdentifier = thing => /^[A-Za-z][A-Za-z0-9_]*$/g.test(thing);
|
||||
|
||||
function anInteger(thing) {
|
||||
return thing && typeof thing === 'number' && Number.isInteger(thing);
|
||||
}
|
||||
const anInteger = thing => thing && typeof thing === 'number' && Number.isInteger(thing);
|
||||
|
||||
class Configuration {
|
||||
throwExceptionIf(checks, message) {
|
||||
const tests = Array.isArray(checks) ? checks : [checks];
|
||||
tests.forEach((test) => {
|
||||
if (test) {
|
||||
throw new Error(`Problem with log4js configuration: (${util.inspect(this.candidate, { depth: 5 })})` +
|
||||
` - ${message}`);
|
||||
}
|
||||
});
|
||||
const addListener = (fn) => {
|
||||
if (fn) {
|
||||
listeners.push(fn);
|
||||
}
|
||||
};
|
||||
|
||||
tryLoading(modulePath) {
|
||||
debug('Loading module from ', modulePath);
|
||||
try {
|
||||
return require(modulePath); //eslint-disable-line
|
||||
} catch (e) {
|
||||
// if the module was found, and we still got an error, then raise it
|
||||
this.throwExceptionIf(
|
||||
e.code !== 'MODULE_NOT_FOUND',
|
||||
`appender "${path}" could not be loaded (error was: ${e})`
|
||||
);
|
||||
return undefined;
|
||||
const throwExceptionIf = (config, checks, message) => {
|
||||
const tests = Array.isArray(checks) ? checks : [checks];
|
||||
tests.forEach((test) => {
|
||||
if (test) {
|
||||
throw new Error(`Problem with log4js configuration: (${util.inspect(config, { depth: 5 })})` +
|
||||
` - ${message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadAppenderModule(type) {
|
||||
return this.tryLoading(`./appenders/${type}`) ||
|
||||
this.tryLoading(type) ||
|
||||
this.tryLoading(path.join(path.dirname(require.main.filename), type)) ||
|
||||
this.tryLoading(path.join(process.cwd(), type));
|
||||
}
|
||||
const configure = (candidate) => {
|
||||
debug('New configuration to be validated: ', candidate);
|
||||
throwExceptionIf(candidate, not(anObject(candidate)), 'must be an object.');
|
||||
|
||||
createAppender(name, config) {
|
||||
const appenderModule = this.loadAppenderModule(config.type);
|
||||
this.throwExceptionIf(
|
||||
not(appenderModule),
|
||||
`appender "${name}" is not valid (type "${config.type}" could not be found)`
|
||||
);
|
||||
if (appenderModule.appender) {
|
||||
debug(`DEPRECATION: Appender ${config.type} exports an appender function.`);
|
||||
}
|
||||
if (appenderModule.shutdown) {
|
||||
debug(`DEPRECATION: Appender ${config.type} exports a shutdown function.`);
|
||||
}
|
||||
debug('Calling configuration listeners');
|
||||
listeners.forEach(listener => listener(candidate));
|
||||
debug('Configuration finished.');
|
||||
};
|
||||
|
||||
this.clustering.onlyOnMaster(() => appenderModule.configure(
|
||||
config,
|
||||
layouts,
|
||||
this.configuredAppenders.get.bind(this.configuredAppenders),
|
||||
this.configuredLevels
|
||||
));
|
||||
return () => {};
|
||||
}
|
||||
|
||||
get appenders() {
|
||||
return this.configuredAppenders;
|
||||
}
|
||||
|
||||
set appenders(appenderConfig) {
|
||||
const appenderNames = Object.keys(appenderConfig);
|
||||
this.throwExceptionIf(not(appenderNames.length), 'must define at least one appender.');
|
||||
|
||||
this.configuredAppenders = new Map();
|
||||
appenderNames.forEach((name) => {
|
||||
this.throwExceptionIf(
|
||||
not(appenderConfig[name].type),
|
||||
`appender "${name}" is not valid (must be an object with property "type")`
|
||||
);
|
||||
|
||||
debug(`Creating appender ${name}`);
|
||||
this.configuredAppenders.set(name, this.createAppender(name, appenderConfig[name]));
|
||||
});
|
||||
}
|
||||
|
||||
get categories() {
|
||||
return this.configuredCategories;
|
||||
}
|
||||
|
||||
set categories(categoryConfig) {
|
||||
const categoryNames = Object.keys(categoryConfig);
|
||||
this.throwExceptionIf(not(categoryNames.length), 'must define at least one category.');
|
||||
|
||||
this.configuredCategories = new Map();
|
||||
categoryNames.forEach((name) => {
|
||||
const category = categoryConfig[name];
|
||||
this.throwExceptionIf(
|
||||
[
|
||||
not(category.appenders),
|
||||
not(category.level)
|
||||
],
|
||||
`category "${name}" is not valid (must be an object with properties "appenders" and "level")`
|
||||
);
|
||||
|
||||
this.throwExceptionIf(
|
||||
not(Array.isArray(category.appenders)),
|
||||
`category "${name}" is not valid (appenders must be an array of appender names)`
|
||||
);
|
||||
|
||||
this.throwExceptionIf(
|
||||
not(category.appenders.length),
|
||||
`category "${name}" is not valid (appenders must contain at least one appender name)`
|
||||
);
|
||||
|
||||
const appenders = [];
|
||||
category.appenders.forEach((appender) => {
|
||||
this.throwExceptionIf(
|
||||
not(this.configuredAppenders.get(appender)),
|
||||
`category "${name}" is not valid (appender "${appender}" is not defined)`
|
||||
);
|
||||
appenders.push(this.appenders.get(appender));
|
||||
});
|
||||
|
||||
this.throwExceptionIf(
|
||||
not(this.configuredLevels.getLevel(category.level)),
|
||||
`category "${name}" is not valid (level "${category.level}" not recognised;` +
|
||||
` valid levels are ${this.configuredLevels.levels.join(', ')})`
|
||||
);
|
||||
|
||||
debug(`Creating category ${name}`);
|
||||
this.configuredCategories.set(
|
||||
name,
|
||||
{ appenders: appenders, level: this.configuredLevels.getLevel(category.level) }
|
||||
);
|
||||
});
|
||||
|
||||
this.throwExceptionIf(not(categoryConfig.default), 'must define a "default" category.');
|
||||
}
|
||||
|
||||
get levels() {
|
||||
return this.configuredLevels;
|
||||
}
|
||||
|
||||
set levels(levelConfig) {
|
||||
// levels are optional
|
||||
if (levelConfig) {
|
||||
this.throwExceptionIf(not(anObject(levelConfig)), 'levels must be an object');
|
||||
const newLevels = Object.keys(levelConfig);
|
||||
newLevels.forEach((l) => {
|
||||
this.throwExceptionIf(
|
||||
not(validIdentifier(l)),
|
||||
`level name "${l}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)`
|
||||
);
|
||||
this.throwExceptionIf(not(anObject(levelConfig[l])), `level "${l}" must be an object`);
|
||||
this.throwExceptionIf(not(levelConfig[l].value), `level "${l}" must have a 'value' property`);
|
||||
this.throwExceptionIf(not(anInteger(levelConfig[l].value)), `level "${l}".value must have an integer value`);
|
||||
this.throwExceptionIf(not(levelConfig[l].colour), `level "${l}" must have a 'colour' property`);
|
||||
this.throwExceptionIf(
|
||||
not(validColours.indexOf(levelConfig[l].colour) > -1),
|
||||
`level "${l}".colour must be one of ${validColours.join(', ')}`
|
||||
);
|
||||
});
|
||||
}
|
||||
this.configuredLevels = levels(levelConfig);
|
||||
}
|
||||
|
||||
constructor(candidate) {
|
||||
this.candidate = candidate;
|
||||
|
||||
this.throwExceptionIf(not(anObject(candidate)), 'must be an object.');
|
||||
this.throwExceptionIf(not(anObject(candidate.appenders)), 'must have a property "appenders" of type object.');
|
||||
this.throwExceptionIf(not(anObject(candidate.categories)), 'must have a property "categories" of type object.');
|
||||
|
||||
this.levels = candidate.levels;
|
||||
this.clustering = clustering(this.candidate);
|
||||
this.appenders = candidate.appenders;
|
||||
this.categories = candidate.categories;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Configuration;
|
||||
module.exports = {
|
||||
configure,
|
||||
addListener,
|
||||
throwExceptionIf,
|
||||
anObject,
|
||||
anInteger,
|
||||
validIdentifier,
|
||||
not
|
||||
};
|
||||
|
||||
@ -2,6 +2,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const levels = require('./levels');
|
||||
|
||||
const DEFAULT_FORMAT = ':remote-addr - -' +
|
||||
' ":method :url HTTP/:http-version"' +
|
||||
' :status :content-length ":referrer"' +
|
||||
@ -163,8 +165,7 @@ function createNoLogCondition(nolog) {
|
||||
return regexp;
|
||||
}
|
||||
|
||||
module.exports = function (levels) {
|
||||
/**
|
||||
/**
|
||||
* Log requests with the given `options` or a `format` string.
|
||||
*
|
||||
* Options:
|
||||
@ -192,80 +193,77 @@ module.exports = function (levels) {
|
||||
* @param options
|
||||
* @api public
|
||||
*/
|
||||
function getLogger(logger4js, options) {
|
||||
/* eslint no-underscore-dangle:0 */
|
||||
if (typeof options === 'object') {
|
||||
options = options || {};
|
||||
} else if (options) {
|
||||
options = { format: options };
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
const thisLogger = logger4js;
|
||||
let level = levels.getLevel(options.level, levels.INFO);
|
||||
const fmt = options.format || DEFAULT_FORMAT;
|
||||
const nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
|
||||
|
||||
return (req, res, next) => {
|
||||
// mount safety
|
||||
if (req._logging) return next();
|
||||
|
||||
// nologs
|
||||
if (nolog && nolog.test(req.originalUrl)) return next();
|
||||
|
||||
if (thisLogger.isLevelEnabled(level) || options.level === 'auto') {
|
||||
const start = new Date();
|
||||
const writeHead = res.writeHead;
|
||||
|
||||
// flag as logging
|
||||
req._logging = true;
|
||||
|
||||
// proxy for statusCode.
|
||||
res.writeHead = (code, headers) => {
|
||||
res.writeHead = writeHead;
|
||||
res.writeHead(code, headers);
|
||||
|
||||
res.__statusCode = code;
|
||||
res.__headers = headers || {};
|
||||
|
||||
// status code response level handling
|
||||
if (options.level === 'auto') {
|
||||
level = levels.INFO;
|
||||
if (code >= 300) level = levels.WARN;
|
||||
if (code >= 400) level = levels.ERROR;
|
||||
} else {
|
||||
level = levels.getLevel(options.level, levels.INFO);
|
||||
}
|
||||
};
|
||||
|
||||
// hook on end request to emit the log entry of the HTTP request.
|
||||
res.on('finish', () => {
|
||||
res.responseTime = new Date() - start;
|
||||
// status code response level handling
|
||||
if (res.statusCode && options.level === 'auto') {
|
||||
level = levels.INFO;
|
||||
if (res.statusCode >= 300) level = levels.WARN;
|
||||
if (res.statusCode >= 400) level = levels.ERROR;
|
||||
}
|
||||
|
||||
if (thisLogger.isLevelEnabled(level)) {
|
||||
const combinedTokens = assembleTokens(req, res, options.tokens || []);
|
||||
|
||||
if (typeof fmt === 'function') {
|
||||
const line = fmt(req, res, str => format(str, combinedTokens));
|
||||
if (line) thisLogger.log(level, line);
|
||||
} else {
|
||||
thisLogger.log(level, format(fmt, combinedTokens));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure next gets always called
|
||||
return next();
|
||||
};
|
||||
module.exports = function getLogger(logger4js, options) {
|
||||
/* eslint no-underscore-dangle:0 */
|
||||
if (typeof options === 'object') {
|
||||
options = options || {};
|
||||
} else if (options) {
|
||||
options = { format: options };
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
return { connectLogger: getLogger };
|
||||
const thisLogger = logger4js;
|
||||
let level = levels.getLevel(options.level, levels.INFO);
|
||||
const fmt = options.format || DEFAULT_FORMAT;
|
||||
const nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
|
||||
|
||||
return (req, res, next) => {
|
||||
// mount safety
|
||||
if (req._logging) return next();
|
||||
|
||||
// nologs
|
||||
if (nolog && nolog.test(req.originalUrl)) return next();
|
||||
|
||||
if (thisLogger.isLevelEnabled(level) || options.level === 'auto') {
|
||||
const start = new Date();
|
||||
const writeHead = res.writeHead;
|
||||
|
||||
// flag as logging
|
||||
req._logging = true;
|
||||
|
||||
// proxy for statusCode.
|
||||
res.writeHead = (code, headers) => {
|
||||
res.writeHead = writeHead;
|
||||
res.writeHead(code, headers);
|
||||
|
||||
res.__statusCode = code;
|
||||
res.__headers = headers || {};
|
||||
|
||||
// status code response level handling
|
||||
if (options.level === 'auto') {
|
||||
level = levels.INFO;
|
||||
if (code >= 300) level = levels.WARN;
|
||||
if (code >= 400) level = levels.ERROR;
|
||||
} else {
|
||||
level = levels.getLevel(options.level, levels.INFO);
|
||||
}
|
||||
};
|
||||
|
||||
// hook on end request to emit the log entry of the HTTP request.
|
||||
res.on('finish', () => {
|
||||
res.responseTime = new Date() - start;
|
||||
// status code response level handling
|
||||
if (res.statusCode && options.level === 'auto') {
|
||||
level = levels.INFO;
|
||||
if (res.statusCode >= 300) level = levels.WARN;
|
||||
if (res.statusCode >= 400) level = levels.ERROR;
|
||||
}
|
||||
|
||||
if (thisLogger.isLevelEnabled(level)) {
|
||||
const combinedTokens = assembleTokens(req, res, options.tokens || []);
|
||||
|
||||
if (typeof fmt === 'function') {
|
||||
const line = fmt(req, res, str => format(str, combinedTokens));
|
||||
if (line) thisLogger.log(level, line);
|
||||
} else {
|
||||
thisLogger.log(level, format(fmt, combinedTokens));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure next gets always called
|
||||
return next();
|
||||
};
|
||||
};
|
||||
|
||||
174
lib/levels.js
174
lib/levels.js
@ -1,60 +1,22 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = function (customLevels) {
|
||||
/**
|
||||
* @name Level
|
||||
* @namespace Log4js
|
||||
*/
|
||||
class Level {
|
||||
constructor(level, levelStr, colour) {
|
||||
this.level = level;
|
||||
this.levelStr = levelStr;
|
||||
this.colour = colour;
|
||||
}
|
||||
const configuration = require('./configuration');
|
||||
|
||||
toString() {
|
||||
return this.levelStr;
|
||||
}
|
||||
const validColours = [
|
||||
'white', 'grey', 'black',
|
||||
'blue', 'cyan', 'green',
|
||||
'magenta', 'red', 'yellow'
|
||||
];
|
||||
|
||||
isLessThanOrEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = getLevel(otherLevel);
|
||||
}
|
||||
return this.level <= otherLevel.level;
|
||||
}
|
||||
|
||||
isGreaterThanOrEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = getLevel(otherLevel);
|
||||
}
|
||||
return this.level >= otherLevel.level;
|
||||
}
|
||||
|
||||
isEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = getLevel(otherLevel);
|
||||
}
|
||||
return this.level === otherLevel.level;
|
||||
}
|
||||
class Level {
|
||||
constructor(level, levelStr, colour) {
|
||||
this.level = level;
|
||||
this.levelStr = levelStr;
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
const defaultLevels = {
|
||||
ALL: new Level(Number.MIN_VALUE, 'ALL', 'grey'),
|
||||
TRACE: new Level(5000, 'TRACE', 'blue'),
|
||||
DEBUG: new Level(10000, 'DEBUG', 'cyan'),
|
||||
INFO: new Level(20000, 'INFO', 'green'),
|
||||
WARN: new Level(30000, 'WARN', 'yellow'),
|
||||
ERROR: new Level(40000, 'ERROR', 'red'),
|
||||
FATAL: new Level(50000, 'FATAL', 'magenta'),
|
||||
MARK: new Level(9007199254740992, 'MARK', 'grey'), // 2^53
|
||||
OFF: new Level(Number.MAX_VALUE, 'OFF', 'grey')
|
||||
};
|
||||
|
||||
if (customLevels) {
|
||||
const levels = Object.keys(customLevels);
|
||||
levels.forEach((l) => {
|
||||
defaultLevels[l.toUpperCase()] = new Level(customLevels[l].value, l.toUpperCase(), customLevels[l].colour);
|
||||
});
|
||||
toString() {
|
||||
return this.levelStr;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,7 +25,7 @@ module.exports = function (customLevels) {
|
||||
* @param {Level} [defaultLevel] -- default Level, if no String representation
|
||||
* @return {Level}
|
||||
*/
|
||||
function getLevel(sArg, defaultLevel) {
|
||||
static getLevel(sArg, defaultLevel) {
|
||||
if (!sArg) {
|
||||
return defaultLevel;
|
||||
}
|
||||
@ -73,15 +35,109 @@ module.exports = function (customLevels) {
|
||||
}
|
||||
|
||||
if (typeof sArg === 'string') {
|
||||
return defaultLevels[sArg.toUpperCase()] || defaultLevel;
|
||||
return Level[sArg.toUpperCase()] || defaultLevel;
|
||||
}
|
||||
|
||||
return getLevel(sArg.toString());
|
||||
return Level.getLevel(sArg.toString());
|
||||
}
|
||||
|
||||
const orderedLevels = Object.keys(defaultLevels).sort((a, b) => b.level - a.level);
|
||||
defaultLevels.getLevel = getLevel;
|
||||
defaultLevels.levels = orderedLevels;
|
||||
static addLevels(customLevels) {
|
||||
if (customLevels) {
|
||||
const levels = Object.keys(customLevels);
|
||||
levels.forEach((l) => {
|
||||
Level[l.toUpperCase()] = new Level(
|
||||
customLevels[l].value,
|
||||
l.toUpperCase(),
|
||||
customLevels[l].colour
|
||||
);
|
||||
Level.levels.push(Level[l.toUpperCase()]);
|
||||
});
|
||||
Level.levels.sort((a, b) => a.level - b.level);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultLevels;
|
||||
};
|
||||
|
||||
isLessThanOrEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = Level.getLevel(otherLevel);
|
||||
}
|
||||
return this.level <= otherLevel.level;
|
||||
}
|
||||
|
||||
isGreaterThanOrEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = Level.getLevel(otherLevel);
|
||||
}
|
||||
return this.level >= otherLevel.level;
|
||||
}
|
||||
|
||||
isEqualTo(otherLevel) {
|
||||
if (typeof otherLevel === 'string') {
|
||||
otherLevel = Level.getLevel(otherLevel);
|
||||
}
|
||||
return this.level === otherLevel.level;
|
||||
}
|
||||
}
|
||||
|
||||
Level.levels = [];
|
||||
Level.addLevels({
|
||||
ALL: { value: Number.MIN_VALUE, colour: 'grey' },
|
||||
TRACE: { value: 5000, colour: 'blue' },
|
||||
DEBUG: { value: 10000, colour: 'cyan' },
|
||||
INFO: { value: 20000, colour: 'green' },
|
||||
WARN: { value: 30000, colour: 'yellow' },
|
||||
ERROR: { value: 40000, colour: 'red' },
|
||||
FATAL: { value: 50000, colour: 'magenta' },
|
||||
MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53
|
||||
OFF: { value: Number.MAX_VALUE, colour: 'grey' }
|
||||
});
|
||||
|
||||
configuration.addListener((config) => {
|
||||
const levelConfig = config.levels;
|
||||
if (levelConfig) {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.anObject(levelConfig)),
|
||||
'levels must be an object'
|
||||
);
|
||||
const newLevels = Object.keys(levelConfig);
|
||||
newLevels.forEach((l) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.validIdentifier(l)),
|
||||
`level name "${l}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)`
|
||||
);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.anObject(levelConfig[l])),
|
||||
`level "${l}" must be an object`
|
||||
);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(levelConfig[l].value),
|
||||
`level "${l}" must have a 'value' property`
|
||||
);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(configuration.anInteger(levelConfig[l].value)),
|
||||
`level "${l}".value must have an integer value`
|
||||
);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(levelConfig[l].colour),
|
||||
`level "${l}" must have a 'colour' property`
|
||||
);
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
configuration.not(validColours.indexOf(levelConfig[l].colour) > -1),
|
||||
`level "${l}".colour must be one of ${validColours.join(', ')}`
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
configuration.addListener((config) => {
|
||||
Level.addLevels(config.levels);
|
||||
});
|
||||
|
||||
module.exports = Level;
|
||||
|
||||
@ -15,88 +15,33 @@
|
||||
*
|
||||
* NOTE: the authors below are the original browser-based log4js authors
|
||||
* don't try to contact them about bugs in this version :)
|
||||
* @version 1.0
|
||||
* @author Stephan Strittmatter - http://jroller.com/page/stritti
|
||||
* @author Seth Chisamore - http://www.chisamore.com
|
||||
* @since 2005-05-20
|
||||
* @static
|
||||
* Website: http://log4js.berlios.de
|
||||
*/
|
||||
const debug = require('debug')('log4js:main');
|
||||
const fs = require('fs');
|
||||
const CircularJSON = require('circular-json');
|
||||
const Configuration = require('./configuration');
|
||||
const connectModule = require('./connect-logger');
|
||||
const logger = require('./logger');
|
||||
const configuration = require('./configuration');
|
||||
const layouts = require('./layouts');
|
||||
const levels = require('./levels');
|
||||
const appenders = require('./appenders');
|
||||
const categories = require('./categories');
|
||||
const Logger = require('./logger');
|
||||
const clustering = require('./clustering');
|
||||
const connectLogger = require('./connect-logger');
|
||||
|
||||
|
||||
const defaultConfig = {
|
||||
appenders: {
|
||||
stdout: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout'], level: 'OFF' }
|
||||
}
|
||||
};
|
||||
|
||||
let Logger;
|
||||
let LoggingEvent;
|
||||
let config;
|
||||
let connectLogger;
|
||||
let clustering;
|
||||
let enabled = false;
|
||||
|
||||
function configForCategory(category) {
|
||||
debug(`configForCategory: searching for config for ${category}`);
|
||||
if (config.categories.has(category)) {
|
||||
debug(`configForCategory: ${category} exists in config, returning it`);
|
||||
return config.categories.get(category);
|
||||
}
|
||||
if (category.indexOf('.') > 0) {
|
||||
debug(`configForCategory: ${category} has hierarchy, searching for parents`);
|
||||
return configForCategory(category.substring(0, category.lastIndexOf('.')));
|
||||
}
|
||||
debug('configForCategory: returning config for default category');
|
||||
return configForCategory('default');
|
||||
}
|
||||
|
||||
function appendersForCategory(category) {
|
||||
return configForCategory(category).appenders;
|
||||
}
|
||||
|
||||
function levelForCategory(category) {
|
||||
return configForCategory(category).level;
|
||||
}
|
||||
|
||||
function setLevelForCategory(category, level) {
|
||||
let categoryConfig = config.categories.get(category);
|
||||
debug(`setLevelForCategory: found ${categoryConfig} for ${category}`);
|
||||
if (!categoryConfig) {
|
||||
const sourceCategoryConfig = configForCategory(category);
|
||||
debug('setLevelForCategory: no config found for category, ' +
|
||||
`found ${sourceCategoryConfig} for parents of ${category}`);
|
||||
categoryConfig = { appenders: sourceCategoryConfig.appenders };
|
||||
}
|
||||
categoryConfig.level = level;
|
||||
config.categories.set(category, categoryConfig);
|
||||
}
|
||||
|
||||
|
||||
function sendLogEventToAppender(logEvent) {
|
||||
if (!enabled) return;
|
||||
debug('Received log event ', logEvent);
|
||||
const appenders = appendersForCategory(logEvent.categoryName);
|
||||
appenders.forEach((appender) => {
|
||||
const categoryAppenders = categories.appendersForCategory(logEvent.categoryName);
|
||||
categoryAppenders.forEach((appender) => {
|
||||
appender(logEvent);
|
||||
});
|
||||
}
|
||||
|
||||
function workerDispatch(logEvent) {
|
||||
debug(`sending message to master from worker ${process.pid}`);
|
||||
process.send({ topic: 'log4js:message', data: logEvent.serialise() });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a logger instance.
|
||||
* @static
|
||||
@ -104,9 +49,7 @@ function workerDispatch(logEvent) {
|
||||
* @return {Logger} instance of logger for the category
|
||||
*/
|
||||
function getLogger(category) {
|
||||
const cat = category || 'default';
|
||||
debug(`creating logger as ${isMaster() ? 'master' : 'worker'}`);
|
||||
return new Logger((isMaster() ? sendLogEventToAppender : workerDispatch), cat);
|
||||
return new Logger(category || 'default');
|
||||
}
|
||||
|
||||
function loadConfigurationFile(filename) {
|
||||
@ -124,13 +67,7 @@ function configure(configurationFileOrObject) {
|
||||
configObject = loadConfigurationFile(configurationFileOrObject);
|
||||
}
|
||||
debug(`Configuration is ${configObject}`);
|
||||
config = new Configuration(configObject);
|
||||
clustering = config.clustering;
|
||||
module.exports.levels = config.levels;
|
||||
const loggerModule = logger(config.levels, levelForCategory, setLevelForCategory);
|
||||
Logger = loggerModule.Logger;
|
||||
LoggingEvent = loggerModule.LoggingEvent;
|
||||
module.exports.connectLogger = connectModule(config.levels).connectLogger;
|
||||
configuration.configure(configObject);
|
||||
|
||||
clustering.onMessage(sendLogEventToAppender);
|
||||
|
||||
@ -152,8 +89,8 @@ function shutdown(cb) {
|
||||
enabled = false;
|
||||
|
||||
// Call each of the shutdown functions in parallel
|
||||
const appenders = Array.from(config.appenders.values());
|
||||
const shutdownFunctions = appenders.reduceRight((accum, next) => (next.shutdown ? accum + 1 : accum), 0);
|
||||
const appendersToCheck = Array.from(appenders.values());
|
||||
const shutdownFunctions = appendersToCheck.reduceRight((accum, next) => (next.shutdown ? accum + 1 : accum), 0);
|
||||
let completed = 0;
|
||||
let error;
|
||||
|
||||
@ -173,7 +110,7 @@ function shutdown(cb) {
|
||||
return cb();
|
||||
}
|
||||
|
||||
appenders.filter(a => a.shutdown).forEach(a => a.shutdown(complete));
|
||||
appendersToCheck.filter(a => a.shutdown).forEach(a => a.shutdown(complete));
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -190,9 +127,12 @@ const log4js = {
|
||||
configure,
|
||||
shutdown,
|
||||
connectLogger,
|
||||
levels,
|
||||
addLayout: layouts.addLayout
|
||||
};
|
||||
|
||||
module.exports = log4js;
|
||||
// set ourselves up
|
||||
configure(process.env.LOG4JS_CONFIG || defaultConfig);
|
||||
if (process.env.LOG4JS_CONFIG) {
|
||||
configure(process.env.LOG4JS_CONFIG);
|
||||
}
|
||||
|
||||
173
lib/logger.js
173
lib/logger.js
@ -3,103 +3,100 @@
|
||||
'use strict';
|
||||
|
||||
const debug = require('debug')('log4js:logger');
|
||||
const loggingEventModule = require('./LoggingEvent');
|
||||
const LoggingEvent = require('./LoggingEvent');
|
||||
const levels = require('./levels');
|
||||
const clustering = require('./clustering');
|
||||
const categories = require('./categories');
|
||||
const configuration = require('./configuration');
|
||||
|
||||
module.exports = function (levels, getLevelForCategory, setLevelForCategory) {
|
||||
const LoggingEvent = loggingEventModule(levels);
|
||||
|
||||
/**
|
||||
* Logger to log messages.
|
||||
* use {@see log4js#getLogger(String)} to get an instance.
|
||||
*
|
||||
* @name Logger
|
||||
* @namespace Log4js
|
||||
* @param name name of category to log to
|
||||
* @param level - the loglevel for the category
|
||||
* @param dispatch - the function which will receive the logevents
|
||||
*
|
||||
* @author Stephan Strittmatter
|
||||
*/
|
||||
class Logger {
|
||||
constructor(dispatch, name) {
|
||||
if (typeof dispatch !== 'function') {
|
||||
throw new Error('No dispatch function provided.');
|
||||
}
|
||||
if (!name) {
|
||||
throw new Error('No category provided.');
|
||||
}
|
||||
this.category = name;
|
||||
this.dispatch = dispatch;
|
||||
this.context = {};
|
||||
debug(`Logger created (${this.category}, ${this.level}, ${this.dispatch})`);
|
||||
/**
|
||||
* Logger to log messages.
|
||||
* use {@see log4js#getLogger(String)} to get an instance.
|
||||
*
|
||||
* @name Logger
|
||||
* @namespace Log4js
|
||||
* @param name name of category to log to
|
||||
* @param level - the loglevel for the category
|
||||
* @param dispatch - the function which will receive the logevents
|
||||
*
|
||||
* @author Stephan Strittmatter
|
||||
*/
|
||||
class Logger {
|
||||
constructor(name) {
|
||||
if (!name) {
|
||||
throw new Error('No category provided.');
|
||||
}
|
||||
this.category = name;
|
||||
this.context = {};
|
||||
debug(`Logger created (${this.category}, ${this.level})`);
|
||||
}
|
||||
|
||||
get level() {
|
||||
return levels.getLevel(getLevelForCategory(this.category), levels.TRACE);
|
||||
}
|
||||
get level() {
|
||||
return levels.getLevel(categories.getLevelForCategory(this.category), levels.TRACE);
|
||||
}
|
||||
|
||||
set level(level) {
|
||||
setLevelForCategory(this.category, levels.getLevel(level, this.level));
|
||||
}
|
||||
set level(level) {
|
||||
categories.setLevelForCategory(this.category, levels.getLevel(level, this.level));
|
||||
}
|
||||
|
||||
log() {
|
||||
/* eslint prefer-rest-params:0 */
|
||||
// todo: once node v4 support dropped, use rest parameter instead
|
||||
const args = Array.from(arguments);
|
||||
const logLevel = levels.getLevel(args[0], levels.INFO);
|
||||
if (this.isLevelEnabled(logLevel)) {
|
||||
this._log(logLevel, args.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
isLevelEnabled(otherLevel) {
|
||||
return this.level.isLessThanOrEqualTo(otherLevel);
|
||||
}
|
||||
|
||||
_log(level, data) {
|
||||
debug(`sending log data (${level}) to appenders`);
|
||||
const loggingEvent = new LoggingEvent(this.category, level, data, this.context);
|
||||
this.dispatch(loggingEvent);
|
||||
}
|
||||
|
||||
addContext(key, value) {
|
||||
this.context[key] = value;
|
||||
}
|
||||
|
||||
removeContext(key) {
|
||||
delete this.context[key];
|
||||
}
|
||||
|
||||
clearContext() {
|
||||
this.context = {};
|
||||
log() {
|
||||
/* eslint prefer-rest-params:0 */
|
||||
// todo: once node v4 support dropped, use rest parameter instead
|
||||
const args = Array.from(arguments);
|
||||
const logLevel = levels.getLevel(args[0], levels.INFO);
|
||||
if (this.isLevelEnabled(logLevel)) {
|
||||
this._log(logLevel, args.slice(1));
|
||||
}
|
||||
}
|
||||
|
||||
function addLevelMethods(target) {
|
||||
const level = levels.getLevel(target);
|
||||
|
||||
const levelStrLower = level.toString().toLowerCase();
|
||||
const levelMethod = levelStrLower.replace(/_([a-z])/g, g => g[1].toUpperCase());
|
||||
const isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1);
|
||||
|
||||
Logger.prototype[`is${isLevelMethod}Enabled`] = function () {
|
||||
return this.isLevelEnabled(level);
|
||||
};
|
||||
|
||||
Logger.prototype[levelMethod] = function () {
|
||||
/* eslint prefer-rest-params:0 */
|
||||
// todo: once node v4 support dropped, use rest parameter instead
|
||||
const args = Array.from(arguments);
|
||||
if (this.isLevelEnabled(level)) {
|
||||
this._log(level, args);
|
||||
}
|
||||
};
|
||||
isLevelEnabled(otherLevel) {
|
||||
return this.level.isLessThanOrEqualTo(otherLevel);
|
||||
}
|
||||
|
||||
levels.levels.forEach(addLevelMethods);
|
||||
_log(level, data) {
|
||||
debug(`sending log data (${level}) to appenders`);
|
||||
const loggingEvent = new LoggingEvent(this.category, level, data, this.context);
|
||||
clustering.send(loggingEvent);
|
||||
}
|
||||
|
||||
return {
|
||||
LoggingEvent: LoggingEvent,
|
||||
Logger: Logger
|
||||
addContext(key, value) {
|
||||
this.context[key] = value;
|
||||
}
|
||||
|
||||
removeContext(key) {
|
||||
delete this.context[key];
|
||||
}
|
||||
|
||||
clearContext() {
|
||||
this.context = {};
|
||||
}
|
||||
}
|
||||
|
||||
function addLevelMethods(target) {
|
||||
const level = levels.getLevel(target);
|
||||
|
||||
const levelStrLower = level.toString().toLowerCase();
|
||||
const levelMethod = levelStrLower.replace(/_([a-z])/g, g => g[1].toUpperCase());
|
||||
const isLevelMethod = levelMethod[0].toUpperCase() + levelMethod.slice(1);
|
||||
|
||||
Logger.prototype[`is${isLevelMethod}Enabled`] = function () {
|
||||
return this.isLevelEnabled(level);
|
||||
};
|
||||
};
|
||||
|
||||
Logger.prototype[levelMethod] = function () {
|
||||
/* eslint prefer-rest-params:0 */
|
||||
// todo: once node v4 support dropped, use rest parameter instead
|
||||
const args = Array.from(arguments);
|
||||
if (this.isLevelEnabled(level)) {
|
||||
this._log(level, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
levels.levels.forEach(addLevelMethods);
|
||||
|
||||
configuration.addListener(() => {
|
||||
levels.levels.forEach(addLevelMethods);
|
||||
});
|
||||
|
||||
module.exports = Logger;
|
||||
|
||||
@ -1,328 +1,333 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('tap').test;
|
||||
const Configuration = require('../../lib/configuration');
|
||||
const util = require('util');
|
||||
const path = require('path');
|
||||
// const util = require('util');
|
||||
// const path = require('path');
|
||||
const sandbox = require('sandboxed-module');
|
||||
// const log4js = require('../../lib/log4js');
|
||||
// const appenders = require('../../lib/appenders');
|
||||
// const configuration = require('../../lib/configuration');
|
||||
const debug = require('debug')('log4js:test.configuration-validation');
|
||||
|
||||
function testAppender(label) {
|
||||
return {
|
||||
configure: function (config, layouts, findAppender) {
|
||||
return {
|
||||
configureCalled: true,
|
||||
type: config.type,
|
||||
label: label,
|
||||
config: config,
|
||||
layouts: layouts,
|
||||
findAppender: findAppender
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
const testAppender = (label, result) => ({
|
||||
configure: function (config, layouts, findAppender) {
|
||||
debug(`testAppender(${label}).configure called`);
|
||||
result.configureCalled = true;
|
||||
result.type = config.type;
|
||||
result.label = label;
|
||||
result.config = config;
|
||||
result.layouts = layouts;
|
||||
result.findAppender = findAppender;
|
||||
return { };
|
||||
}
|
||||
});
|
||||
|
||||
test('log4js configuration validation', (batch) => {
|
||||
batch.test('should give error if config is just plain silly', (t) => {
|
||||
[null, undefined, '', ' ', []].forEach((config) => {
|
||||
const expectedError =
|
||||
new Error(`Problem with log4js configuration: (${util.inspect(config)}) - must be an object.`);
|
||||
t.throws(
|
||||
() => new Configuration(config),
|
||||
expectedError
|
||||
);
|
||||
});
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if config is an empty object', (t) => {
|
||||
const expectedError =
|
||||
new Error('Problem with log4js configuration: ({}) - must have a property "appenders" of type object.');
|
||||
t.throws(() => new Configuration({}), expectedError);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if config has no appenders', (t) => {
|
||||
const expectedError =
|
||||
new Error('Problem with log4js configuration: ({ categories: {} }) ' +
|
||||
'- must have a property "appenders" of type object.');
|
||||
t.throws(() => new Configuration({ categories: {} }), expectedError);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if config has no categories', (t) => {
|
||||
const expectedError =
|
||||
new Error('Problem with log4js configuration: ({ appenders: {} }) ' +
|
||||
'- must have a property "categories" of type object.');
|
||||
t.throws(() => new Configuration({ appenders: {} }), expectedError);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if appenders is not an object', (t) => {
|
||||
const error =
|
||||
new Error('Problem with log4js configuration: ({ appenders: [], categories: [] })' +
|
||||
' - must have a property "appenders" of type object.');
|
||||
t.throws(
|
||||
() => new Configuration({ appenders: [], categories: [] }),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if appenders are not all valid', (t) => {
|
||||
const error =
|
||||
new Error('Problem with log4js configuration: ({ appenders: { thing: \'cheese\' }, categories: {} })' +
|
||||
' - appender "thing" is not valid (must be an object with property "type")');
|
||||
t.throws(
|
||||
() => new Configuration({ appenders: { thing: 'cheese' }, categories: {} }),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should require at least one appender', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ({ appenders: {}, categories: {} })' +
|
||||
' - must define at least one appender.');
|
||||
t.throws(
|
||||
() => new Configuration({ appenders: {}, categories: {} }),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if categories are not all valid', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n categories: { thing: \'cheese\' } })' +
|
||||
' - category "thing" is not valid (must be an object with properties "appenders" and "level")');
|
||||
t.throws(
|
||||
() => new Configuration({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: 'cheese' } }),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if default category not defined', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
' categories: { thing: { appenders: [ \'stdout\' ], level: \'ERROR\' } } })' +
|
||||
' - must define a "default" category.');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { stdout: { type: 'stdout' } },
|
||||
categories: { thing: { appenders: ['stdout'], level: 'ERROR' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should require at least one category', (t) => {
|
||||
const error =
|
||||
new Error('Problem with log4js configuration: ({ appenders: { stdout: { type: \'stdout\' } }, categories: {} })' +
|
||||
' - must define at least one category.');
|
||||
t.throws(
|
||||
() => new Configuration({ appenders: { stdout: { type: 'stdout' } }, categories: {} }),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if category.appenders is not an array', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
' categories: { thing: { appenders: {}, level: \'ERROR\' } } })' +
|
||||
' - category "thing" is not valid (appenders must be an array of appender names)');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { stdout: { type: 'stdout' } },
|
||||
categories: { thing: { appenders: {}, level: 'ERROR' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if category.appenders is empty', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
' categories: { thing: { appenders: [], level: \'ERROR\' } } })' +
|
||||
' - category "thing" is not valid (appenders must contain at least one appender name)');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { stdout: { type: 'stdout' } },
|
||||
categories: { thing: { appenders: [], level: 'ERROR' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if categories do not refer to valid appenders', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
' categories: { thing: { appenders: [ \'cheese\' ], level: \'ERROR\' } } })' +
|
||||
' - category "thing" is not valid (appender "cheese" is not defined)');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { stdout: { type: 'stdout' } },
|
||||
categories: { thing: { appenders: ['cheese'], level: 'ERROR' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if category level is not valid', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
' categories: { default: { appenders: [ \'stdout\' ], level: \'Biscuits\' } } })' +
|
||||
' - category "default" is not valid (level "Biscuits" not recognised; ' +
|
||||
'valid levels are ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, MARK, OFF)');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { stdout: { type: 'stdout' } },
|
||||
categories: { default: { appenders: ['stdout'], level: 'Biscuits' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should give error if appender type cannot be found', (t) => {
|
||||
const error = new Error('Problem with log4js configuration: ' +
|
||||
'({ appenders: { thing: { type: \'cheese\' } },\n' +
|
||||
' categories: { default: { appenders: [ \'thing\' ], level: \'ERROR\' } } })' +
|
||||
' - appender "thing" is not valid (type "cheese" could not be found)');
|
||||
t.throws(
|
||||
() => new Configuration({
|
||||
appenders: { thing: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
}),
|
||||
error
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
// batch.test('should give error if config is just plain silly', (t) => {
|
||||
// [null, undefined, '', ' ', []].forEach((config) => {
|
||||
// const expectedError =
|
||||
// new Error(`Problem with log4js configuration: (${util.inspect(config)}) - must be an object.`);
|
||||
// t.throws(
|
||||
// () => configuration.configure(config),
|
||||
// expectedError
|
||||
// );
|
||||
// });
|
||||
//
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if config is an empty object', (t) => {
|
||||
// const expectedError =
|
||||
// new Error('Problem with log4js configuration: ({}) - must have a property "appenders" of type object.');
|
||||
// t.throws(() => log4js.configure({}), expectedError);
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if config has no appenders', (t) => {
|
||||
// const expectedError =
|
||||
// new Error('Problem with log4js configuration: ({ categories: {} }) ' +
|
||||
// '- must have a property "appenders" of type object.');
|
||||
// t.throws(() => log4js.configure({ categories: {} }), expectedError);
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if config has no categories', (t) => {
|
||||
// const expectedError =
|
||||
// new Error('Problem with log4js configuration: ({ appenders: { out: { type: \'stdout\' } } }) ' +
|
||||
// '- must have a property "categories" of type object.');
|
||||
// t.throws(() => log4js.configure({ appenders: { out: { type: 'stdout' } } }), expectedError);
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if appenders is not an object', (t) => {
|
||||
// const error =
|
||||
// new Error('Problem with log4js configuration: ({ appenders: [], categories: [] })' +
|
||||
// ' - must have a property "appenders" of type object.');
|
||||
// t.throws(
|
||||
// () => log4js.configure({ appenders: [], categories: [] }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if appenders are not all valid', (t) => {
|
||||
// const error =
|
||||
// new Error('Problem with log4js configuration: ({ appenders: { thing: \'cheese\' }, categories: {} })' +
|
||||
// ' - appender "thing" is not valid (must be an object with property "type")');
|
||||
// t.throws(
|
||||
// () => log4js.configure({ appenders: { thing: 'cheese' }, categories: {} }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should require at least one appender', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ({ appenders: {}, categories: {} })' +
|
||||
// ' - must define at least one appender.');
|
||||
// t.throws(
|
||||
// () => log4js.configure({ appenders: {}, categories: {} }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if categories are not all valid', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n categories: { thing: \'cheese\' } })' +
|
||||
// ' - category "thing" is not valid (must be an object with properties "appenders" and "level")');
|
||||
// t.throws(
|
||||
// () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: { thing: 'cheese' } }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if default category not defined', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
// ' categories: { thing: { appenders: [ \'stdout\' ], level: \'ERROR\' } } })' +
|
||||
// ' - must define a "default" category.');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { stdout: { type: 'stdout' } },
|
||||
// categories: { thing: { appenders: ['stdout'], level: 'ERROR' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should require at least one category', (t) => {
|
||||
// const error =
|
||||
// new Error('Problem with log4js configuration: ({ appenders: { stdout: { type: \'stdout\' } }, categories: {} })' +
|
||||
// ' - must define at least one category.');
|
||||
// t.throws(
|
||||
// () => log4js.configure({ appenders: { stdout: { type: 'stdout' } }, categories: {} }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if category.appenders is not an array', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
// ' categories: { thing: { appenders: {}, level: \'ERROR\' } } })' +
|
||||
// ' - category "thing" is not valid (appenders must be an array of appender names)');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { stdout: { type: 'stdout' } },
|
||||
// categories: { thing: { appenders: {}, level: 'ERROR' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if category.appenders is empty', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
// ' categories: { thing: { appenders: [], level: \'ERROR\' } } })' +
|
||||
// ' - category "thing" is not valid (appenders must contain at least one appender name)');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { stdout: { type: 'stdout' } },
|
||||
// categories: { thing: { appenders: [], level: 'ERROR' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if categories do not refer to valid appenders', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
// ' categories: { thing: { appenders: [ \'cheese\' ], level: \'ERROR\' } } })' +
|
||||
// ' - category "thing" is not valid (appender "cheese" is not defined)');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { stdout: { type: 'stdout' } },
|
||||
// categories: { thing: { appenders: ['cheese'], level: 'ERROR' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if category level is not valid', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { stdout: { type: \'stdout\' } },\n' +
|
||||
// ' categories: { default: { appenders: [ \'stdout\' ], level: \'Biscuits\' } } })' +
|
||||
// ' - category "default" is not valid (level "Biscuits" not recognised; ' +
|
||||
// 'valid levels are ALL, TRACE, DEBUG, INFO, WARN, ERROR, FATAL, MARK, OFF)');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { stdout: { type: 'stdout' } },
|
||||
// categories: { default: { appenders: ['stdout'], level: 'Biscuits' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should give error if appender type cannot be found', (t) => {
|
||||
// const error = new Error('Problem with log4js configuration: ' +
|
||||
// '({ appenders: { thing: { type: \'cheese\' } },\n' +
|
||||
// ' categories: { default: { appenders: [ \'thing\' ], level: \'ERROR\' } } })' +
|
||||
// ' - appender "thing" is not valid (type "cheese" could not be found)');
|
||||
// t.throws(
|
||||
// () => log4js.configure({
|
||||
// appenders: { thing: { type: 'cheese' } },
|
||||
// categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
// }),
|
||||
// error
|
||||
// );
|
||||
// t.end();
|
||||
// });
|
||||
|
||||
batch.test('should create appender instances', (t) => {
|
||||
const SandboxedConfiguration = sandbox.require(
|
||||
'../../lib/configuration',
|
||||
const thing = {};
|
||||
const sandboxedLog4js = sandbox.require(
|
||||
'../../lib/log4js',
|
||||
{
|
||||
singleOnly: true,
|
||||
requires: {
|
||||
cheese: testAppender('cheesy')
|
||||
cheese: testAppender('cheesy', thing)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const config = new SandboxedConfiguration({
|
||||
sandboxedLog4js.configure({
|
||||
appenders: { thing: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
});
|
||||
|
||||
const thing = config.appenders.get('thing');
|
||||
t.ok(thing.configureCalled);
|
||||
t.equal(thing.type, 'cheese');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should load appenders from core first', (t) => {
|
||||
const SandboxedConfiguration = sandbox.require(
|
||||
'../../lib/configuration',
|
||||
{
|
||||
singleOnly: true,
|
||||
requires: {
|
||||
'./appenders/cheese': testAppender('correct'),
|
||||
cheese: testAppender('wrong')
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const config = new SandboxedConfiguration({
|
||||
appenders: { thing: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
});
|
||||
|
||||
const thing = config.appenders.get('thing');
|
||||
t.ok(thing.configureCalled);
|
||||
t.equal(thing.type, 'cheese');
|
||||
t.equal(thing.label, 'correct');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should load appenders relative to main file if not in core, or node_modules', (t) => {
|
||||
const mainPath = path.dirname(require.main.filename);
|
||||
const sandboxConfig = { singleOnly: true, requires: {} };
|
||||
sandboxConfig.requires[`${mainPath}/cheese`] = testAppender('correct');
|
||||
// add this one, because when we're running coverage the main path is a bit different
|
||||
sandboxConfig.requires[
|
||||
`${path.join(mainPath, '../../node_modules/tap/node_modules/nyc/bin/cheese')}`
|
||||
] = testAppender('correct');
|
||||
const SandboxedConfiguration = sandbox.require('../../lib/configuration', sandboxConfig);
|
||||
|
||||
const config = new SandboxedConfiguration({
|
||||
appenders: { thing: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
});
|
||||
|
||||
const thing = config.appenders.get('thing');
|
||||
t.ok(thing.configureCalled);
|
||||
t.equal(thing.type, 'cheese');
|
||||
t.equal(thing.label, 'correct');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should load appenders relative to process.cwd if not found in core, node_modules', (t) => {
|
||||
const SandboxedConfiguration = sandbox.require(
|
||||
'../../lib/configuration',
|
||||
{
|
||||
singleOnly: true,
|
||||
requires: {
|
||||
'/var/lib/cheese/cheese': testAppender('correct'),
|
||||
},
|
||||
globals: {
|
||||
process: { cwd: () => '/var/lib/cheese', env: {} }
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const config = new SandboxedConfiguration({
|
||||
appenders: { thing: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
});
|
||||
|
||||
const thing = config.appenders.get('thing');
|
||||
t.ok(thing.configureCalled);
|
||||
t.equal(thing.type, 'cheese');
|
||||
t.equal(thing.label, 'correct');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should pass config, layout, findAppender to appenders', (t) => {
|
||||
const SandboxedConfiguration = sandbox.require(
|
||||
'../../lib/configuration',
|
||||
{
|
||||
singleOnly: true,
|
||||
requires: {
|
||||
cheese: testAppender('cheesy')
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const config = new SandboxedConfiguration({
|
||||
appenders: { thing: { type: 'cheese', foo: 'bar' }, thing2: { type: 'cheese' } },
|
||||
categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
});
|
||||
|
||||
const thing = config.appenders.get('thing');
|
||||
t.ok(thing.configureCalled);
|
||||
t.equal(thing.type, 'cheese');
|
||||
t.equal(thing.config.foo, 'bar');
|
||||
t.type(thing.layouts, 'object');
|
||||
t.type(thing.layouts.basicLayout, 'function');
|
||||
t.type(thing.findAppender, 'function');
|
||||
t.type(thing.findAppender('thing2'), 'object');
|
||||
t.end();
|
||||
});
|
||||
// batch.test('should load appenders from core first', (t) => {
|
||||
// const sandboxedLog4js = sandbox.require(
|
||||
// '../../lib/log4js',
|
||||
// {
|
||||
// singleOnly: true,
|
||||
// requires: {
|
||||
// './cheese': testAppender('correct'),
|
||||
// cheese: testAppender('wrong')
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// sandboxedLog4js.configure({
|
||||
// appenders: { thing: { type: 'cheese' } },
|
||||
// categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
// });
|
||||
//
|
||||
// const thing = appenders.get('thing');
|
||||
// t.ok(thing.configureCalled);
|
||||
// t.equal(thing.type, 'cheese');
|
||||
// t.equal(thing.label, 'correct');
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should load appenders relative to main file if not in core, or node_modules', (t) => {
|
||||
// const mainPath = path.dirname(require.main.filename);
|
||||
// const sandboxConfig = {
|
||||
// singleOnly: true,
|
||||
// requires: {}
|
||||
// };
|
||||
// sandboxConfig.requires[`${mainPath}/cheese`] = testAppender('correct');
|
||||
// // add this one, because when we're running coverage the main path is a bit different
|
||||
// sandboxConfig.requires[
|
||||
// `${path.join(mainPath, '../../node_modules/tap/node_modules/nyc/bin/cheese')}`
|
||||
// ] = testAppender('correct');
|
||||
// const sandboxedLog4js = sandbox.require('../../lib/log4js', sandboxConfig);
|
||||
//
|
||||
// sandboxedLog4js.configure({
|
||||
// appenders: { thing: { type: 'cheese' } },
|
||||
// categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
// });
|
||||
//
|
||||
// const thing = appenders.get('thing');
|
||||
// t.ok(thing.configureCalled);
|
||||
// t.equal(thing.type, 'cheese');
|
||||
// t.equal(thing.label, 'correct');
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should load appenders relative to process.cwd if not found in core, node_modules', (t) => {
|
||||
// const sandboxedLog4js = sandbox.require(
|
||||
// '../../lib/log4js',
|
||||
// {
|
||||
// singleOnly: true,
|
||||
// requires: {
|
||||
// '/var/lib/cheese/cheese': testAppender('correct'),
|
||||
// },
|
||||
// globals: {
|
||||
// process: { cwd: () => '/var/lib/cheese', env: {} }
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// sandboxedLog4js.configure({
|
||||
// appenders: { thing: { type: 'cheese' } },
|
||||
// categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
// });
|
||||
//
|
||||
// const thing = appenders.get('thing');
|
||||
// t.ok(thing.configureCalled);
|
||||
// t.equal(thing.type, 'cheese');
|
||||
// t.equal(thing.label, 'correct');
|
||||
// t.end();
|
||||
// });
|
||||
//
|
||||
// batch.test('should pass config, layout, findAppender to appenders', (t) => {
|
||||
// const sandboxedLog4js = sandbox.require(
|
||||
// '../../lib/log4js',
|
||||
// {
|
||||
// singleOnly: true,
|
||||
// requires: {
|
||||
// './appenders': appenders,
|
||||
// cheese: testAppender('cheesy')
|
||||
// }
|
||||
// }
|
||||
// );
|
||||
//
|
||||
// sandboxedLog4js.configure({
|
||||
// appenders: { thing: { type: 'cheese', foo: 'bar' }, thing2: { type: 'cheese' } },
|
||||
// categories: { default: { appenders: ['thing'], level: 'ERROR' } }
|
||||
// });
|
||||
//
|
||||
// const thing = appenders.get('thing');
|
||||
// t.ok(thing.configureCalled);
|
||||
// t.equal(thing.type, 'cheese');
|
||||
// t.equal(thing.config.foo, 'bar');
|
||||
// t.type(thing.layouts, 'object');
|
||||
// t.type(thing.layouts.basicLayout, 'function');
|
||||
// t.type(thing.findAppender, 'function');
|
||||
// t.type(thing.findAppender('thing2'), 'object');
|
||||
// t.end();
|
||||
// });
|
||||
|
||||
batch.end();
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
const test = require('tap').test;
|
||||
const EE = require('events').EventEmitter;
|
||||
const levels = require('../../lib/levels')();
|
||||
const levels = require('../../lib/levels');
|
||||
|
||||
class MockLogger {
|
||||
constructor() {
|
||||
@ -58,7 +58,7 @@ function request(cl, method, url, code, reqHeaders, resHeaders) {
|
||||
}
|
||||
|
||||
test('log4js connect logger', (batch) => {
|
||||
const clm = require('../../lib/connect-logger')(levels);
|
||||
const clm = require('../../lib/connect-logger');
|
||||
batch.test('getConnectLoggerModule', (t) => {
|
||||
t.type(clm, 'object', 'should return a connect logger factory');
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
const test = require('tap').test;
|
||||
const EE = require('events').EventEmitter;
|
||||
const levels = require('../../lib/levels')();
|
||||
const levels = require('../../lib/levels');
|
||||
|
||||
class MockLogger {
|
||||
constructor() {
|
||||
@ -41,7 +41,7 @@ class MockResponse extends EE {
|
||||
}
|
||||
|
||||
test('log4js connect logger', (batch) => {
|
||||
const clm = require('../../lib/connect-logger')(levels);
|
||||
const clm = require('../../lib/connect-logger');
|
||||
|
||||
batch.test('with nolog config', (t) => {
|
||||
const ml = new MockLogger();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('tap').test;
|
||||
const levels = require('../../lib/levels')();
|
||||
const levels = require('../../lib/levels');
|
||||
|
||||
function assertThat(assert, level) {
|
||||
function assertForEach(assertion, testFn, otherLevels) {
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('tap').test;
|
||||
const levels = require('../../lib/levels')();
|
||||
const levels = require('../../lib/levels');
|
||||
const Logger = require('../../lib/logger');
|
||||
|
||||
const testConfig = {
|
||||
level: levels.TRACE
|
||||
};
|
||||
|
||||
const loggerModule = require('../../lib/logger')(
|
||||
levels,
|
||||
() => testConfig.level,
|
||||
(category, level) => { testConfig.level = level; }
|
||||
);
|
||||
|
||||
const Logger = loggerModule.Logger;
|
||||
const testDispatcher = {
|
||||
events: [],
|
||||
dispatch: function (evt) {
|
||||
this.events.push(evt);
|
||||
}
|
||||
};
|
||||
const dispatch = testDispatcher.dispatch.bind(testDispatcher);
|
||||
|
||||
test('../../lib/logger', (batch) => {
|
||||
batch.beforeEach((done) => {
|
||||
@ -32,28 +25,20 @@ test('../../lib/logger', (batch) => {
|
||||
batch.test('constructor with no parameters', (t) => {
|
||||
t.throws(
|
||||
() => new Logger(),
|
||||
new Error('No dispatch function provided.')
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('constructor with only dispatch', (t) => {
|
||||
t.throws(
|
||||
() => new Logger(dispatch),
|
||||
new Error('No category provided.')
|
||||
);
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('constructor with category', (t) => {
|
||||
const logger = new Logger(dispatch, 'cheese');
|
||||
const logger = new Logger('cheese');
|
||||
t.equal(logger.category, 'cheese', 'should use category');
|
||||
t.equal(logger.level, levels.TRACE, 'should use TRACE log level');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('set level should delegate', (t) => {
|
||||
const logger = new Logger(dispatch, 'cheese');
|
||||
const logger = new Logger('cheese');
|
||||
logger.level = 'debug';
|
||||
t.equal(logger.category, 'cheese', 'should use category');
|
||||
t.equal(logger.level, levels.DEBUG, 'should use level');
|
||||
@ -61,7 +46,7 @@ test('../../lib/logger', (batch) => {
|
||||
});
|
||||
|
||||
batch.test('isLevelEnabled', (t) => {
|
||||
const logger = new Logger(dispatch, 'cheese');
|
||||
const logger = new Logger('cheese');
|
||||
const functions = [
|
||||
'isTraceEnabled', 'isDebugEnabled', 'isInfoEnabled',
|
||||
'isWarnEnabled', 'isErrorEnabled', 'isFatalEnabled'
|
||||
@ -83,7 +68,7 @@ test('../../lib/logger', (batch) => {
|
||||
});
|
||||
|
||||
batch.test('should send log events to dispatch function', (t) => {
|
||||
const logger = new Logger(dispatch, 'cheese');
|
||||
const logger = new Logger('cheese');
|
||||
logger.debug('Event 1');
|
||||
logger.debug('Event 2');
|
||||
logger.debug('Event 3');
|
||||
@ -97,7 +82,7 @@ test('../../lib/logger', (batch) => {
|
||||
});
|
||||
|
||||
batch.test('should add context values to every event', (t) => {
|
||||
const logger = new Logger(dispatch, 'fromage');
|
||||
const logger = new Logger('fromage');
|
||||
logger.debug('Event 1');
|
||||
logger.addContext('cheese', 'edam');
|
||||
logger.debug('Event 2');
|
||||
@ -121,7 +106,7 @@ test('../../lib/logger', (batch) => {
|
||||
});
|
||||
|
||||
batch.test('should not break when log data has no toString', (t) => {
|
||||
const logger = new Logger(dispatch, 'thing');
|
||||
const logger = new Logger('thing');
|
||||
logger.info('Just testing ', Object.create(null));
|
||||
|
||||
const events = testDispatcher.events;
|
||||
|
||||
@ -2,8 +2,8 @@ const test = require('tap').test;
|
||||
const net = require('net');
|
||||
const log4js = require('../../lib/log4js');
|
||||
const vcr = require('../../lib/appenders/recording');
|
||||
const levels = require('../../lib/levels')();
|
||||
const LoggingEvent = (require('../../lib/logger')(levels)).LoggingEvent;
|
||||
const levels = require('../../lib/levels');
|
||||
const LoggingEvent = require('../../lib/LoggingEvent');
|
||||
|
||||
log4js.configure({
|
||||
appenders: {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user