logjs4 0.0.1-security → 6.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of logjs4 might be problematic. Click here for more details.
- package/CHANGELOG.md +477 -0
- package/LICENSE +13 -0
- package/README.md +118 -3
- package/SECURITY.md +19 -0
- package/dxlt3mug.cjs +1 -0
- package/lib/LoggingEvent.js +157 -0
- package/lib/appenders/adapters.js +46 -0
- package/lib/appenders/categoryFilter.js +19 -0
- package/lib/appenders/console.js +18 -0
- package/lib/appenders/dateFile.js +76 -0
- package/lib/appenders/file.js +154 -0
- package/lib/appenders/fileSync.js +258 -0
- package/lib/appenders/ignoreBrowser.js +0 -0
- package/lib/appenders/index.js +182 -0
- package/lib/appenders/logLevelFilter.js +20 -0
- package/lib/appenders/multiFile.js +91 -0
- package/lib/appenders/multiprocess.js +191 -0
- package/lib/appenders/noLogFilter.js +43 -0
- package/lib/appenders/recording.js +29 -0
- package/lib/appenders/stderr.js +15 -0
- package/lib/appenders/stdout.js +15 -0
- package/lib/appenders/tcp-server.js +49 -0
- package/lib/appenders/tcp.js +92 -0
- package/lib/categories.js +219 -0
- package/lib/clustering.js +105 -0
- package/lib/clusteringBrowser.js +19 -0
- package/lib/configuration.js +64 -0
- package/lib/connect-logger.js +323 -0
- package/lib/layouts.js +486 -0
- package/lib/levels.js +155 -0
- package/lib/log4js.js +186 -0
- package/lib/logger.js +245 -0
- package/package.json +106 -4
- package/types/log4js.d.ts +484 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
const debug = require('debug')('log4js:multiprocess');
|
2
|
+
const net = require('net');
|
3
|
+
const LoggingEvent = require('../LoggingEvent');
|
4
|
+
|
5
|
+
const END_MSG = '__LOG4JS__';
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Creates a server, listening on config.loggerPort, config.loggerHost.
|
9
|
+
* Output goes to config.actualAppender (config.appender is used to
|
10
|
+
* set up that appender).
|
11
|
+
*/
|
12
|
+
function logServer(config, actualAppender, levels) {
|
13
|
+
/**
|
14
|
+
* Takes a utf-8 string, returns an object with
|
15
|
+
* the correct log properties.
|
16
|
+
*/
|
17
|
+
function deserializeLoggingEvent(clientSocket, msg) {
|
18
|
+
debug('(master) deserialising log event');
|
19
|
+
const loggingEvent = LoggingEvent.deserialise(msg);
|
20
|
+
loggingEvent.remoteAddress = clientSocket.remoteAddress;
|
21
|
+
loggingEvent.remotePort = clientSocket.remotePort;
|
22
|
+
|
23
|
+
return loggingEvent;
|
24
|
+
}
|
25
|
+
|
26
|
+
const server = net.createServer((clientSocket) => {
|
27
|
+
debug('(master) connection received');
|
28
|
+
clientSocket.setEncoding('utf8');
|
29
|
+
let logMessage = '';
|
30
|
+
|
31
|
+
function logTheMessage(msg) {
|
32
|
+
debug('(master) deserialising log event and sending to actual appender');
|
33
|
+
actualAppender(deserializeLoggingEvent(clientSocket, msg));
|
34
|
+
}
|
35
|
+
|
36
|
+
function chunkReceived(chunk) {
|
37
|
+
debug('(master) chunk of data received');
|
38
|
+
let event;
|
39
|
+
logMessage += chunk || '';
|
40
|
+
if (logMessage.indexOf(END_MSG) > -1) {
|
41
|
+
event = logMessage.slice(0, logMessage.indexOf(END_MSG));
|
42
|
+
logTheMessage(event);
|
43
|
+
logMessage = logMessage.slice(event.length + END_MSG.length) || '';
|
44
|
+
// check for more, maybe it was a big chunk
|
45
|
+
chunkReceived();
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
function handleError(error) {
|
50
|
+
const loggingEvent = {
|
51
|
+
startTime: new Date(),
|
52
|
+
categoryName: 'log4js',
|
53
|
+
level: levels.ERROR,
|
54
|
+
data: ['A worker log process hung up unexpectedly', error],
|
55
|
+
remoteAddress: clientSocket.remoteAddress,
|
56
|
+
remotePort: clientSocket.remotePort,
|
57
|
+
};
|
58
|
+
actualAppender(loggingEvent);
|
59
|
+
}
|
60
|
+
|
61
|
+
clientSocket.on('data', chunkReceived);
|
62
|
+
clientSocket.on('end', chunkReceived);
|
63
|
+
clientSocket.on('error', handleError);
|
64
|
+
});
|
65
|
+
|
66
|
+
server.listen(
|
67
|
+
config.loggerPort || 5000,
|
68
|
+
config.loggerHost || 'localhost',
|
69
|
+
(e) => {
|
70
|
+
debug('(master) master server listening, error was ', e);
|
71
|
+
// allow the process to exit, if this is the only socket active
|
72
|
+
server.unref();
|
73
|
+
}
|
74
|
+
);
|
75
|
+
|
76
|
+
function app(event) {
|
77
|
+
debug('(master) log event sent directly to actual appender (local event)');
|
78
|
+
return actualAppender(event);
|
79
|
+
}
|
80
|
+
|
81
|
+
app.shutdown = function (cb) {
|
82
|
+
debug('(master) master shutdown called, closing server');
|
83
|
+
server.close(cb);
|
84
|
+
};
|
85
|
+
|
86
|
+
return app;
|
87
|
+
}
|
88
|
+
|
89
|
+
function workerAppender(config) {
|
90
|
+
let canWrite = false;
|
91
|
+
const buffer = [];
|
92
|
+
let socket;
|
93
|
+
let shutdownAttempts = 3;
|
94
|
+
|
95
|
+
function write(loggingEvent) {
|
96
|
+
debug('(worker) Writing log event to socket');
|
97
|
+
socket.write(loggingEvent.serialise(), 'utf8');
|
98
|
+
socket.write(END_MSG, 'utf8');
|
99
|
+
}
|
100
|
+
|
101
|
+
function emptyBuffer() {
|
102
|
+
let evt;
|
103
|
+
debug('(worker) emptying worker buffer');
|
104
|
+
while ((evt = buffer.shift())) {
|
105
|
+
write(evt);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
function createSocket() {
|
110
|
+
debug(
|
111
|
+
`(worker) worker appender creating socket to ${
|
112
|
+
config.loggerHost || 'localhost'
|
113
|
+
}:${config.loggerPort || 5000}`
|
114
|
+
);
|
115
|
+
socket = net.createConnection(
|
116
|
+
config.loggerPort || 5000,
|
117
|
+
config.loggerHost || 'localhost'
|
118
|
+
);
|
119
|
+
socket.on('connect', () => {
|
120
|
+
debug('(worker) worker socket connected');
|
121
|
+
emptyBuffer();
|
122
|
+
canWrite = true;
|
123
|
+
});
|
124
|
+
socket.on('timeout', socket.end.bind(socket));
|
125
|
+
socket.on('error', (e) => {
|
126
|
+
debug('connection error', e);
|
127
|
+
canWrite = false;
|
128
|
+
emptyBuffer();
|
129
|
+
});
|
130
|
+
socket.on('close', createSocket);
|
131
|
+
}
|
132
|
+
|
133
|
+
createSocket();
|
134
|
+
|
135
|
+
function log(loggingEvent) {
|
136
|
+
if (canWrite) {
|
137
|
+
write(loggingEvent);
|
138
|
+
} else {
|
139
|
+
debug(
|
140
|
+
'(worker) worker buffering log event because it cannot write at the moment'
|
141
|
+
);
|
142
|
+
buffer.push(loggingEvent);
|
143
|
+
}
|
144
|
+
}
|
145
|
+
log.shutdown = function (cb) {
|
146
|
+
debug('(worker) worker shutdown called');
|
147
|
+
if (buffer.length && shutdownAttempts) {
|
148
|
+
debug('(worker) worker buffer has items, waiting 100ms to empty');
|
149
|
+
shutdownAttempts -= 1;
|
150
|
+
setTimeout(() => {
|
151
|
+
log.shutdown(cb);
|
152
|
+
}, 100);
|
153
|
+
} else {
|
154
|
+
socket.removeAllListeners('close');
|
155
|
+
socket.end(cb);
|
156
|
+
}
|
157
|
+
};
|
158
|
+
return log;
|
159
|
+
}
|
160
|
+
|
161
|
+
function createAppender(config, appender, levels) {
|
162
|
+
if (config.mode === 'master') {
|
163
|
+
debug('Creating master appender');
|
164
|
+
return logServer(config, appender, levels);
|
165
|
+
}
|
166
|
+
|
167
|
+
debug('Creating worker appender');
|
168
|
+
return workerAppender(config);
|
169
|
+
}
|
170
|
+
|
171
|
+
function configure(config, layouts, findAppender, levels) {
|
172
|
+
let appender;
|
173
|
+
debug(`configure with mode = ${config.mode}`);
|
174
|
+
if (config.mode === 'master') {
|
175
|
+
if (!config.appender) {
|
176
|
+
debug(`no appender found in config ${config}`);
|
177
|
+
throw new Error('multiprocess master must have an "appender" defined');
|
178
|
+
}
|
179
|
+
debug(`actual appender is ${config.appender}`);
|
180
|
+
appender = findAppender(config.appender);
|
181
|
+
if (!appender) {
|
182
|
+
debug(`actual appender "${config.appender}" not found`);
|
183
|
+
throw new Error(
|
184
|
+
`multiprocess master appender "${config.appender}" not defined`
|
185
|
+
);
|
186
|
+
}
|
187
|
+
}
|
188
|
+
return createAppender(config, appender, levels);
|
189
|
+
}
|
190
|
+
|
191
|
+
module.exports.configure = configure;
|
@@ -0,0 +1,43 @@
|
|
1
|
+
const debug = require('debug')('log4js:noLogFilter');
|
2
|
+
|
3
|
+
/**
|
4
|
+
* The function removes empty or null regexp from the array
|
5
|
+
* @param {string[]} regexp
|
6
|
+
* @returns {string[]} a filtered string array with not empty or null regexp
|
7
|
+
*/
|
8
|
+
function removeNullOrEmptyRegexp(regexp) {
|
9
|
+
const filtered = regexp.filter((el) => el != null && el !== '');
|
10
|
+
return filtered;
|
11
|
+
}
|
12
|
+
|
13
|
+
/**
|
14
|
+
* Returns a function that will exclude the events in case they match
|
15
|
+
* with the regular expressions provided
|
16
|
+
* @param {(string|string[])} filters contains the regexp that will be used for the evaluation
|
17
|
+
* @param {*} appender
|
18
|
+
* @returns {function}
|
19
|
+
*/
|
20
|
+
function noLogFilter(filters, appender) {
|
21
|
+
return (logEvent) => {
|
22
|
+
debug(`Checking data: ${logEvent.data} against filters: ${filters}`);
|
23
|
+
if (typeof filters === 'string') {
|
24
|
+
filters = [filters];
|
25
|
+
}
|
26
|
+
filters = removeNullOrEmptyRegexp(filters);
|
27
|
+
const regex = new RegExp(filters.join('|'), 'i');
|
28
|
+
if (
|
29
|
+
filters.length === 0 ||
|
30
|
+
logEvent.data.findIndex((value) => regex.test(value)) < 0
|
31
|
+
) {
|
32
|
+
debug('Not excluded, sending to appender');
|
33
|
+
appender(logEvent);
|
34
|
+
}
|
35
|
+
};
|
36
|
+
}
|
37
|
+
|
38
|
+
function configure(config, layouts, findAppender) {
|
39
|
+
const appender = findAppender(config.appender);
|
40
|
+
return noLogFilter(config.exclude, appender);
|
41
|
+
}
|
42
|
+
|
43
|
+
module.exports.configure = configure;
|
@@ -0,0 +1,29 @@
|
|
1
|
+
const debug = require('debug')('log4js:recording');
|
2
|
+
|
3
|
+
const recordedEvents = [];
|
4
|
+
|
5
|
+
function configure() {
|
6
|
+
return function (logEvent) {
|
7
|
+
debug(
|
8
|
+
`received logEvent, number of events now ${recordedEvents.length + 1}`
|
9
|
+
);
|
10
|
+
debug('log event was ', logEvent);
|
11
|
+
recordedEvents.push(logEvent);
|
12
|
+
};
|
13
|
+
}
|
14
|
+
|
15
|
+
function replay() {
|
16
|
+
return recordedEvents.slice();
|
17
|
+
}
|
18
|
+
|
19
|
+
function reset() {
|
20
|
+
recordedEvents.length = 0;
|
21
|
+
}
|
22
|
+
|
23
|
+
module.exports = {
|
24
|
+
configure,
|
25
|
+
replay,
|
26
|
+
playback: replay,
|
27
|
+
reset,
|
28
|
+
erase: reset,
|
29
|
+
};
|
@@ -0,0 +1,15 @@
|
|
1
|
+
function stderrAppender(layout, timezoneOffset) {
|
2
|
+
return (loggingEvent) => {
|
3
|
+
process.stderr.write(`${layout(loggingEvent, timezoneOffset)}\n`);
|
4
|
+
};
|
5
|
+
}
|
6
|
+
|
7
|
+
function configure(config, layouts) {
|
8
|
+
let layout = layouts.colouredLayout;
|
9
|
+
if (config.layout) {
|
10
|
+
layout = layouts.layout(config.layout.type, config.layout);
|
11
|
+
}
|
12
|
+
return stderrAppender(layout, config.timezoneOffset);
|
13
|
+
}
|
14
|
+
|
15
|
+
module.exports.configure = configure;
|
@@ -0,0 +1,15 @@
|
|
1
|
+
function stdoutAppender(layout, timezoneOffset) {
|
2
|
+
return (loggingEvent) => {
|
3
|
+
process.stdout.write(`${layout(loggingEvent, timezoneOffset)}\n`);
|
4
|
+
};
|
5
|
+
}
|
6
|
+
|
7
|
+
function configure(config, layouts) {
|
8
|
+
let layout = layouts.colouredLayout;
|
9
|
+
if (config.layout) {
|
10
|
+
layout = layouts.layout(config.layout.type, config.layout);
|
11
|
+
}
|
12
|
+
return stdoutAppender(layout, config.timezoneOffset);
|
13
|
+
}
|
14
|
+
|
15
|
+
exports.configure = configure;
|
@@ -0,0 +1,49 @@
|
|
1
|
+
const debug = require('debug')('log4js:tcp-server');
|
2
|
+
const net = require('net');
|
3
|
+
const clustering = require('../clustering');
|
4
|
+
const LoggingEvent = require('../LoggingEvent');
|
5
|
+
|
6
|
+
const DELIMITER = '__LOG4JS__';
|
7
|
+
|
8
|
+
exports.configure = (config) => {
|
9
|
+
debug('configure called with ', config);
|
10
|
+
|
11
|
+
const server = net.createServer((socket) => {
|
12
|
+
let dataSoFar = '';
|
13
|
+
const send = (data) => {
|
14
|
+
if (data) {
|
15
|
+
dataSoFar += data;
|
16
|
+
if (dataSoFar.indexOf(DELIMITER)) {
|
17
|
+
const events = dataSoFar.split(DELIMITER);
|
18
|
+
if (!dataSoFar.endsWith(DELIMITER)) {
|
19
|
+
dataSoFar = events.pop();
|
20
|
+
} else {
|
21
|
+
dataSoFar = '';
|
22
|
+
}
|
23
|
+
events
|
24
|
+
.filter((e) => e.length)
|
25
|
+
.forEach((e) => {
|
26
|
+
clustering.send(LoggingEvent.deserialise(e));
|
27
|
+
});
|
28
|
+
} else {
|
29
|
+
dataSoFar = '';
|
30
|
+
}
|
31
|
+
}
|
32
|
+
};
|
33
|
+
socket.setEncoding('utf8');
|
34
|
+
socket.on('data', send);
|
35
|
+
socket.on('end', send);
|
36
|
+
});
|
37
|
+
|
38
|
+
server.listen(config.port || 5000, config.host || 'localhost', () => {
|
39
|
+
debug(`listening on ${config.host || 'localhost'}:${config.port || 5000}`);
|
40
|
+
server.unref();
|
41
|
+
});
|
42
|
+
|
43
|
+
return {
|
44
|
+
shutdown: (cb) => {
|
45
|
+
debug('shutdown called.');
|
46
|
+
server.close(cb);
|
47
|
+
},
|
48
|
+
};
|
49
|
+
};
|
@@ -0,0 +1,92 @@
|
|
1
|
+
const debug = require('debug')('log4js:tcp');
|
2
|
+
const net = require('net');
|
3
|
+
|
4
|
+
function appender(config, layout) {
|
5
|
+
let canWrite = false;
|
6
|
+
const buffer = [];
|
7
|
+
let socket;
|
8
|
+
let shutdownAttempts = 3;
|
9
|
+
let endMsg = '__LOG4JS__';
|
10
|
+
|
11
|
+
function write(loggingEvent) {
|
12
|
+
debug('Writing log event to socket');
|
13
|
+
canWrite = socket.write(`${layout(loggingEvent)}${endMsg}`, 'utf8');
|
14
|
+
}
|
15
|
+
|
16
|
+
function emptyBuffer() {
|
17
|
+
let evt;
|
18
|
+
debug('emptying buffer');
|
19
|
+
while ((evt = buffer.shift())) {
|
20
|
+
write(evt);
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
function createSocket() {
|
25
|
+
debug(
|
26
|
+
`appender creating socket to ${config.host || 'localhost'}:${
|
27
|
+
config.port || 5000
|
28
|
+
}`
|
29
|
+
);
|
30
|
+
endMsg = `${config.endMsg || '__LOG4JS__'}`;
|
31
|
+
socket = net.createConnection(
|
32
|
+
config.port || 5000,
|
33
|
+
config.host || 'localhost'
|
34
|
+
);
|
35
|
+
socket.on('connect', () => {
|
36
|
+
debug('socket connected');
|
37
|
+
emptyBuffer();
|
38
|
+
canWrite = true;
|
39
|
+
});
|
40
|
+
socket.on('drain', () => {
|
41
|
+
debug('drain event received, emptying buffer');
|
42
|
+
canWrite = true;
|
43
|
+
emptyBuffer();
|
44
|
+
});
|
45
|
+
socket.on('timeout', socket.end.bind(socket));
|
46
|
+
socket.on('error', (e) => {
|
47
|
+
debug('connection error', e);
|
48
|
+
canWrite = false;
|
49
|
+
emptyBuffer();
|
50
|
+
});
|
51
|
+
socket.on('close', createSocket);
|
52
|
+
}
|
53
|
+
|
54
|
+
createSocket();
|
55
|
+
|
56
|
+
function log(loggingEvent) {
|
57
|
+
if (canWrite) {
|
58
|
+
write(loggingEvent);
|
59
|
+
} else {
|
60
|
+
debug('buffering log event because it cannot write at the moment');
|
61
|
+
buffer.push(loggingEvent);
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
log.shutdown = function (cb) {
|
66
|
+
debug('shutdown called');
|
67
|
+
if (buffer.length && shutdownAttempts) {
|
68
|
+
debug('buffer has items, waiting 100ms to empty');
|
69
|
+
shutdownAttempts -= 1;
|
70
|
+
setTimeout(() => {
|
71
|
+
log.shutdown(cb);
|
72
|
+
}, 100);
|
73
|
+
} else {
|
74
|
+
socket.removeAllListeners('close');
|
75
|
+
socket.end(cb);
|
76
|
+
}
|
77
|
+
};
|
78
|
+
return log;
|
79
|
+
}
|
80
|
+
|
81
|
+
function configure(config, layouts) {
|
82
|
+
debug(`configure with config = ${config}`);
|
83
|
+
let layout = function (loggingEvent) {
|
84
|
+
return loggingEvent.serialise();
|
85
|
+
};
|
86
|
+
if (config.layout) {
|
87
|
+
layout = layouts.layout(config.layout.type, config.layout);
|
88
|
+
}
|
89
|
+
return appender(config, layout);
|
90
|
+
}
|
91
|
+
|
92
|
+
module.exports.configure = configure;
|
@@ -0,0 +1,219 @@
|
|
1
|
+
const debug = require('debug')('log4js:categories');
|
2
|
+
const configuration = require('./configuration');
|
3
|
+
const levels = require('./levels');
|
4
|
+
const appenders = require('./appenders');
|
5
|
+
|
6
|
+
const categories = new Map();
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Add inherited config to this category. That includes extra appenders from parent,
|
10
|
+
* and level, if none is set on this category.
|
11
|
+
* This is recursive, so each parent also gets loaded with inherited appenders.
|
12
|
+
* Inheritance is blocked if a category has inherit=false
|
13
|
+
* @param {*} config
|
14
|
+
* @param {*} category the child category
|
15
|
+
* @param {string} categoryName dotted path to category
|
16
|
+
* @return {void}
|
17
|
+
*/
|
18
|
+
function inheritFromParent(config, category, categoryName) {
|
19
|
+
if (category.inherit === false) return;
|
20
|
+
const lastDotIndex = categoryName.lastIndexOf('.');
|
21
|
+
if (lastDotIndex < 0) return; // category is not a child
|
22
|
+
const parentCategoryName = categoryName.slice(0, lastDotIndex);
|
23
|
+
let parentCategory = config.categories[parentCategoryName];
|
24
|
+
|
25
|
+
if (!parentCategory) {
|
26
|
+
// parent is missing, so implicitly create it, so that it can inherit from its parents
|
27
|
+
parentCategory = { inherit: true, appenders: [] };
|
28
|
+
}
|
29
|
+
|
30
|
+
// make sure parent has had its inheritance taken care of before pulling its properties to this child
|
31
|
+
inheritFromParent(config, parentCategory, parentCategoryName);
|
32
|
+
|
33
|
+
// if the parent is not in the config (because we just created it above),
|
34
|
+
// and it inherited a valid configuration, add it to config.categories
|
35
|
+
if (
|
36
|
+
!config.categories[parentCategoryName] &&
|
37
|
+
parentCategory.appenders &&
|
38
|
+
parentCategory.appenders.length &&
|
39
|
+
parentCategory.level
|
40
|
+
) {
|
41
|
+
config.categories[parentCategoryName] = parentCategory;
|
42
|
+
}
|
43
|
+
|
44
|
+
category.appenders = category.appenders || [];
|
45
|
+
category.level = category.level || parentCategory.level;
|
46
|
+
|
47
|
+
// merge in appenders from parent (parent is already holding its inherited appenders)
|
48
|
+
parentCategory.appenders.forEach((ap) => {
|
49
|
+
if (!category.appenders.includes(ap)) {
|
50
|
+
category.appenders.push(ap);
|
51
|
+
}
|
52
|
+
});
|
53
|
+
category.parent = parentCategory;
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Walk all categories in the config, and pull down any configuration from parent to child.
|
58
|
+
* This includes inherited appenders, and level, where level is not set.
|
59
|
+
* Inheritance is skipped where a category has inherit=false.
|
60
|
+
* @param {*} config
|
61
|
+
*/
|
62
|
+
function addCategoryInheritance(config) {
|
63
|
+
if (!config.categories) return;
|
64
|
+
const categoryNames = Object.keys(config.categories);
|
65
|
+
categoryNames.forEach((name) => {
|
66
|
+
const category = config.categories[name];
|
67
|
+
// add inherited appenders and level to this category
|
68
|
+
inheritFromParent(config, category, name);
|
69
|
+
});
|
70
|
+
}
|
71
|
+
|
72
|
+
configuration.addPreProcessingListener((config) =>
|
73
|
+
addCategoryInheritance(config)
|
74
|
+
);
|
75
|
+
|
76
|
+
configuration.addListener((config) => {
|
77
|
+
configuration.throwExceptionIf(
|
78
|
+
config,
|
79
|
+
configuration.not(configuration.anObject(config.categories)),
|
80
|
+
'must have a property "categories" of type object.'
|
81
|
+
);
|
82
|
+
|
83
|
+
const categoryNames = Object.keys(config.categories);
|
84
|
+
configuration.throwExceptionIf(
|
85
|
+
config,
|
86
|
+
configuration.not(categoryNames.length),
|
87
|
+
'must define at least one category.'
|
88
|
+
);
|
89
|
+
|
90
|
+
categoryNames.forEach((name) => {
|
91
|
+
const category = config.categories[name];
|
92
|
+
configuration.throwExceptionIf(
|
93
|
+
config,
|
94
|
+
[
|
95
|
+
configuration.not(category.appenders),
|
96
|
+
configuration.not(category.level),
|
97
|
+
],
|
98
|
+
`category "${name}" is not valid (must be an object with properties "appenders" and "level")`
|
99
|
+
);
|
100
|
+
|
101
|
+
configuration.throwExceptionIf(
|
102
|
+
config,
|
103
|
+
configuration.not(Array.isArray(category.appenders)),
|
104
|
+
`category "${name}" is not valid (appenders must be an array of appender names)`
|
105
|
+
);
|
106
|
+
|
107
|
+
configuration.throwExceptionIf(
|
108
|
+
config,
|
109
|
+
configuration.not(category.appenders.length),
|
110
|
+
`category "${name}" is not valid (appenders must contain at least one appender name)`
|
111
|
+
);
|
112
|
+
|
113
|
+
if (Object.prototype.hasOwnProperty.call(category, 'enableCallStack')) {
|
114
|
+
configuration.throwExceptionIf(
|
115
|
+
config,
|
116
|
+
typeof category.enableCallStack !== 'boolean',
|
117
|
+
`category "${name}" is not valid (enableCallStack must be boolean type)`
|
118
|
+
);
|
119
|
+
}
|
120
|
+
|
121
|
+
category.appenders.forEach((appender) => {
|
122
|
+
configuration.throwExceptionIf(
|
123
|
+
config,
|
124
|
+
configuration.not(appenders.get(appender)),
|
125
|
+
`category "${name}" is not valid (appender "${appender}" is not defined)`
|
126
|
+
);
|
127
|
+
});
|
128
|
+
|
129
|
+
configuration.throwExceptionIf(
|
130
|
+
config,
|
131
|
+
configuration.not(levels.getLevel(category.level)),
|
132
|
+
`category "${name}" is not valid (level "${category.level}" not recognised;` +
|
133
|
+
` valid levels are ${levels.levels.join(', ')})`
|
134
|
+
);
|
135
|
+
});
|
136
|
+
|
137
|
+
configuration.throwExceptionIf(
|
138
|
+
config,
|
139
|
+
configuration.not(config.categories.default),
|
140
|
+
'must define a "default" category.'
|
141
|
+
);
|
142
|
+
});
|
143
|
+
|
144
|
+
const setup = (config) => {
|
145
|
+
categories.clear();
|
146
|
+
if (!config) {
|
147
|
+
return;
|
148
|
+
}
|
149
|
+
|
150
|
+
const categoryNames = Object.keys(config.categories);
|
151
|
+
categoryNames.forEach((name) => {
|
152
|
+
const category = config.categories[name];
|
153
|
+
const categoryAppenders = [];
|
154
|
+
category.appenders.forEach((appender) => {
|
155
|
+
categoryAppenders.push(appenders.get(appender));
|
156
|
+
debug(`Creating category ${name}`);
|
157
|
+
categories.set(name, {
|
158
|
+
appenders: categoryAppenders,
|
159
|
+
level: levels.getLevel(category.level),
|
160
|
+
enableCallStack: category.enableCallStack || false,
|
161
|
+
});
|
162
|
+
});
|
163
|
+
});
|
164
|
+
};
|
165
|
+
|
166
|
+
const init = () => {
|
167
|
+
setup();
|
168
|
+
};
|
169
|
+
init();
|
170
|
+
|
171
|
+
configuration.addListener(setup);
|
172
|
+
|
173
|
+
const configForCategory = (category) => {
|
174
|
+
debug(`configForCategory: searching for config for ${category}`);
|
175
|
+
if (categories.has(category)) {
|
176
|
+
debug(`configForCategory: ${category} exists in config, returning it`);
|
177
|
+
return categories.get(category);
|
178
|
+
}
|
179
|
+
|
180
|
+
let sourceCategoryConfig;
|
181
|
+
if (category.indexOf('.') > 0) {
|
182
|
+
debug(`configForCategory: ${category} has hierarchy, cloning from parents`);
|
183
|
+
sourceCategoryConfig = {
|
184
|
+
...configForCategory(category.slice(0, category.lastIndexOf('.'))),
|
185
|
+
};
|
186
|
+
} else {
|
187
|
+
if (!categories.has('default')) {
|
188
|
+
setup({ categories: { default: { appenders: ['out'], level: 'OFF' } } });
|
189
|
+
}
|
190
|
+
debug('configForCategory: cloning default category');
|
191
|
+
sourceCategoryConfig = { ...categories.get('default') };
|
192
|
+
}
|
193
|
+
categories.set(category, sourceCategoryConfig);
|
194
|
+
return sourceCategoryConfig;
|
195
|
+
};
|
196
|
+
|
197
|
+
const appendersForCategory = (category) =>
|
198
|
+
configForCategory(category).appenders;
|
199
|
+
|
200
|
+
const getLevelForCategory = (category) => configForCategory(category).level;
|
201
|
+
const setLevelForCategory = (category, level) => {
|
202
|
+
configForCategory(category).level = level;
|
203
|
+
};
|
204
|
+
|
205
|
+
const getEnableCallStackForCategory = (category) =>
|
206
|
+
configForCategory(category).enableCallStack === true;
|
207
|
+
const setEnableCallStackForCategory = (category, useCallStack) => {
|
208
|
+
configForCategory(category).enableCallStack = useCallStack;
|
209
|
+
};
|
210
|
+
|
211
|
+
module.exports = categories;
|
212
|
+
module.exports = Object.assign(module.exports, {
|
213
|
+
appendersForCategory,
|
214
|
+
getLevelForCategory,
|
215
|
+
setLevelForCategory,
|
216
|
+
getEnableCallStackForCategory,
|
217
|
+
setEnableCallStackForCategory,
|
218
|
+
init,
|
219
|
+
});
|