diff --git a/lib/categories.js b/lib/categories.js index f983bf6..e2e98ee 100644 --- a/lib/categories.js +++ b/lib/categories.js @@ -21,23 +21,39 @@ function inheritFromParent(config, category, categoryName) { 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); + // parent is missing, may need to implicitly create it, so that it can inherit from its parents + if (parentCategoryName.lastIndexOf('.') >= 0) { // don't create a missing root category, it can inherit nothing + parentCategory = { inherit: true, appenders: [] }; } - }); - category.parent = parentCategory; + } + + if (parentCategory) { + // make sure parent has had its inheritance taken care of before pulling its properties to this child + inheritFromParent(config, parentCategory, parentCategoryName); + + // if the parent is not in the config (because we just created it above), + // and it inherited a valid configuration, add it to config.categories + if (!config.categories[parentCategoryName] + && parentCategory.appenders + && parentCategory.appenders.length + && parentCategory.level) { + config.categories[parentCategoryName] = parentCategory; + } + + 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; + } } @@ -106,7 +122,7 @@ configuration.addListener((config) => { config, configuration.not(levels.getLevel(category.level)), `category "${name}" is not valid (level "${category.level}" not recognised;` - + ` valid levels are ${levels.levels.join(', ')})` + + ` valid levels are ${levels.levels.join(', ')})` ); }); @@ -161,7 +177,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; diff --git a/test/tap/configuration-inheritance-test.js b/test/tap/configuration-inheritance-test.js index 6503533..aaf80c2 100644 --- a/test/tap/configuration-inheritance-test.js +++ b/test/tap/configuration-inheritance-test.js @@ -132,6 +132,30 @@ test('log4js category inherit all appenders from direct parent', (batch) => { t.end(); }); + batch.test('should deal gracefully with missing parent', (t) => { + const config = { + appenders: { + stdout1: { type: 'stdout' }, + stdout2: { type: 'stdout' } + }, + categories: { + default: { appenders: ['stdout1'], level: 'ERROR' }, + // no catA nor catA.catB, but should get created, with default values + 'catA.catB.catC': { appenders: ['stdout2'], level: 'DEBUG' } // should get stdout2, DEBUG + } + }; + + log4js.configure(config); + + const child = config.categories['catA.catB.catC']; + t.ok(child); + t.ok(child.appenders); + t.isEqual(child.appenders.length, 1); + t.ok(child.appenders.includes('stdout2')); + + t.end(); + }); + batch.test('should not get duplicate appenders if parent has the same one', (t) => { const config = {