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.

@@ -0,0 +1,258 @@
1
+ const debug = require('debug')('log4js:fileSync');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const os = require('os');
5
+
6
+ const eol = os.EOL;
7
+
8
+ function touchFile(file, options) {
9
+ // attempt to create the directory
10
+ const mkdir = (dir) => {
11
+ try {
12
+ return fs.mkdirSync(dir, { recursive: true });
13
+ } catch (e) {
14
+ // backward-compatible fs.mkdirSync for nodejs pre-10.12.0 (without recursive option)
15
+ // recursive creation of parent first
16
+ if (e.code === 'ENOENT') {
17
+ mkdir(path.dirname(dir));
18
+ return mkdir(dir);
19
+ }
20
+
21
+ // throw error for all except EEXIST and EROFS (read-only filesystem)
22
+ if (e.code !== 'EEXIST' && e.code !== 'EROFS') {
23
+ throw e;
24
+ }
25
+
26
+ // EEXIST: throw if file and not directory
27
+ // EROFS : throw if directory not found
28
+ else {
29
+ try {
30
+ if (fs.statSync(dir).isDirectory()) {
31
+ return dir;
32
+ }
33
+ throw e;
34
+ } catch (err) {
35
+ throw e;
36
+ }
37
+ }
38
+ }
39
+ };
40
+ mkdir(path.dirname(file));
41
+
42
+ // try to throw EISDIR, EROFS, EACCES
43
+ fs.appendFileSync(file, '', { mode: options.mode, flag: options.flags });
44
+ }
45
+
46
+ class RollingFileSync {
47
+ constructor(filename, maxLogSize, backups, options) {
48
+ debug('In RollingFileStream');
49
+
50
+ if (maxLogSize < 0) {
51
+ throw new Error(`maxLogSize (${maxLogSize}) should be > 0`);
52
+ }
53
+
54
+ this.filename = filename;
55
+ this.size = maxLogSize;
56
+ this.backups = backups;
57
+ this.options = options;
58
+ this.currentSize = 0;
59
+
60
+ function currentFileSize(file) {
61
+ let fileSize = 0;
62
+
63
+ try {
64
+ fileSize = fs.statSync(file).size;
65
+ } catch (e) {
66
+ // file does not exist
67
+ touchFile(file, options);
68
+ }
69
+ return fileSize;
70
+ }
71
+
72
+ this.currentSize = currentFileSize(this.filename);
73
+ }
74
+
75
+ shouldRoll() {
76
+ debug(
77
+ 'should roll with current size %d, and max size %d',
78
+ this.currentSize,
79
+ this.size
80
+ );
81
+ return this.currentSize >= this.size;
82
+ }
83
+
84
+ roll(filename) {
85
+ const that = this;
86
+ const nameMatcher = new RegExp(`^${path.basename(filename)}`);
87
+
88
+ function justTheseFiles(item) {
89
+ return nameMatcher.test(item);
90
+ }
91
+
92
+ function index(filename_) {
93
+ return (
94
+ parseInt(filename_.slice(`${path.basename(filename)}.`.length), 10) || 0
95
+ );
96
+ }
97
+
98
+ function byIndex(a, b) {
99
+ return index(a) - index(b);
100
+ }
101
+
102
+ function increaseFileIndex(fileToRename) {
103
+ const idx = index(fileToRename);
104
+ debug(`Index of ${fileToRename} is ${idx}`);
105
+ if (that.backups === 0) {
106
+ fs.truncateSync(filename, 0);
107
+ } else if (idx < that.backups) {
108
+ // on windows, you can get a EEXIST error if you rename a file to an existing file
109
+ // so, we'll try to delete the file we're renaming to first
110
+ try {
111
+ fs.unlinkSync(`${filename}.${idx + 1}`);
112
+ } catch (e) {
113
+ // ignore err: if we could not delete, it's most likely that it doesn't exist
114
+ }
115
+
116
+ debug(`Renaming ${fileToRename} -> ${filename}.${idx + 1}`);
117
+ fs.renameSync(
118
+ path.join(path.dirname(filename), fileToRename),
119
+ `${filename}.${idx + 1}`
120
+ );
121
+ }
122
+ }
123
+
124
+ function renameTheFiles() {
125
+ // roll the backups (rename file.n to file.n+1, where n <= numBackups)
126
+ debug('Renaming the old files');
127
+
128
+ const files = fs.readdirSync(path.dirname(filename));
129
+ files
130
+ .filter(justTheseFiles)
131
+ .sort(byIndex)
132
+ .reverse()
133
+ .forEach(increaseFileIndex);
134
+ }
135
+
136
+ debug('Rolling, rolling, rolling');
137
+ renameTheFiles();
138
+ }
139
+
140
+ // eslint-disable-next-line no-unused-vars
141
+ write(chunk, encoding) {
142
+ const that = this;
143
+
144
+ function writeTheChunk() {
145
+ debug('writing the chunk to the file');
146
+ that.currentSize += chunk.length;
147
+ fs.appendFileSync(that.filename, chunk);
148
+ }
149
+
150
+ debug('in write');
151
+
152
+ if (this.shouldRoll()) {
153
+ this.currentSize = 0;
154
+ this.roll(this.filename);
155
+ }
156
+
157
+ writeTheChunk();
158
+ }
159
+ }
160
+
161
+ /**
162
+ * File Appender writing the logs to a text file. Supports rolling of logs by size.
163
+ *
164
+ * @param file the file log messages will be written to
165
+ * @param layout a function that takes a logevent and returns a string
166
+ * (defaults to basicLayout).
167
+ * @param logSize - the maximum size (in bytes) for a log file,
168
+ * if not provided then logs won't be rotated.
169
+ * @param numBackups - the number of log files to keep after logSize
170
+ * has been reached (default 5)
171
+ * @param options - options to be passed to the underlying stream
172
+ * @param timezoneOffset - optional timezone offset in minutes (default system local)
173
+ */
174
+ function fileAppender(
175
+ file,
176
+ layout,
177
+ logSize,
178
+ numBackups,
179
+ options,
180
+ timezoneOffset
181
+ ) {
182
+ if (typeof file !== 'string' || file.length === 0) {
183
+ throw new Error(`Invalid filename: ${file}`);
184
+ } else if (file.endsWith(path.sep)) {
185
+ throw new Error(`Filename is a directory: ${file}`);
186
+ } else if (file.indexOf(`~${path.sep}`) === 0) {
187
+ // handle ~ expansion: https://github.com/nodejs/node/issues/684
188
+ // exclude ~ and ~filename as these can be valid files
189
+ file = file.replace('~', os.homedir());
190
+ }
191
+ file = path.normalize(file);
192
+ numBackups = !numBackups && numBackups !== 0 ? 5 : numBackups;
193
+
194
+ debug(
195
+ 'Creating fileSync appender (',
196
+ file,
197
+ ', ',
198
+ logSize,
199
+ ', ',
200
+ numBackups,
201
+ ', ',
202
+ options,
203
+ ', ',
204
+ timezoneOffset,
205
+ ')'
206
+ );
207
+
208
+ function openTheStream(filePath, fileSize, numFiles) {
209
+ let stream;
210
+
211
+ if (fileSize) {
212
+ stream = new RollingFileSync(filePath, fileSize, numFiles, options);
213
+ } else {
214
+ stream = ((f) => {
215
+ // touch the file to apply flags (like w to truncate the file)
216
+ touchFile(f, options);
217
+
218
+ return {
219
+ write(data) {
220
+ fs.appendFileSync(f, data);
221
+ },
222
+ };
223
+ })(filePath);
224
+ }
225
+
226
+ return stream;
227
+ }
228
+
229
+ const logFile = openTheStream(file, logSize, numBackups);
230
+
231
+ return (loggingEvent) => {
232
+ logFile.write(layout(loggingEvent, timezoneOffset) + eol);
233
+ };
234
+ }
235
+
236
+ function configure(config, layouts) {
237
+ let layout = layouts.basicLayout;
238
+ if (config.layout) {
239
+ layout = layouts.layout(config.layout.type, config.layout);
240
+ }
241
+
242
+ const options = {
243
+ flags: config.flags || 'a',
244
+ encoding: config.encoding || 'utf8',
245
+ mode: config.mode || 0o600,
246
+ };
247
+
248
+ return fileAppender(
249
+ config.filename,
250
+ layout,
251
+ config.maxLogSize,
252
+ config.backups,
253
+ options,
254
+ config.timezoneOffset
255
+ );
256
+ }
257
+
258
+ module.exports.configure = configure;
File without changes
@@ -0,0 +1,182 @@
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
+ coreAppenders.set('tcp', require('./tcp'));
21
+
22
+ const appenders = new Map();
23
+
24
+ const tryLoading = (modulePath, config) => {
25
+ let resolvedPath;
26
+ try {
27
+ const modulePathCJS = `${modulePath}.cjs`;
28
+ resolvedPath = require.resolve(modulePathCJS);
29
+ debug('Loading module from ', modulePathCJS);
30
+ } catch (e) {
31
+ resolvedPath = modulePath;
32
+ debug('Loading module from ', modulePath);
33
+ }
34
+ try {
35
+ // eslint-disable-next-line global-require, import/no-dynamic-require
36
+ return require(resolvedPath);
37
+ } catch (e) {
38
+ // if the module was found, and we still got an error, then raise it
39
+ configuration.throwExceptionIf(
40
+ config,
41
+ e.code !== 'MODULE_NOT_FOUND',
42
+ `appender "${modulePath}" could not be loaded (error was: ${e})`
43
+ );
44
+ return undefined;
45
+ }
46
+ };
47
+
48
+ const loadAppenderModule = (type, config) =>
49
+ coreAppenders.get(type) ||
50
+ tryLoading(`./${type}`, config) ||
51
+ tryLoading(type, config) ||
52
+ (require.main &&
53
+ require.main.filename &&
54
+ tryLoading(path.join(path.dirname(require.main.filename), type), config)) ||
55
+ tryLoading(path.join(process.cwd(), type), config);
56
+
57
+ const appendersLoading = new Set();
58
+
59
+ const getAppender = (name, config) => {
60
+ if (appenders.has(name)) return appenders.get(name);
61
+ if (!config.appenders[name]) return false;
62
+ if (appendersLoading.has(name))
63
+ throw new Error(`Dependency loop detected for appender ${name}.`);
64
+ appendersLoading.add(name);
65
+
66
+ debug(`Creating appender ${name}`);
67
+ // eslint-disable-next-line no-use-before-define
68
+ const appender = createAppender(name, config);
69
+ appendersLoading.delete(name);
70
+ appenders.set(name, appender);
71
+ return appender;
72
+ };
73
+
74
+ const createAppender = (name, config) => {
75
+ const appenderConfig = config.appenders[name];
76
+ const appenderModule = appenderConfig.type.configure
77
+ ? appenderConfig.type
78
+ : loadAppenderModule(appenderConfig.type, config);
79
+ configuration.throwExceptionIf(
80
+ config,
81
+ configuration.not(appenderModule),
82
+ `appender "${name}" is not valid (type "${appenderConfig.type}" could not be found)`
83
+ );
84
+ if (appenderModule.appender) {
85
+ process.emitWarning(
86
+ `Appender ${appenderConfig.type} exports an appender function.`,
87
+ 'DeprecationWarning',
88
+ 'log4js-node-DEP0001'
89
+ );
90
+ debug(
91
+ '[log4js-node-DEP0001]',
92
+ `DEPRECATION: Appender ${appenderConfig.type} exports an appender function.`
93
+ );
94
+ }
95
+ if (appenderModule.shutdown) {
96
+ process.emitWarning(
97
+ `Appender ${appenderConfig.type} exports a shutdown function.`,
98
+ 'DeprecationWarning',
99
+ 'log4js-node-DEP0002'
100
+ );
101
+ debug(
102
+ '[log4js-node-DEP0002]',
103
+ `DEPRECATION: Appender ${appenderConfig.type} exports a shutdown function.`
104
+ );
105
+ }
106
+
107
+ debug(`${name}: clustering.isMaster ? ${clustering.isMaster()}`);
108
+ debug(
109
+ // eslint-disable-next-line global-require
110
+ `${name}: appenderModule is ${require('util').inspect(appenderModule)}`
111
+ );
112
+ return clustering.onlyOnMaster(
113
+ () => {
114
+ debug(
115
+ `calling appenderModule.configure for ${name} / ${appenderConfig.type}`
116
+ );
117
+ return appenderModule.configure(
118
+ adapters.modifyConfig(appenderConfig),
119
+ layouts,
120
+ (appender) => getAppender(appender, config),
121
+ levels
122
+ );
123
+ },
124
+ /* istanbul ignore next: fn never gets called by non-master yet needed to pass config validation */ () => {}
125
+ );
126
+ };
127
+
128
+ const setup = (config) => {
129
+ appenders.clear();
130
+ appendersLoading.clear();
131
+ if (!config) {
132
+ return;
133
+ }
134
+
135
+ const usedAppenders = [];
136
+ Object.values(config.categories).forEach((category) => {
137
+ usedAppenders.push(...category.appenders);
138
+ });
139
+ Object.keys(config.appenders).forEach((name) => {
140
+ // dodgy hard-coding of special case for tcp-server and multiprocess which may not have
141
+ // any categories associated with it, but needs to be started up anyway
142
+ if (
143
+ usedAppenders.includes(name) ||
144
+ config.appenders[name].type === 'tcp-server' ||
145
+ config.appenders[name].type === 'multiprocess'
146
+ ) {
147
+ getAppender(name, config);
148
+ }
149
+ });
150
+ };
151
+
152
+ const init = () => {
153
+ setup();
154
+ };
155
+ init();
156
+
157
+ configuration.addListener((config) => {
158
+ configuration.throwExceptionIf(
159
+ config,
160
+ configuration.not(configuration.anObject(config.appenders)),
161
+ 'must have a property "appenders" of type object.'
162
+ );
163
+ const appenderNames = Object.keys(config.appenders);
164
+ configuration.throwExceptionIf(
165
+ config,
166
+ configuration.not(appenderNames.length),
167
+ 'must define at least one appender.'
168
+ );
169
+
170
+ appenderNames.forEach((name) => {
171
+ configuration.throwExceptionIf(
172
+ config,
173
+ configuration.not(config.appenders[name].type),
174
+ `appender "${name}" is not valid (must be an object with property "type")`
175
+ );
176
+ });
177
+ });
178
+
179
+ configuration.addListener(setup);
180
+
181
+ module.exports = appenders;
182
+ module.exports.init = init;
@@ -0,0 +1,20 @@
1
+ function logLevelFilter(minLevelString, maxLevelString, appender, levels) {
2
+ const minLevel = levels.getLevel(minLevelString);
3
+ const maxLevel = levels.getLevel(maxLevelString, levels.FATAL);
4
+ return (logEvent) => {
5
+ const eventLevel = logEvent.level;
6
+ if (
7
+ minLevel.isLessThanOrEqualTo(eventLevel) &&
8
+ maxLevel.isGreaterThanOrEqualTo(eventLevel)
9
+ ) {
10
+ appender(logEvent);
11
+ }
12
+ };
13
+ }
14
+
15
+ function configure(config, layouts, findAppender, levels) {
16
+ const appender = findAppender(config.appender);
17
+ return logLevelFilter(config.level, config.maxLevel, appender, levels);
18
+ }
19
+
20
+ module.exports.configure = configure;
@@ -0,0 +1,91 @@
1
+ const debug = require('debug')('log4js:multiFile');
2
+ const path = require('path');
3
+ const fileAppender = require('./file');
4
+
5
+ const findFileKey = (property, event) =>
6
+ event[property] || event.context[property];
7
+
8
+ module.exports.configure = (config, layouts) => {
9
+ debug('Creating a multi-file appender');
10
+ const files = new Map();
11
+ const timers = new Map();
12
+
13
+ function checkForTimeout(fileKey) {
14
+ const timer = timers.get(fileKey);
15
+ const app = files.get(fileKey);
16
+ /* istanbul ignore else: failsafe */
17
+ if (timer && app) {
18
+ if (Date.now() - timer.lastUsed > timer.timeout) {
19
+ debug('%s not used for > %d ms => close', fileKey, timer.timeout);
20
+ clearInterval(timer.interval);
21
+ timers.delete(fileKey);
22
+ files.delete(fileKey);
23
+ app.shutdown((err) => {
24
+ if (err) {
25
+ debug('ignore error on file shutdown: %s', err.message);
26
+ }
27
+ });
28
+ }
29
+ } else {
30
+ // will never get here as files and timers are coupled to be added and deleted at same place
31
+ debug('timer or app does not exist');
32
+ }
33
+ }
34
+
35
+ const appender = (logEvent) => {
36
+ const fileKey = findFileKey(config.property, logEvent);
37
+ debug('fileKey for property ', config.property, ' is ', fileKey);
38
+ if (fileKey) {
39
+ let file = files.get(fileKey);
40
+ debug('existing file appender is ', file);
41
+ if (!file) {
42
+ debug('creating new file appender');
43
+ config.filename = path.join(config.base, fileKey + config.extension);
44
+ file = fileAppender.configure(config, layouts);
45
+ files.set(fileKey, file);
46
+ if (config.timeout) {
47
+ debug('creating new timer');
48
+ timers.set(fileKey, {
49
+ timeout: config.timeout,
50
+ lastUsed: Date.now(),
51
+ interval: setInterval(
52
+ checkForTimeout.bind(null, fileKey),
53
+ config.timeout
54
+ ),
55
+ });
56
+ }
57
+ } else if (config.timeout) {
58
+ debug('%s extending activity', fileKey);
59
+ timers.get(fileKey).lastUsed = Date.now();
60
+ }
61
+
62
+ file(logEvent);
63
+ } else {
64
+ debug('No fileKey for logEvent, quietly ignoring this log event');
65
+ }
66
+ };
67
+
68
+ appender.shutdown = (cb) => {
69
+ let shutdownFunctions = files.size;
70
+ if (shutdownFunctions <= 0) {
71
+ cb();
72
+ }
73
+ let error;
74
+ timers.forEach((timer, fileKey) => {
75
+ debug('clearing timer for ', fileKey);
76
+ clearInterval(timer.interval);
77
+ });
78
+ files.forEach((app, fileKey) => {
79
+ debug('calling shutdown for ', fileKey);
80
+ app.shutdown((err) => {
81
+ error = error || err;
82
+ shutdownFunctions -= 1;
83
+ if (shutdownFunctions <= 0) {
84
+ cb(error);
85
+ }
86
+ });
87
+ });
88
+ };
89
+
90
+ return appender;
91
+ };