123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- const debug = require('debug')('log4js:categories');
- const configuration = require('./configuration');
- const levels = require('./levels');
- 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 {*} config
- * @param {*} 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) {
- // parent is missing, so implicitly create it, so that it can inherit from its parents
- parentCategory = { inherit: true, appenders: [] };
- }
- // 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;
- }
- /**
- * 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 {*} config
- */
- function addCategoryInheritance(config) {
- if (!config.categories) return;
- 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.addPreProcessingListener(config => addCategoryInheritance(config));
- 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)`
- );
- if (Object.prototype.hasOwnProperty.call(category, 'enableCallStack')) {
- configuration.throwExceptionIf(
- config,
- typeof category.enableCallStack !== 'boolean',
- `category "${name}" is not valid (enableCallStack must be boolean type)`
- );
- }
- 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),
- enableCallStack: category.enableCallStack || false
- }
- );
- });
- });
- };
- setup({ categories: { default: { appenders: ['out'], 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);
- };
- const getEnableCallStackForCategory = category => configForCategory(category).enableCallStack === true;
- const setEnableCallStackForCategory = (category, useCallStack) => {
- configForCategory(category).enableCallStack = useCallStack;
- };
- module.exports = {
- appendersForCategory,
- getLevelForCategory,
- setLevelForCategory,
- getEnableCallStackForCategory,
- setEnableCallStackForCategory,
- };
|