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