mirror of
https://github.com/log4js-node/log4js-node.git
synced 2025-12-08 19:26:01 +00:00
feat: add appender and level inheritance
This commit is contained in:
parent
945247028a
commit
805634f4d2
@ -5,6 +5,58 @@ const appenders = require('./appenders');
|
||||
|
||||
const categories = new Map();
|
||||
|
||||
/**
|
||||
* Add inherited config to this category. That includes extra appenders from parent,
|
||||
* and level, if none is set on this category.
|
||||
* This is recursive, so each parent also gets loaded with inherited appenders.
|
||||
* Inheritance is blocked if a category has inherit=false
|
||||
* @param {any} config
|
||||
* @param {any} category the child category
|
||||
* @param {string} categoryName dotted path to category
|
||||
* @return {void}
|
||||
*/
|
||||
function inheritFromParent(config, category, categoryName) {
|
||||
if (category.inherit === false) return;
|
||||
const lastDotIndex = categoryName.lastIndexOf('.');
|
||||
if (lastDotIndex < 0) return; // category is not a child
|
||||
const parentCategoryName = categoryName.substring(0, lastDotIndex);
|
||||
let parentCategory = config.categories[parentCategoryName];
|
||||
if (!parentCategory) {
|
||||
parentCategory = { inherit: true };
|
||||
config.categories[parentCategoryName] = parentCategory;
|
||||
}
|
||||
// make sure parent has had its inheritance taken care of before pulling its properties to this child
|
||||
inheritFromParent(config, parentCategory, parentCategoryName);
|
||||
|
||||
category.appenders = category.appenders || [];
|
||||
category.level = category.level || parentCategory.level;
|
||||
|
||||
// merge in appenders from parent (parent is already holding its inherited appenders)
|
||||
parentCategory.appenders.forEach((ap) => {
|
||||
if (!category.appenders.includes(ap)) {
|
||||
category.appenders.push(ap);
|
||||
}
|
||||
});
|
||||
category.parent = parentCategory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walk all categories in the config, and pull down any configuration from parent to child.
|
||||
* This includes inherited appenders, and level, where level is not set.
|
||||
* Inheritance is skipped where a category has inherit=false.
|
||||
* @param {any} config
|
||||
*/
|
||||
function addInheritedConfig(config) {
|
||||
const categoryNames = Object.keys(config.categories);
|
||||
categoryNames.forEach((name) => {
|
||||
const category = config.categories[name];
|
||||
// add inherited appenders and level to this category
|
||||
inheritFromParent(config, category, name);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
configuration.addListener((config) => {
|
||||
configuration.throwExceptionIf(
|
||||
config,
|
||||
@ -109,7 +161,7 @@ const setLevelForCategory = (category, level) => {
|
||||
if (!categoryConfig) {
|
||||
const sourceCategoryConfig = configForCategory(category);
|
||||
debug('setLevelForCategory: no config found for category, '
|
||||
+ `found ${sourceCategoryConfig} for parents of ${category}`);
|
||||
+ `found ${sourceCategoryConfig} for parents of ${category}`);
|
||||
categoryConfig = { appenders: sourceCategoryConfig.appenders };
|
||||
}
|
||||
categoryConfig.level = level;
|
||||
@ -117,6 +169,7 @@ const setLevelForCategory = (category, level) => {
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
addInheritedConfig,
|
||||
appendersForCategory,
|
||||
getLevelForCategory,
|
||||
setLevelForCategory
|
||||
|
||||
@ -59,6 +59,8 @@ function configure(configurationFileOrObject) {
|
||||
}
|
||||
debug(`Configuration is ${configObject}`);
|
||||
|
||||
categories.addInheritedConfig(configObject); // copy config from parent to child categories
|
||||
|
||||
configuration.configure(deepClone(configObject));
|
||||
|
||||
clustering.onMessage(sendLogEventToAppender);
|
||||
|
||||
244
test/tap/configuration-inheritance-test.js
Normal file
244
test/tap/configuration-inheritance-test.js
Normal file
@ -0,0 +1,244 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('tap').test;
|
||||
// const util = require('util');
|
||||
// const debug = require('debug')('log4js:test.configuration-inheritance');
|
||||
const log4js = require('../../lib/log4js');
|
||||
// const configuration = require('../../lib/configuration');
|
||||
|
||||
|
||||
test('log4js category inherit all appenders from direct parent', (batch) => {
|
||||
batch.test('should inherit appenders from direct parent', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
|
||||
'catA.catB': { level: 'DEBUG' }
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 2, 'inherited 2 appenders');
|
||||
t.ok(child.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
t.ok(child.appenders.includes('stdout2'), 'inherited stdout2');
|
||||
t.isEqual(child.level, 'DEBUG', 'child level overrides parent');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('multiple children should inherit config from shared parent', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
'catA.catB.cat1': { level: 'DEBUG' }, // should get sdtout1, DEBUG
|
||||
'catA.catB.cat2': { appenders: ['stdout2'] } // should get sdtout1,sdtout2, INFO
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child1 = config.categories['catA.catB.cat1'];
|
||||
t.ok(child1);
|
||||
t.ok(child1.appenders);
|
||||
t.isEqual(child1.appenders.length, 1, 'inherited 1 appender');
|
||||
t.ok(child1.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
t.isEqual(child1.level, 'DEBUG', 'child level overrides parent');
|
||||
|
||||
const child2 = config.categories['catA.catB.cat2'];
|
||||
t.ok(child2);
|
||||
t.ok(child2.appenders);
|
||||
t.isEqual(child2.appenders.length, 2, 'inherited 1 appenders, plus its original');
|
||||
t.ok(child2.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
t.ok(child2.appenders.includes('stdout2'), 'kept stdout2');
|
||||
t.isEqual(child2.level, 'INFO', 'inherited parent level');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should inherit appenders from multiple parents', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
'catA.catB': { appenders: ['stdout2'], level: 'INFO' }, // should get stdout1 and stdout2
|
||||
'catA.catB.catC': { level: 'DEBUG' } // should get stdout1 and stdout2
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB.catC'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 2, 'inherited 2 appenders');
|
||||
t.ok(child.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
t.ok(child.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
|
||||
const firstParent = config.categories['catA.catB'];
|
||||
t.ok(firstParent);
|
||||
t.ok(firstParent.appenders);
|
||||
t.isEqual(firstParent.appenders.length, 2, 'ended up with 2 appenders');
|
||||
t.ok(firstParent.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
t.ok(firstParent.appenders.includes('stdout2'), 'kept stdout2');
|
||||
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should inherit appenders from deep parent with missing direct parent', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
// no catA.catB, but should get created, with stdout1
|
||||
'catA.catB.catC': { level: 'DEBUG' } // should get stdout1
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB.catC'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 1, 'inherited 1 appenders');
|
||||
t.ok(child.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
|
||||
const firstParent = config.categories['catA.catB'];
|
||||
t.ok(firstParent);
|
||||
t.ok(firstParent.appenders, 'catA.catB got created implicitily');
|
||||
t.isEqual(firstParent.appenders.length, 1, 'created with 1 inherited appender');
|
||||
t.ok(firstParent.appenders.includes('stdout1'), 'inherited stdout1');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
batch.test('should not get duplicate appenders if parent has the same one', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
|
||||
'catA.catB': { appenders: ['stdout1'], level: 'DEBUG' }
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 2, 'inherited 1 appender');
|
||||
t.ok(child.appenders.includes('stdout1'), 'still have stdout1');
|
||||
t.ok(child.appenders.includes('stdout2'), 'inherited stdout2');
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('inherit:falses should disable inheritance', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
'catA.catB': { appenders: ['stdout2'], level: 'INFO', inherit: false }, // should not inherit from catA
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 1, 'inherited no appender');
|
||||
t.ok(child.appenders.includes('stdout2'), 'kept stdout2');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
batch.test('inheritance should stop if direct parent has inherit off', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
'catA.catB': { appenders: ['stdout2'], level: 'INFO', inherit: false }, // should not inherit from catA
|
||||
'catA.catB.catC': { level: 'DEBUG' } // should inherit from catB only
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB.catC'];
|
||||
t.ok(child);
|
||||
t.ok(child.appenders);
|
||||
t.isEqual(child.appenders.length, 1, 'inherited 1 appender');
|
||||
t.ok(child.appenders.includes('stdout2'), 'inherited stdout2');
|
||||
|
||||
const firstParent = config.categories['catA.catB'];
|
||||
t.ok(firstParent);
|
||||
t.ok(firstParent.appenders);
|
||||
t.isEqual(firstParent.appenders.length, 1, 'did not inherit new appenders');
|
||||
t.ok(firstParent.appenders.includes('stdout2'), 'kept stdout2');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.test('should inherit level when it is missing', (t) => {
|
||||
const config = {
|
||||
appenders: {
|
||||
stdout1: { type: 'stdout' },
|
||||
stdout2: { type: 'stdout' }
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['stdout1'], level: 'ERROR' },
|
||||
catA: { appenders: ['stdout1'], level: 'INFO' },
|
||||
// no catA.catB, but should get created, with stdout1, level INFO
|
||||
'catA.catB.catC': {} // should get stdout1, level INFO
|
||||
}
|
||||
};
|
||||
|
||||
log4js.configure(config);
|
||||
|
||||
const child = config.categories['catA.catB.catC'];
|
||||
t.ok(child);
|
||||
t.isEqual(child.level, 'INFO', 'inherited level');
|
||||
|
||||
const firstParent = config.categories['catA.catB'];
|
||||
t.ok(firstParent);
|
||||
t.isEqual(firstParent.level, 'INFO', 'generate parent inherited level from base');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
batch.end();
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user