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
package/lib/layouts.js
ADDED
@@ -0,0 +1,486 @@
|
|
1
|
+
const dateFormat = require('date-format');
|
2
|
+
const os = require('os');
|
3
|
+
const util = require('util');
|
4
|
+
const path = require('path');
|
5
|
+
const url = require('url');
|
6
|
+
const debug = require('debug')('log4js:layouts');
|
7
|
+
|
8
|
+
const styles = {
|
9
|
+
// styles
|
10
|
+
bold: [1, 22],
|
11
|
+
italic: [3, 23],
|
12
|
+
underline: [4, 24],
|
13
|
+
inverse: [7, 27],
|
14
|
+
// grayscale
|
15
|
+
white: [37, 39],
|
16
|
+
grey: [90, 39],
|
17
|
+
black: [90, 39],
|
18
|
+
// colors
|
19
|
+
blue: [34, 39],
|
20
|
+
cyan: [36, 39],
|
21
|
+
green: [32, 39],
|
22
|
+
magenta: [35, 39],
|
23
|
+
red: [91, 39],
|
24
|
+
yellow: [33, 39],
|
25
|
+
};
|
26
|
+
|
27
|
+
function colorizeStart(style) {
|
28
|
+
return style ? `\x1B[${styles[style][0]}m` : '';
|
29
|
+
}
|
30
|
+
|
31
|
+
function colorizeEnd(style) {
|
32
|
+
return style ? `\x1B[${styles[style][1]}m` : '';
|
33
|
+
}
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Taken from masylum's fork (https://github.com/masylum/log4js-node)
|
37
|
+
*/
|
38
|
+
function colorize(str, style) {
|
39
|
+
return colorizeStart(style) + str + colorizeEnd(style);
|
40
|
+
}
|
41
|
+
|
42
|
+
function timestampLevelAndCategory(loggingEvent, colour) {
|
43
|
+
return colorize(
|
44
|
+
util.format(
|
45
|
+
'[%s] [%s] %s - ',
|
46
|
+
dateFormat.asString(loggingEvent.startTime),
|
47
|
+
loggingEvent.level.toString(),
|
48
|
+
loggingEvent.categoryName
|
49
|
+
),
|
50
|
+
colour
|
51
|
+
);
|
52
|
+
}
|
53
|
+
|
54
|
+
/**
|
55
|
+
* BasicLayout is a simple layout for storing the logs. The logs are stored
|
56
|
+
* in following format:
|
57
|
+
* <pre>
|
58
|
+
* [startTime] [logLevel] categoryName - message\n
|
59
|
+
* </pre>
|
60
|
+
*
|
61
|
+
* @author Stephan Strittmatter
|
62
|
+
*/
|
63
|
+
function basicLayout(loggingEvent) {
|
64
|
+
return (
|
65
|
+
timestampLevelAndCategory(loggingEvent) + util.format(...loggingEvent.data)
|
66
|
+
);
|
67
|
+
}
|
68
|
+
|
69
|
+
/**
|
70
|
+
* colouredLayout - taken from masylum's fork.
|
71
|
+
* same as basicLayout, but with colours.
|
72
|
+
*/
|
73
|
+
function colouredLayout(loggingEvent) {
|
74
|
+
return (
|
75
|
+
timestampLevelAndCategory(loggingEvent, loggingEvent.level.colour) +
|
76
|
+
util.format(...loggingEvent.data)
|
77
|
+
);
|
78
|
+
}
|
79
|
+
|
80
|
+
function messagePassThroughLayout(loggingEvent) {
|
81
|
+
return util.format(...loggingEvent.data);
|
82
|
+
}
|
83
|
+
|
84
|
+
function dummyLayout(loggingEvent) {
|
85
|
+
return loggingEvent.data[0];
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* PatternLayout
|
90
|
+
* Format for specifiers is %[padding].[truncation][field]{[format]}
|
91
|
+
* e.g. %5.10p - left pad the log level by 5 characters, up to a max of 10
|
92
|
+
* both padding and truncation can be negative.
|
93
|
+
* Negative truncation = trunc from end of string
|
94
|
+
* Positive truncation = trunc from start of string
|
95
|
+
* Negative padding = pad right
|
96
|
+
* Positive padding = pad left
|
97
|
+
*
|
98
|
+
* Fields can be any of:
|
99
|
+
* - %r time in toLocaleTimeString format
|
100
|
+
* - %p log level
|
101
|
+
* - %c log category
|
102
|
+
* - %h hostname
|
103
|
+
* - %m log data
|
104
|
+
* - %m{l} where l is an integer, log data.slice(l)
|
105
|
+
* - %m{l,u} where l and u are integers, log data.slice(l, u)
|
106
|
+
* - %d date in constious formats
|
107
|
+
* - %% %
|
108
|
+
* - %n newline
|
109
|
+
* - %z pid
|
110
|
+
* - %f filename
|
111
|
+
* - %l line number
|
112
|
+
* - %o column postion
|
113
|
+
* - %s call stack
|
114
|
+
* - %C class name [#1316](https://github.com/log4js-node/log4js-node/pull/1316)
|
115
|
+
* - %M method or function name [#1316](https://github.com/log4js-node/log4js-node/pull/1316)
|
116
|
+
* - %A method or function alias [#1316](https://github.com/log4js-node/log4js-node/pull/1316)
|
117
|
+
* - %F fully qualified caller name [#1316](https://github.com/log4js-node/log4js-node/pull/1316)
|
118
|
+
* - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
|
119
|
+
* - %X{<tokenname>} add dynamic tokens to your log. Tokens are specified in logger context
|
120
|
+
* You can use %[ and %] to define a colored block.
|
121
|
+
*
|
122
|
+
* Tokens are specified as simple key:value objects.
|
123
|
+
* The key represents the token name whereas the value can be a string or function
|
124
|
+
* which is called to extract the value to put in the log message. If token is not
|
125
|
+
* found, it doesn't replace the field.
|
126
|
+
*
|
127
|
+
* A sample token would be: { 'pid' : function() { return process.pid; } }
|
128
|
+
*
|
129
|
+
* Takes a pattern string, array of tokens and returns a layout function.
|
130
|
+
* @return {Function}
|
131
|
+
* @param pattern
|
132
|
+
* @param tokens
|
133
|
+
* @param timezoneOffset
|
134
|
+
*
|
135
|
+
* @authors ['Stephan Strittmatter', 'Jan Schmidle']
|
136
|
+
*/
|
137
|
+
function patternLayout(pattern, tokens) {
|
138
|
+
const TTCC_CONVERSION_PATTERN = '%r %p %c - %m%n';
|
139
|
+
const regex =
|
140
|
+
/%(-?[0-9]+)?(\.?-?[0-9]+)?([[\]cdhmnprzxXyflosCMAF%])(\{([^}]+)\})?|([^%]+)/;
|
141
|
+
|
142
|
+
pattern = pattern || TTCC_CONVERSION_PATTERN;
|
143
|
+
|
144
|
+
function categoryName(loggingEvent, specifier) {
|
145
|
+
let loggerName = loggingEvent.categoryName;
|
146
|
+
if (specifier) {
|
147
|
+
const precision = parseInt(specifier, 10);
|
148
|
+
const loggerNameBits = loggerName.split('.');
|
149
|
+
if (precision < loggerNameBits.length) {
|
150
|
+
loggerName = loggerNameBits
|
151
|
+
.slice(loggerNameBits.length - precision)
|
152
|
+
.join('.');
|
153
|
+
}
|
154
|
+
}
|
155
|
+
return loggerName;
|
156
|
+
}
|
157
|
+
|
158
|
+
function formatAsDate(loggingEvent, specifier) {
|
159
|
+
let format = dateFormat.ISO8601_FORMAT;
|
160
|
+
if (specifier) {
|
161
|
+
format = specifier;
|
162
|
+
// Pick up special cases
|
163
|
+
switch (format) {
|
164
|
+
case 'ISO8601':
|
165
|
+
case 'ISO8601_FORMAT':
|
166
|
+
format = dateFormat.ISO8601_FORMAT;
|
167
|
+
break;
|
168
|
+
case 'ISO8601_WITH_TZ_OFFSET':
|
169
|
+
case 'ISO8601_WITH_TZ_OFFSET_FORMAT':
|
170
|
+
format = dateFormat.ISO8601_WITH_TZ_OFFSET_FORMAT;
|
171
|
+
break;
|
172
|
+
case 'ABSOLUTE':
|
173
|
+
process.emitWarning(
|
174
|
+
'Pattern %d{ABSOLUTE} is deprecated in favor of %d{ABSOLUTETIME}. ' +
|
175
|
+
'Please use %d{ABSOLUTETIME} instead.',
|
176
|
+
'DeprecationWarning',
|
177
|
+
'log4js-node-DEP0003'
|
178
|
+
);
|
179
|
+
debug(
|
180
|
+
'[log4js-node-DEP0003]',
|
181
|
+
'DEPRECATION: Pattern %d{ABSOLUTE} is deprecated and replaced by %d{ABSOLUTETIME}.'
|
182
|
+
);
|
183
|
+
// falls through
|
184
|
+
case 'ABSOLUTETIME':
|
185
|
+
case 'ABSOLUTETIME_FORMAT':
|
186
|
+
format = dateFormat.ABSOLUTETIME_FORMAT;
|
187
|
+
break;
|
188
|
+
case 'DATE':
|
189
|
+
process.emitWarning(
|
190
|
+
'Pattern %d{DATE} is deprecated due to the confusion it causes when used. ' +
|
191
|
+
'Please use %d{DATETIME} instead.',
|
192
|
+
'DeprecationWarning',
|
193
|
+
'log4js-node-DEP0004'
|
194
|
+
);
|
195
|
+
debug(
|
196
|
+
'[log4js-node-DEP0004]',
|
197
|
+
'DEPRECATION: Pattern %d{DATE} is deprecated and replaced by %d{DATETIME}.'
|
198
|
+
);
|
199
|
+
// falls through
|
200
|
+
case 'DATETIME':
|
201
|
+
case 'DATETIME_FORMAT':
|
202
|
+
format = dateFormat.DATETIME_FORMAT;
|
203
|
+
break;
|
204
|
+
// no default
|
205
|
+
}
|
206
|
+
}
|
207
|
+
// Format the date
|
208
|
+
return dateFormat.asString(format, loggingEvent.startTime);
|
209
|
+
}
|
210
|
+
|
211
|
+
function hostname() {
|
212
|
+
return os.hostname().toString();
|
213
|
+
}
|
214
|
+
|
215
|
+
function formatMessage(loggingEvent, specifier) {
|
216
|
+
let dataSlice = loggingEvent.data;
|
217
|
+
if (specifier) {
|
218
|
+
const [lowerBound, upperBound] = specifier.split(',');
|
219
|
+
dataSlice = dataSlice.slice(lowerBound, upperBound);
|
220
|
+
}
|
221
|
+
return util.format(...dataSlice);
|
222
|
+
}
|
223
|
+
|
224
|
+
function endOfLine() {
|
225
|
+
return os.EOL;
|
226
|
+
}
|
227
|
+
|
228
|
+
function logLevel(loggingEvent) {
|
229
|
+
return loggingEvent.level.toString();
|
230
|
+
}
|
231
|
+
|
232
|
+
function startTime(loggingEvent) {
|
233
|
+
return dateFormat.asString('hh:mm:ss', loggingEvent.startTime);
|
234
|
+
}
|
235
|
+
|
236
|
+
function startColour(loggingEvent) {
|
237
|
+
return colorizeStart(loggingEvent.level.colour);
|
238
|
+
}
|
239
|
+
|
240
|
+
function endColour(loggingEvent) {
|
241
|
+
return colorizeEnd(loggingEvent.level.colour);
|
242
|
+
}
|
243
|
+
|
244
|
+
function percent() {
|
245
|
+
return '%';
|
246
|
+
}
|
247
|
+
|
248
|
+
function pid(loggingEvent) {
|
249
|
+
return loggingEvent && loggingEvent.pid
|
250
|
+
? loggingEvent.pid.toString()
|
251
|
+
: process.pid.toString();
|
252
|
+
}
|
253
|
+
|
254
|
+
function clusterInfo() {
|
255
|
+
// this used to try to return the master and worker pids,
|
256
|
+
// but it would never have worked because master pid is not available to workers
|
257
|
+
// leaving this here to maintain compatibility for patterns
|
258
|
+
return pid();
|
259
|
+
}
|
260
|
+
|
261
|
+
function userDefined(loggingEvent, specifier) {
|
262
|
+
if (typeof tokens[specifier] !== 'undefined') {
|
263
|
+
return typeof tokens[specifier] === 'function'
|
264
|
+
? tokens[specifier](loggingEvent)
|
265
|
+
: tokens[specifier];
|
266
|
+
}
|
267
|
+
|
268
|
+
return null;
|
269
|
+
}
|
270
|
+
|
271
|
+
function contextDefined(loggingEvent, specifier) {
|
272
|
+
const resolver = loggingEvent.context[specifier];
|
273
|
+
|
274
|
+
if (typeof resolver !== 'undefined') {
|
275
|
+
return typeof resolver === 'function' ? resolver(loggingEvent) : resolver;
|
276
|
+
}
|
277
|
+
|
278
|
+
return null;
|
279
|
+
}
|
280
|
+
|
281
|
+
function fileName(loggingEvent, specifier) {
|
282
|
+
let filename = loggingEvent.fileName || '';
|
283
|
+
|
284
|
+
// support for ESM as it uses url instead of path for file
|
285
|
+
/* istanbul ignore next: unsure how to simulate ESM for test coverage */
|
286
|
+
const convertFileURLToPath = function (filepath) {
|
287
|
+
const urlPrefix = 'file://';
|
288
|
+
if (filepath.startsWith(urlPrefix)) {
|
289
|
+
// https://nodejs.org/api/url.html#urlfileurltopathurl
|
290
|
+
if (typeof url.fileURLToPath === 'function') {
|
291
|
+
filepath = url.fileURLToPath(filepath);
|
292
|
+
}
|
293
|
+
// backward-compatible for nodejs pre-10.12.0 (without url.fileURLToPath method)
|
294
|
+
else {
|
295
|
+
// posix: file:///hello/world/foo.txt -> /hello/world/foo.txt -> /hello/world/foo.txt
|
296
|
+
// win32: file:///C:/path/foo.txt -> /C:/path/foo.txt -> \C:\path\foo.txt -> C:\path\foo.txt
|
297
|
+
// win32: file://nas/foo.txt -> //nas/foo.txt -> nas\foo.txt -> \\nas\foo.txt
|
298
|
+
filepath = path.normalize(
|
299
|
+
filepath.replace(new RegExp(`^${urlPrefix}`), '')
|
300
|
+
);
|
301
|
+
if (process.platform === 'win32') {
|
302
|
+
if (filepath.startsWith('\\')) {
|
303
|
+
filepath = filepath.slice(1);
|
304
|
+
} else {
|
305
|
+
filepath = path.sep + path.sep + filepath;
|
306
|
+
}
|
307
|
+
}
|
308
|
+
}
|
309
|
+
}
|
310
|
+
return filepath;
|
311
|
+
};
|
312
|
+
filename = convertFileURLToPath(filename);
|
313
|
+
|
314
|
+
if (specifier) {
|
315
|
+
const fileDepth = parseInt(specifier, 10);
|
316
|
+
const fileList = filename.split(path.sep);
|
317
|
+
if (fileList.length > fileDepth) {
|
318
|
+
filename = fileList.slice(-fileDepth).join(path.sep);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
|
322
|
+
return filename;
|
323
|
+
}
|
324
|
+
|
325
|
+
function lineNumber(loggingEvent) {
|
326
|
+
return loggingEvent.lineNumber ? `${loggingEvent.lineNumber}` : '';
|
327
|
+
}
|
328
|
+
|
329
|
+
function columnNumber(loggingEvent) {
|
330
|
+
return loggingEvent.columnNumber ? `${loggingEvent.columnNumber}` : '';
|
331
|
+
}
|
332
|
+
|
333
|
+
function callStack(loggingEvent) {
|
334
|
+
return loggingEvent.callStack || '';
|
335
|
+
}
|
336
|
+
|
337
|
+
function className(loggingEvent) {
|
338
|
+
return loggingEvent.className || '';
|
339
|
+
}
|
340
|
+
|
341
|
+
function functionName(loggingEvent) {
|
342
|
+
return loggingEvent.functionName || '';
|
343
|
+
}
|
344
|
+
|
345
|
+
function functionAlias(loggingEvent) {
|
346
|
+
return loggingEvent.functionAlias || '';
|
347
|
+
}
|
348
|
+
|
349
|
+
function callerName(loggingEvent) {
|
350
|
+
return loggingEvent.callerName || '';
|
351
|
+
}
|
352
|
+
|
353
|
+
const replacers = {
|
354
|
+
c: categoryName,
|
355
|
+
d: formatAsDate,
|
356
|
+
h: hostname,
|
357
|
+
m: formatMessage,
|
358
|
+
n: endOfLine,
|
359
|
+
p: logLevel,
|
360
|
+
r: startTime,
|
361
|
+
'[': startColour,
|
362
|
+
']': endColour,
|
363
|
+
y: clusterInfo,
|
364
|
+
z: pid,
|
365
|
+
'%': percent,
|
366
|
+
x: userDefined,
|
367
|
+
X: contextDefined,
|
368
|
+
f: fileName,
|
369
|
+
l: lineNumber,
|
370
|
+
o: columnNumber,
|
371
|
+
s: callStack,
|
372
|
+
C: className,
|
373
|
+
M: functionName,
|
374
|
+
A: functionAlias,
|
375
|
+
F: callerName,
|
376
|
+
};
|
377
|
+
|
378
|
+
function replaceToken(conversionCharacter, loggingEvent, specifier) {
|
379
|
+
return replacers[conversionCharacter](loggingEvent, specifier);
|
380
|
+
}
|
381
|
+
|
382
|
+
function truncate(truncation, toTruncate) {
|
383
|
+
let len;
|
384
|
+
if (truncation) {
|
385
|
+
len = parseInt(truncation.slice(1), 10);
|
386
|
+
// negative truncate length means truncate from end of string
|
387
|
+
return len > 0 ? toTruncate.slice(0, len) : toTruncate.slice(len);
|
388
|
+
}
|
389
|
+
|
390
|
+
return toTruncate;
|
391
|
+
}
|
392
|
+
|
393
|
+
function pad(padding, toPad) {
|
394
|
+
let len;
|
395
|
+
if (padding) {
|
396
|
+
if (padding.charAt(0) === '-') {
|
397
|
+
len = parseInt(padding.slice(1), 10);
|
398
|
+
// Right pad with spaces
|
399
|
+
while (toPad.length < len) {
|
400
|
+
toPad += ' ';
|
401
|
+
}
|
402
|
+
} else {
|
403
|
+
len = parseInt(padding, 10);
|
404
|
+
// Left pad with spaces
|
405
|
+
while (toPad.length < len) {
|
406
|
+
toPad = ` ${toPad}`;
|
407
|
+
}
|
408
|
+
}
|
409
|
+
}
|
410
|
+
return toPad;
|
411
|
+
}
|
412
|
+
|
413
|
+
function truncateAndPad(toTruncAndPad, truncation, padding) {
|
414
|
+
let replacement = toTruncAndPad;
|
415
|
+
replacement = truncate(truncation, replacement);
|
416
|
+
replacement = pad(padding, replacement);
|
417
|
+
return replacement;
|
418
|
+
}
|
419
|
+
|
420
|
+
return function (loggingEvent) {
|
421
|
+
let formattedString = '';
|
422
|
+
let result;
|
423
|
+
let searchString = pattern;
|
424
|
+
|
425
|
+
while ((result = regex.exec(searchString)) !== null) {
|
426
|
+
// const matchedString = result[0];
|
427
|
+
const padding = result[1];
|
428
|
+
const truncation = result[2];
|
429
|
+
const conversionCharacter = result[3];
|
430
|
+
const specifier = result[5];
|
431
|
+
const text = result[6];
|
432
|
+
|
433
|
+
// Check if the pattern matched was just normal text
|
434
|
+
if (text) {
|
435
|
+
formattedString += text.toString();
|
436
|
+
} else {
|
437
|
+
// Create a raw replacement string based on the conversion
|
438
|
+
// character and specifier
|
439
|
+
const replacement = replaceToken(
|
440
|
+
conversionCharacter,
|
441
|
+
loggingEvent,
|
442
|
+
specifier
|
443
|
+
);
|
444
|
+
formattedString += truncateAndPad(replacement, truncation, padding);
|
445
|
+
}
|
446
|
+
searchString = searchString.slice(result.index + result[0].length);
|
447
|
+
}
|
448
|
+
return formattedString;
|
449
|
+
};
|
450
|
+
}
|
451
|
+
|
452
|
+
const layoutMakers = {
|
453
|
+
messagePassThrough() {
|
454
|
+
return messagePassThroughLayout;
|
455
|
+
},
|
456
|
+
basic() {
|
457
|
+
return basicLayout;
|
458
|
+
},
|
459
|
+
colored() {
|
460
|
+
return colouredLayout;
|
461
|
+
},
|
462
|
+
coloured() {
|
463
|
+
return colouredLayout;
|
464
|
+
},
|
465
|
+
pattern(config) {
|
466
|
+
return patternLayout(config && config.pattern, config && config.tokens);
|
467
|
+
},
|
468
|
+
dummy() {
|
469
|
+
return dummyLayout;
|
470
|
+
},
|
471
|
+
};
|
472
|
+
|
473
|
+
module.exports = {
|
474
|
+
basicLayout,
|
475
|
+
messagePassThroughLayout,
|
476
|
+
patternLayout,
|
477
|
+
colouredLayout,
|
478
|
+
coloredLayout: colouredLayout,
|
479
|
+
dummyLayout,
|
480
|
+
addLayout(name, serializerGenerator) {
|
481
|
+
layoutMakers[name] = serializerGenerator;
|
482
|
+
},
|
483
|
+
layout(name, config) {
|
484
|
+
return layoutMakers[name] && layoutMakers[name](config);
|
485
|
+
},
|
486
|
+
};
|
package/lib/levels.js
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
const configuration = require('./configuration');
|
2
|
+
|
3
|
+
const validColours = [
|
4
|
+
'white',
|
5
|
+
'grey',
|
6
|
+
'black',
|
7
|
+
'blue',
|
8
|
+
'cyan',
|
9
|
+
'green',
|
10
|
+
'magenta',
|
11
|
+
'red',
|
12
|
+
'yellow',
|
13
|
+
];
|
14
|
+
|
15
|
+
class Level {
|
16
|
+
constructor(level, levelStr, colour) {
|
17
|
+
this.level = level;
|
18
|
+
this.levelStr = levelStr;
|
19
|
+
this.colour = colour;
|
20
|
+
}
|
21
|
+
|
22
|
+
toString() {
|
23
|
+
return this.levelStr;
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* converts given String to corresponding Level
|
28
|
+
* @param {(Level|string)} sArg -- String value of Level OR Log4js.Level
|
29
|
+
* @param {Level} [defaultLevel] -- default Level, if no String representation
|
30
|
+
* @return {Level}
|
31
|
+
*/
|
32
|
+
static getLevel(sArg, defaultLevel) {
|
33
|
+
if (!sArg) {
|
34
|
+
return defaultLevel;
|
35
|
+
}
|
36
|
+
|
37
|
+
if (sArg instanceof Level) {
|
38
|
+
return sArg;
|
39
|
+
}
|
40
|
+
|
41
|
+
// a json-serialised level won't be an instance of Level (see issue #768)
|
42
|
+
if (sArg instanceof Object && sArg.levelStr) {
|
43
|
+
sArg = sArg.levelStr;
|
44
|
+
}
|
45
|
+
|
46
|
+
return Level[sArg.toString().toUpperCase()] || defaultLevel;
|
47
|
+
}
|
48
|
+
|
49
|
+
static addLevels(customLevels) {
|
50
|
+
if (customLevels) {
|
51
|
+
const levels = Object.keys(customLevels);
|
52
|
+
levels.forEach((l) => {
|
53
|
+
const levelStr = l.toUpperCase();
|
54
|
+
Level[levelStr] = new Level(
|
55
|
+
customLevels[l].value,
|
56
|
+
levelStr,
|
57
|
+
customLevels[l].colour
|
58
|
+
);
|
59
|
+
const existingLevelIndex = Level.levels.findIndex(
|
60
|
+
(lvl) => lvl.levelStr === levelStr
|
61
|
+
);
|
62
|
+
if (existingLevelIndex > -1) {
|
63
|
+
Level.levels[existingLevelIndex] = Level[levelStr];
|
64
|
+
} else {
|
65
|
+
Level.levels.push(Level[levelStr]);
|
66
|
+
}
|
67
|
+
});
|
68
|
+
Level.levels.sort((a, b) => a.level - b.level);
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
isLessThanOrEqualTo(otherLevel) {
|
73
|
+
if (typeof otherLevel === 'string') {
|
74
|
+
otherLevel = Level.getLevel(otherLevel);
|
75
|
+
}
|
76
|
+
return this.level <= otherLevel.level;
|
77
|
+
}
|
78
|
+
|
79
|
+
isGreaterThanOrEqualTo(otherLevel) {
|
80
|
+
if (typeof otherLevel === 'string') {
|
81
|
+
otherLevel = Level.getLevel(otherLevel);
|
82
|
+
}
|
83
|
+
return this.level >= otherLevel.level;
|
84
|
+
}
|
85
|
+
|
86
|
+
isEqualTo(otherLevel) {
|
87
|
+
if (typeof otherLevel === 'string') {
|
88
|
+
otherLevel = Level.getLevel(otherLevel);
|
89
|
+
}
|
90
|
+
return this.level === otherLevel.level;
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
Level.levels = [];
|
95
|
+
Level.addLevels({
|
96
|
+
ALL: { value: Number.MIN_VALUE, colour: 'grey' },
|
97
|
+
TRACE: { value: 5000, colour: 'blue' },
|
98
|
+
DEBUG: { value: 10000, colour: 'cyan' },
|
99
|
+
INFO: { value: 20000, colour: 'green' },
|
100
|
+
WARN: { value: 30000, colour: 'yellow' },
|
101
|
+
ERROR: { value: 40000, colour: 'red' },
|
102
|
+
FATAL: { value: 50000, colour: 'magenta' },
|
103
|
+
MARK: { value: 9007199254740992, colour: 'grey' }, // 2^53
|
104
|
+
OFF: { value: Number.MAX_VALUE, colour: 'grey' },
|
105
|
+
});
|
106
|
+
|
107
|
+
configuration.addListener((config) => {
|
108
|
+
const levelConfig = config.levels;
|
109
|
+
if (levelConfig) {
|
110
|
+
configuration.throwExceptionIf(
|
111
|
+
config,
|
112
|
+
configuration.not(configuration.anObject(levelConfig)),
|
113
|
+
'levels must be an object'
|
114
|
+
);
|
115
|
+
const newLevels = Object.keys(levelConfig);
|
116
|
+
newLevels.forEach((l) => {
|
117
|
+
configuration.throwExceptionIf(
|
118
|
+
config,
|
119
|
+
configuration.not(configuration.validIdentifier(l)),
|
120
|
+
`level name "${l}" is not a valid identifier (must start with a letter, only contain A-Z,a-z,0-9,_)`
|
121
|
+
);
|
122
|
+
configuration.throwExceptionIf(
|
123
|
+
config,
|
124
|
+
configuration.not(configuration.anObject(levelConfig[l])),
|
125
|
+
`level "${l}" must be an object`
|
126
|
+
);
|
127
|
+
configuration.throwExceptionIf(
|
128
|
+
config,
|
129
|
+
configuration.not(levelConfig[l].value),
|
130
|
+
`level "${l}" must have a 'value' property`
|
131
|
+
);
|
132
|
+
configuration.throwExceptionIf(
|
133
|
+
config,
|
134
|
+
configuration.not(configuration.anInteger(levelConfig[l].value)),
|
135
|
+
`level "${l}".value must have an integer value`
|
136
|
+
);
|
137
|
+
configuration.throwExceptionIf(
|
138
|
+
config,
|
139
|
+
configuration.not(levelConfig[l].colour),
|
140
|
+
`level "${l}" must have a 'colour' property`
|
141
|
+
);
|
142
|
+
configuration.throwExceptionIf(
|
143
|
+
config,
|
144
|
+
configuration.not(validColours.indexOf(levelConfig[l].colour) > -1),
|
145
|
+
`level "${l}".colour must be one of ${validColours.join(', ')}`
|
146
|
+
);
|
147
|
+
});
|
148
|
+
}
|
149
|
+
});
|
150
|
+
|
151
|
+
configuration.addListener((config) => {
|
152
|
+
Level.addLevels(config.levels);
|
153
|
+
});
|
154
|
+
|
155
|
+
module.exports = Level;
|