index.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. const path = require('path');
  2. const debug = require('debug')('log4js:appenders');
  3. const configuration = require('../configuration');
  4. const clustering = require('../clustering');
  5. const levels = require('../levels');
  6. const layouts = require('../layouts');
  7. const adapters = require('./adapters');
  8. // pre-load the core appenders so that webpack can find them
  9. const coreAppenders = new Map();
  10. coreAppenders.set('console', require('./console'));
  11. coreAppenders.set('stdout', require('./stdout'));
  12. coreAppenders.set('stderr', require('./stderr'));
  13. coreAppenders.set('logLevelFilter', require('./logLevelFilter'));
  14. coreAppenders.set('categoryFilter', require('./categoryFilter'));
  15. coreAppenders.set('noLogFilter', require('./noLogFilter'));
  16. coreAppenders.set('file', require('./file'));
  17. coreAppenders.set('dateFile', require('./dateFile'));
  18. coreAppenders.set('fileSync', require('./fileSync'));
  19. coreAppenders.set('tcp', require('./tcp'));
  20. const appenders = new Map();
  21. const tryLoading = (modulePath, config) => {
  22. debug('Loading module from ', modulePath);
  23. try {
  24. // eslint-disable-next-line global-require, import/no-dynamic-require
  25. return require(modulePath);
  26. } catch (e) {
  27. // if the module was found, and we still got an error, then raise it
  28. configuration.throwExceptionIf(
  29. config,
  30. e.code !== 'MODULE_NOT_FOUND',
  31. `appender "${modulePath}" could not be loaded (error was: ${e})`
  32. );
  33. return undefined;
  34. }
  35. };
  36. const loadAppenderModule = (type, config) => coreAppenders.get(type)
  37. || tryLoading(`./${type}`, config)
  38. || tryLoading(type, config)
  39. || (require.main && require.main.filename && tryLoading(path.join(path.dirname(require.main.filename), type), config))
  40. || tryLoading(path.join(process.cwd(), type), config);
  41. const appendersLoading = new Set();
  42. const getAppender = (name, config) => {
  43. if (appenders.has(name)) return appenders.get(name);
  44. if (!config.appenders[name]) return false;
  45. if (appendersLoading.has(name)) throw new Error(`Dependency loop detected for appender ${name}.`);
  46. appendersLoading.add(name);
  47. debug(`Creating appender ${name}`);
  48. // eslint-disable-next-line no-use-before-define
  49. const appender = createAppender(name, config);
  50. appendersLoading.delete(name);
  51. appenders.set(name, appender);
  52. return appender;
  53. };
  54. const createAppender = (name, config) => {
  55. const appenderConfig = config.appenders[name];
  56. const appenderModule = appenderConfig.type.configure
  57. ? appenderConfig.type : loadAppenderModule(appenderConfig.type, config);
  58. configuration.throwExceptionIf(
  59. config,
  60. configuration.not(appenderModule),
  61. `appender "${name}" is not valid (type "${appenderConfig.type}" could not be found)`
  62. );
  63. if (appenderModule.appender) {
  64. process.emitWarning(
  65. `Appender ${appenderConfig.type} exports an appender function.`,
  66. "DeprecationWarning", "log4js-node-DEP0001"
  67. );
  68. debug("[log4js-node-DEP0001]",
  69. `DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`);
  70. }
  71. if (appenderModule.shutdown) {
  72. process.emitWarning(
  73. `Appender ${appenderConfig.type} exports a shutdown function.`,
  74. "DeprecationWarning", "log4js-node-DEP0002"
  75. );
  76. debug("[log4js-node-DEP0002]",
  77. `DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`);
  78. }
  79. debug(`${name}: clustering.isMaster ? ${clustering.isMaster()}`);
  80. debug(`${name}: appenderModule is ${require('util').inspect(appenderModule)}`); // eslint-disable-line global-require
  81. return clustering.onlyOnMaster(() => {
  82. debug(`calling appenderModule.configure for ${name} / ${appenderConfig.type}`);
  83. return appenderModule.configure(
  84. adapters.modifyConfig(appenderConfig),
  85. layouts,
  86. appender => getAppender(appender, config),
  87. levels
  88. );
  89. }, /* istanbul ignore next: fn never gets called by non-master yet needed to pass config validation */ () => {});
  90. };
  91. const setup = (config) => {
  92. appenders.clear();
  93. appendersLoading.clear();
  94. if (!config) {
  95. return;
  96. }
  97. const usedAppenders = [];
  98. Object.values(config.categories).forEach(category => {
  99. usedAppenders.push(...category.appenders);
  100. });
  101. Object.keys(config.appenders).forEach((name) => {
  102. // dodgy hard-coding of special case for tcp-server and multiprocess which may not have
  103. // any categories associated with it, but needs to be started up anyway
  104. if (usedAppenders.includes(name) || config.appenders[name].type === 'tcp-server'
  105. || config.appenders[name].type === 'multiprocess') {
  106. getAppender(name, config);
  107. }
  108. });
  109. };
  110. const init = () => {
  111. setup();
  112. };
  113. init();
  114. configuration.addListener((config) => {
  115. configuration.throwExceptionIf(
  116. config,
  117. configuration.not(configuration.anObject(config.appenders)),
  118. 'must have a property "appenders" of type object.'
  119. );
  120. const appenderNames = Object.keys(config.appenders);
  121. configuration.throwExceptionIf(
  122. config,
  123. configuration.not(appenderNames.length),
  124. 'must define at least one appender.'
  125. );
  126. appenderNames.forEach((name) => {
  127. configuration.throwExceptionIf(
  128. config,
  129. configuration.not(config.appenders[name].type),
  130. `appender "${name}" is not valid (must be an object with property "type")`
  131. );
  132. });
  133. });
  134. configuration.addListener(setup);
  135. module.exports = appenders;
  136. module.exports.init = init;