@contrast/agent 4.16.2 → 4.17.0
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.
- package/esm.mjs +21 -11
- package/lib/assess/policy/propagators.json +4 -0
- package/lib/assess/policy/signatures.json +5 -0
- package/lib/assess/propagators/fastify-static/allowed-path.js +85 -0
- package/lib/contrast.js +1 -1
- package/lib/core/rewrite/index.js +2 -2
- package/node_modules/@colors/colors/LICENSE +26 -0
- package/node_modules/@colors/colors/README.md +219 -0
- package/node_modules/@colors/colors/examples/normal-usage.js +83 -0
- package/node_modules/@colors/colors/examples/safe-string.js +80 -0
- package/node_modules/@colors/colors/index.d.ts +136 -0
- package/node_modules/@colors/colors/lib/colors.js +211 -0
- package/node_modules/@colors/colors/lib/custom/trap.js +46 -0
- package/node_modules/@colors/colors/lib/custom/zalgo.js +110 -0
- package/node_modules/@colors/colors/lib/extendStringPrototype.js +110 -0
- package/node_modules/@colors/colors/lib/index.js +13 -0
- package/node_modules/@colors/colors/lib/maps/america.js +10 -0
- package/node_modules/@colors/colors/lib/maps/rainbow.js +12 -0
- package/node_modules/@colors/colors/lib/maps/random.js +11 -0
- package/node_modules/@colors/colors/lib/maps/zebra.js +5 -0
- package/node_modules/@colors/colors/lib/styles.js +95 -0
- package/node_modules/@colors/colors/lib/system/has-flag.js +35 -0
- package/node_modules/@colors/colors/lib/system/supports-colors.js +151 -0
- package/node_modules/@colors/colors/package.json +49 -0
- package/node_modules/@colors/colors/safe.d.ts +48 -0
- package/node_modules/@colors/colors/safe.js +10 -0
- package/node_modules/@colors/colors/themes/generic-logging.js +12 -0
- package/node_modules/@dabh/diagnostics/README.md +16 -16
- package/node_modules/@dabh/diagnostics/package.json +9 -9
- package/node_modules/color/README.md +9 -0
- package/node_modules/color/index.js +4 -1
- package/node_modules/color/package.json +8 -8
- package/node_modules/color-string/README.md +6 -2
- package/node_modules/color-string/index.js +21 -13
- package/node_modules/color-string/package.json +4 -4
- package/node_modules/colorspace/package.json +5 -5
- package/node_modules/fecha/dist/fecha.min.js +1 -1
- package/node_modules/fecha/dist/fecha.min.js.map +1 -0
- package/node_modules/fecha/lib/fecha.d.ts +2 -2
- package/node_modules/fecha/lib/fecha.js +35 -18
- package/node_modules/fecha/lib/fecha.js.map +1 -0
- package/node_modules/fecha/lib/fecha.umd.js +35 -18
- package/node_modules/fecha/lib/fecha.umd.js.map +1 -0
- package/node_modules/fecha/package.json +9 -10
- package/node_modules/fecha/src/fecha.ts +524 -0
- package/node_modules/logform/.eslintrc +1 -1
- package/node_modules/logform/CHANGELOG.md +24 -0
- package/node_modules/logform/README.md +15 -17
- package/node_modules/logform/browser.js +3 -1
- package/node_modules/logform/cli.js +1 -1
- package/node_modules/logform/colorize.js +2 -2
- package/node_modules/logform/dist/browser.js +8 -2
- package/node_modules/logform/dist/cli.js +2 -2
- package/node_modules/logform/dist/colorize.js +5 -5
- package/node_modules/logform/dist/errors.js +1 -1
- package/node_modules/logform/dist/format.js +9 -5
- package/node_modules/logform/dist/index.js +53 -20
- package/node_modules/logform/dist/json.js +7 -6
- package/node_modules/logform/dist/logstash.js +1 -1
- package/node_modules/logform/dist/pad-levels.js +4 -4
- package/node_modules/logform/dist/printf.js +1 -1
- package/node_modules/logform/dist/simple.js +1 -1
- package/node_modules/logform/dist/splat.js +2 -2
- package/node_modules/logform/dist/uncolorize.js +1 -1
- package/node_modules/logform/errors.js +1 -1
- package/node_modules/logform/index.d.ts +36 -0
- package/node_modules/logform/index.js +19 -20
- package/node_modules/logform/json.js +7 -7
- package/node_modules/logform/logstash.js +1 -1
- package/node_modules/logform/package.json +10 -9
- package/node_modules/logform/simple.js +1 -1
- package/node_modules/logform/uncolorize.js +1 -1
- package/node_modules/safe-stable-stringify/CHANGELOG.md +75 -0
- package/node_modules/safe-stable-stringify/LICENSE +21 -0
- package/node_modules/safe-stable-stringify/esm/package.json +4 -0
- package/node_modules/safe-stable-stringify/esm/wrapper.js +6 -0
- package/node_modules/safe-stable-stringify/index.d.ts +18 -0
- package/node_modules/safe-stable-stringify/index.js +618 -0
- package/node_modules/safe-stable-stringify/package.json +69 -0
- package/node_modules/safe-stable-stringify/readme.md +170 -0
- package/node_modules/safe-stable-stringify/tsconfig.json +22 -0
- package/node_modules/winston/LICENSE +18 -18
- package/node_modules/winston/README.md +1231 -1230
- package/node_modules/winston/dist/winston/common.js +10 -10
- package/node_modules/winston/dist/winston/config/index.js +17 -17
- package/node_modules/winston/dist/winston/container.js +46 -46
- package/node_modules/winston/dist/winston/create-logger.js +28 -24
- package/node_modules/winston/dist/winston/exception-handler.js +49 -49
- package/node_modules/winston/dist/winston/exception-stream.js +27 -27
- package/node_modules/winston/dist/winston/logger.js +152 -138
- package/node_modules/winston/dist/winston/profiler.js +22 -22
- package/node_modules/winston/dist/winston/rejection-handler.js +54 -54
- package/node_modules/winston/dist/winston/tail-file.js +14 -14
- package/node_modules/winston/dist/winston/transports/console.js +31 -31
- package/node_modules/winston/dist/winston/transports/file.js +101 -101
- package/node_modules/winston/dist/winston/transports/http.js +119 -41
- package/node_modules/winston/dist/winston/transports/index.js +17 -17
- package/node_modules/winston/dist/winston/transports/stream.js +24 -24
- package/node_modules/winston/dist/winston.js +91 -97
- package/node_modules/winston/index.d.ts +213 -193
- package/node_modules/winston/lib/winston/common.js +61 -61
- package/node_modules/winston/lib/winston/config/index.d.ts +99 -98
- package/node_modules/winston/lib/winston/config/index.js +35 -35
- package/node_modules/winston/lib/winston/container.js +114 -114
- package/node_modules/winston/lib/winston/create-logger.js +104 -104
- package/node_modules/winston/lib/winston/exception-handler.js +245 -245
- package/node_modules/winston/lib/winston/exception-stream.js +54 -54
- package/node_modules/winston/lib/winston/logger.js +676 -667
- package/node_modules/winston/lib/winston/profiler.js +51 -51
- package/node_modules/winston/lib/winston/rejection-handler.js +251 -251
- package/node_modules/winston/lib/winston/tail-file.js +124 -124
- package/node_modules/winston/lib/winston/transports/console.js +117 -117
- package/node_modules/winston/lib/winston/transports/file.js +695 -695
- package/node_modules/winston/lib/winston/transports/http.js +267 -202
- package/node_modules/winston/lib/winston/transports/index.d.ts +103 -100
- package/node_modules/winston/lib/winston/transports/index.js +56 -56
- package/node_modules/winston/lib/winston/transports/stream.js +63 -63
- package/node_modules/winston/lib/winston.js +176 -182
- package/node_modules/winston/node_modules/winston-transport/.babelrc +3 -0
- package/node_modules/winston/node_modules/winston-transport/.eslintrc +7 -0
- package/node_modules/winston/node_modules/winston-transport/.gitattributes +1 -0
- package/node_modules/winston/node_modules/winston-transport/.nyc_output/c3d7ddb9-cc26-466b-a4f6-993ad69e86f6.json +1 -0
- package/node_modules/winston/node_modules/winston-transport/.nyc_output/processinfo/c3d7ddb9-cc26-466b-a4f6-993ad69e86f6.json +1 -0
- package/node_modules/winston/node_modules/winston-transport/.nyc_output/processinfo/index.json +1 -0
- package/node_modules/winston/node_modules/winston-transport/CHANGELOG.md +126 -0
- package/node_modules/winston/node_modules/winston-transport/LICENSE +22 -0
- package/node_modules/winston/node_modules/winston-transport/README.md +50 -0
- package/node_modules/winston/node_modules/winston-transport/dist/index.js +215 -0
- package/node_modules/winston/node_modules/winston-transport/dist/legacy.js +116 -0
- package/node_modules/winston/node_modules/winston-transport/index.d.ts +39 -0
- package/node_modules/winston/node_modules/winston-transport/index.js +215 -0
- package/node_modules/winston/node_modules/winston-transport/legacy.js +119 -0
- package/node_modules/winston/node_modules/winston-transport/package.json +56 -0
- package/node_modules/winston/package.json +76 -75
- package/package.json +6 -4
- package/node_modules/@dabh/diagnostics/example.png +0 -0
- package/node_modules/color-string/CHANGELOG.md +0 -18
- package/node_modules/colorspace/test.js +0 -14
- package/node_modules/fecha/CHANGELOG.md +0 -67
- package/node_modules/logform/.travis.yml +0 -17
- package/node_modules/winston/CHANGELOG.md +0 -560
|
@@ -1,695 +1,695 @@
|
|
|
1
|
-
/* eslint-disable complexity,max-statements */
|
|
2
|
-
/**
|
|
3
|
-
* file.js: Transport for outputting to a local log file.
|
|
4
|
-
*
|
|
5
|
-
* (C) 2010 Charlie Robbins
|
|
6
|
-
* MIT LICENCE
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
'use strict';
|
|
10
|
-
|
|
11
|
-
const fs = require('fs');
|
|
12
|
-
const path = require('path');
|
|
13
|
-
const asyncSeries = require('async/series');
|
|
14
|
-
const zlib = require('zlib');
|
|
15
|
-
const { MESSAGE } = require('triple-beam');
|
|
16
|
-
const { Stream, PassThrough } = require('readable-stream');
|
|
17
|
-
const TransportStream = require('winston-transport');
|
|
18
|
-
const debug = require('@dabh/diagnostics')('winston:file');
|
|
19
|
-
const os = require('os');
|
|
20
|
-
const tailFile = require('../tail-file');
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Transport for outputting to a local log file.
|
|
24
|
-
* @type {File}
|
|
25
|
-
* @extends {TransportStream}
|
|
26
|
-
*/
|
|
27
|
-
module.exports = class File extends TransportStream {
|
|
28
|
-
/**
|
|
29
|
-
* Constructor function for the File transport object responsible for
|
|
30
|
-
* persisting log messages and metadata to one or more files.
|
|
31
|
-
* @param {Object} options - Options for this instance.
|
|
32
|
-
*/
|
|
33
|
-
constructor(options = {}) {
|
|
34
|
-
super(options);
|
|
35
|
-
|
|
36
|
-
// Expose the name of this Transport on the prototype.
|
|
37
|
-
this.name = options.name || 'file';
|
|
38
|
-
|
|
39
|
-
// Helper function which throws an `Error` in the event that any of the
|
|
40
|
-
// rest of the arguments is present in `options`.
|
|
41
|
-
function throwIf(target, ...args) {
|
|
42
|
-
args.slice(1).forEach(name => {
|
|
43
|
-
if (options[name]) {
|
|
44
|
-
throw new Error(`Cannot set ${name} and ${target} together`);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Setup the base stream that always gets piped to to handle buffering.
|
|
50
|
-
this._stream = new PassThrough();
|
|
51
|
-
this._stream.setMaxListeners(30);
|
|
52
|
-
|
|
53
|
-
// Bind this context for listener methods.
|
|
54
|
-
this._onError = this._onError.bind(this);
|
|
55
|
-
|
|
56
|
-
if (options.filename || options.dirname) {
|
|
57
|
-
throwIf('filename or dirname', 'stream');
|
|
58
|
-
this._basename = this.filename = options.filename
|
|
59
|
-
? path.basename(options.filename)
|
|
60
|
-
: 'winston.log';
|
|
61
|
-
|
|
62
|
-
this.dirname = options.dirname || path.dirname(options.filename);
|
|
63
|
-
this.options = options.options || { flags: 'a' };
|
|
64
|
-
} else if (options.stream) {
|
|
65
|
-
// eslint-disable-next-line no-console
|
|
66
|
-
console.warn('options.stream will be removed in winston@4. Use winston.transports.Stream');
|
|
67
|
-
throwIf('stream', 'filename', 'maxsize');
|
|
68
|
-
this._dest = this._stream.pipe(this._setupStream(options.stream));
|
|
69
|
-
this.dirname = path.dirname(this._dest.path);
|
|
70
|
-
// We need to listen for drain events when write() returns false. This
|
|
71
|
-
// can make node mad at times.
|
|
72
|
-
} else {
|
|
73
|
-
throw new Error('Cannot log to file without filename or stream.');
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.maxsize = options.maxsize || null;
|
|
77
|
-
this.rotationFormat = options.rotationFormat || false;
|
|
78
|
-
this.zippedArchive = options.zippedArchive || false;
|
|
79
|
-
this.maxFiles = options.maxFiles || null;
|
|
80
|
-
this.eol = options.eol
|
|
81
|
-
this.tailable = options.tailable || false;
|
|
82
|
-
|
|
83
|
-
// Internal state variables representing the number of files this instance
|
|
84
|
-
// has created and the current size (in bytes) of the current logfile.
|
|
85
|
-
this._size = 0;
|
|
86
|
-
this._pendingSize = 0;
|
|
87
|
-
this._created = 0;
|
|
88
|
-
this._drain = false;
|
|
89
|
-
this._opening = false;
|
|
90
|
-
this._ending = false;
|
|
91
|
-
|
|
92
|
-
if (this.dirname) this._createLogDirIfNotExist(this.dirname);
|
|
93
|
-
this.open();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
finishIfEnding() {
|
|
97
|
-
if (this._ending) {
|
|
98
|
-
if (this._opening) {
|
|
99
|
-
this.once('open', () => {
|
|
100
|
-
this._stream.once('finish', () => this.emit('finish'));
|
|
101
|
-
setImmediate(() => this._stream.end());
|
|
102
|
-
});
|
|
103
|
-
} else {
|
|
104
|
-
this._stream.once('finish', () => this.emit('finish'));
|
|
105
|
-
setImmediate(() => this._stream.end());
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Core logging method exposed to Winston. Metadata is optional.
|
|
113
|
-
* @param {Object} info - TODO: add param description.
|
|
114
|
-
* @param {Function} callback - TODO: add param description.
|
|
115
|
-
* @returns {undefined}
|
|
116
|
-
*/
|
|
117
|
-
log(info, callback = () => {}) {
|
|
118
|
-
// Remark: (jcrugzz) What is necessary about this callback(null, true) now
|
|
119
|
-
// when thinking about 3.x? Should silent be handled in the base
|
|
120
|
-
// TransportStream _write method?
|
|
121
|
-
if (this.silent) {
|
|
122
|
-
callback();
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Output stream buffer is full and has asked us to wait for the drain event
|
|
127
|
-
if (this._drain) {
|
|
128
|
-
this._stream.once('drain', () => {
|
|
129
|
-
this._drain = false;
|
|
130
|
-
this.log(info, callback);
|
|
131
|
-
});
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
if (this._rotate) {
|
|
135
|
-
this._stream.once('rotate', () => {
|
|
136
|
-
this._rotate = false;
|
|
137
|
-
this.log(info, callback);
|
|
138
|
-
});
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// Grab the raw string and append the expected EOL.
|
|
143
|
-
const output = `${info[MESSAGE]}${this.eol}`;
|
|
144
|
-
const bytes = Buffer.byteLength(output);
|
|
145
|
-
|
|
146
|
-
// After we have written to the PassThrough check to see if we need
|
|
147
|
-
// to rotate to the next file.
|
|
148
|
-
//
|
|
149
|
-
// Remark: This gets called too early and does not depict when data
|
|
150
|
-
// has been actually flushed to disk.
|
|
151
|
-
function logged() {
|
|
152
|
-
this._size += bytes;
|
|
153
|
-
this._pendingSize -= bytes;
|
|
154
|
-
|
|
155
|
-
debug('logged %s %s', this._size, output);
|
|
156
|
-
this.emit('logged', info);
|
|
157
|
-
|
|
158
|
-
// Do not attempt to rotate files while opening
|
|
159
|
-
if (this._opening) {
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Check to see if we need to end the stream and create a new one.
|
|
164
|
-
if (!this._needsNewFile()) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// End the current stream, ensure it flushes and create a new one.
|
|
169
|
-
// This could potentially be optimized to not run a stat call but its
|
|
170
|
-
// the safest way since we are supporting `maxFiles`.
|
|
171
|
-
this._rotate = true;
|
|
172
|
-
this._endStream(() => this._rotateFile());
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Keep track of the pending bytes being written while files are opening
|
|
176
|
-
// in order to properly rotate the PassThrough this._stream when the file
|
|
177
|
-
// eventually does open.
|
|
178
|
-
this._pendingSize += bytes;
|
|
179
|
-
if (this._opening
|
|
180
|
-
&& !this.rotatedWhileOpening
|
|
181
|
-
&& this._needsNewFile(this._size + this._pendingSize)) {
|
|
182
|
-
this.rotatedWhileOpening = true;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const written = this._stream.write(output, logged.bind(this));
|
|
186
|
-
if (!written) {
|
|
187
|
-
this._drain = true;
|
|
188
|
-
this._stream.once('drain', () => {
|
|
189
|
-
this._drain = false;
|
|
190
|
-
callback();
|
|
191
|
-
});
|
|
192
|
-
} else {
|
|
193
|
-
callback(); // eslint-disable-line callback-return
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
debug('written', written, this._drain);
|
|
197
|
-
|
|
198
|
-
this.finishIfEnding();
|
|
199
|
-
|
|
200
|
-
return written;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
/**
|
|
204
|
-
* Query the transport. Options object is optional.
|
|
205
|
-
* @param {Object} options - Loggly-like query options for this instance.
|
|
206
|
-
* @param {function} callback - Continuation to respond to when complete.
|
|
207
|
-
* TODO: Refactor me.
|
|
208
|
-
*/
|
|
209
|
-
query(options, callback) {
|
|
210
|
-
if (typeof options === 'function') {
|
|
211
|
-
callback = options;
|
|
212
|
-
options = {};
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
options = normalizeQuery(options);
|
|
216
|
-
const file = path.join(this.dirname, this.filename);
|
|
217
|
-
let buff = '';
|
|
218
|
-
let results = [];
|
|
219
|
-
let row = 0;
|
|
220
|
-
|
|
221
|
-
const stream = fs.createReadStream(file, {
|
|
222
|
-
encoding: 'utf8'
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
stream.on('error', err => {
|
|
226
|
-
if (stream.readable) {
|
|
227
|
-
stream.destroy();
|
|
228
|
-
}
|
|
229
|
-
if (!callback) {
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
return err.code !== 'ENOENT' ? callback(err) : callback(null, results);
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
stream.on('data', data => {
|
|
237
|
-
data = (buff + data).split(/\n+/);
|
|
238
|
-
const l = data.length - 1;
|
|
239
|
-
let i = 0;
|
|
240
|
-
|
|
241
|
-
for (; i < l; i++) {
|
|
242
|
-
if (!options.start || row >= options.start) {
|
|
243
|
-
add(data[i]);
|
|
244
|
-
}
|
|
245
|
-
row++;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
buff = data[l];
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
stream.on('close', () => {
|
|
252
|
-
if (buff) {
|
|
253
|
-
add(buff, true);
|
|
254
|
-
}
|
|
255
|
-
if (options.order === 'desc') {
|
|
256
|
-
results = results.reverse();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// eslint-disable-next-line callback-return
|
|
260
|
-
if (callback) callback(null, results);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
function add(buff, attempt) {
|
|
264
|
-
try {
|
|
265
|
-
const log = JSON.parse(buff);
|
|
266
|
-
if (check(log)) {
|
|
267
|
-
push(log);
|
|
268
|
-
}
|
|
269
|
-
} catch (e) {
|
|
270
|
-
if (!attempt) {
|
|
271
|
-
stream.emit('error', e);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function push(log) {
|
|
277
|
-
if (
|
|
278
|
-
options.rows &&
|
|
279
|
-
results.length >= options.rows &&
|
|
280
|
-
options.order !== 'desc'
|
|
281
|
-
) {
|
|
282
|
-
if (stream.readable) {
|
|
283
|
-
stream.destroy();
|
|
284
|
-
}
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
if (options.fields) {
|
|
289
|
-
log = options.fields.reduce((obj, key) => {
|
|
290
|
-
obj[key] = log[key];
|
|
291
|
-
return obj;
|
|
292
|
-
}, {});
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (options.order === 'desc') {
|
|
296
|
-
if (results.length >= options.rows) {
|
|
297
|
-
results.shift();
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
results.push(log);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function check(log) {
|
|
304
|
-
if (!log) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
if (typeof log !== 'object') {
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
const time = new Date(log.timestamp);
|
|
313
|
-
if (
|
|
314
|
-
(options.from && time < options.from) ||
|
|
315
|
-
(options.until && time > options.until) ||
|
|
316
|
-
(options.level && options.level !== log.level)
|
|
317
|
-
) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
return true;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function normalizeQuery(options) {
|
|
325
|
-
options = options || {};
|
|
326
|
-
|
|
327
|
-
// limit
|
|
328
|
-
options.rows = options.rows || options.limit || 10;
|
|
329
|
-
|
|
330
|
-
// starting row offset
|
|
331
|
-
options.start = options.start || 0;
|
|
332
|
-
|
|
333
|
-
// now
|
|
334
|
-
options.until = options.until || new Date();
|
|
335
|
-
if (typeof options.until !== 'object') {
|
|
336
|
-
options.until = new Date(options.until);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// now - 24
|
|
340
|
-
options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
|
|
341
|
-
if (typeof options.from !== 'object') {
|
|
342
|
-
options.from = new Date(options.from);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// 'asc' or 'desc'
|
|
346
|
-
options.order = options.order || 'desc';
|
|
347
|
-
|
|
348
|
-
return options;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Returns a log stream for this transport. Options object is optional.
|
|
354
|
-
* @param {Object} options - Stream options for this instance.
|
|
355
|
-
* @returns {Stream} - TODO: add return description.
|
|
356
|
-
* TODO: Refactor me.
|
|
357
|
-
*/
|
|
358
|
-
stream(options = {}) {
|
|
359
|
-
const file = path.join(this.dirname, this.filename);
|
|
360
|
-
const stream = new Stream();
|
|
361
|
-
const tail = {
|
|
362
|
-
file,
|
|
363
|
-
start: options.start
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
stream.destroy = tailFile(tail, (err, line) => {
|
|
367
|
-
if (err) {
|
|
368
|
-
return stream.emit('error', err);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
try {
|
|
372
|
-
stream.emit('data', line);
|
|
373
|
-
line = JSON.parse(line);
|
|
374
|
-
stream.emit('log', line);
|
|
375
|
-
} catch (e) {
|
|
376
|
-
stream.emit('error', e);
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
|
|
380
|
-
return stream;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Checks to see the filesize of.
|
|
385
|
-
* @returns {undefined}
|
|
386
|
-
*/
|
|
387
|
-
open() {
|
|
388
|
-
// If we do not have a filename then we were passed a stream and
|
|
389
|
-
// don't need to keep track of size.
|
|
390
|
-
if (!this.filename) return;
|
|
391
|
-
if (this._opening) return;
|
|
392
|
-
|
|
393
|
-
this._opening = true;
|
|
394
|
-
|
|
395
|
-
// Stat the target file to get the size and create the stream.
|
|
396
|
-
this.stat((err, size) => {
|
|
397
|
-
if (err) {
|
|
398
|
-
return this.emit('error', err);
|
|
399
|
-
}
|
|
400
|
-
debug('stat done: %s { size: %s }', this.filename, size);
|
|
401
|
-
this._size = size;
|
|
402
|
-
this._dest = this._createStream(this._stream);
|
|
403
|
-
this._opening = false;
|
|
404
|
-
this.once('open', () => {
|
|
405
|
-
if (this._stream.eventNames().includes('rotate')) {
|
|
406
|
-
this._stream.emit('rotate');
|
|
407
|
-
} else {
|
|
408
|
-
this._rotate = false;
|
|
409
|
-
}
|
|
410
|
-
});
|
|
411
|
-
});
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Stat the file and assess information in order to create the proper stream.
|
|
416
|
-
* @param {function} callback - TODO: add param description.
|
|
417
|
-
* @returns {undefined}
|
|
418
|
-
*/
|
|
419
|
-
stat(callback) {
|
|
420
|
-
const target = this._getFile();
|
|
421
|
-
const fullpath = path.join(this.dirname, target);
|
|
422
|
-
|
|
423
|
-
fs.stat(fullpath, (err, stat) => {
|
|
424
|
-
if (err && err.code === 'ENOENT') {
|
|
425
|
-
debug('ENOENT ok', fullpath);
|
|
426
|
-
// Update internally tracked filename with the new target name.
|
|
427
|
-
this.filename = target;
|
|
428
|
-
return callback(null, 0);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
if (err) {
|
|
432
|
-
debug(`err ${err.code} ${fullpath}`);
|
|
433
|
-
return callback(err);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
if (!stat || this._needsNewFile(stat.size)) {
|
|
437
|
-
// If `stats.size` is greater than the `maxsize` for this
|
|
438
|
-
// instance then try again.
|
|
439
|
-
return this._incFile(() => this.stat(callback));
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
// Once we have figured out what the filename is, set it
|
|
443
|
-
// and return the size.
|
|
444
|
-
this.filename = target;
|
|
445
|
-
callback(null, stat.size);
|
|
446
|
-
});
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Closes the stream associated with this instance.
|
|
451
|
-
* @param {function} cb - TODO: add param description.
|
|
452
|
-
* @returns {undefined}
|
|
453
|
-
*/
|
|
454
|
-
close(cb) {
|
|
455
|
-
if (!this._stream) {
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
this._stream.end(() => {
|
|
460
|
-
if (cb) {
|
|
461
|
-
cb(); // eslint-disable-line callback-return
|
|
462
|
-
}
|
|
463
|
-
this.emit('flush');
|
|
464
|
-
this.emit('closed');
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* TODO: add method description.
|
|
470
|
-
* @param {number} size - TODO: add param description.
|
|
471
|
-
* @returns {undefined}
|
|
472
|
-
*/
|
|
473
|
-
_needsNewFile(size) {
|
|
474
|
-
size = size || this._size;
|
|
475
|
-
return this.maxsize && size >= this.maxsize;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* TODO: add method description.
|
|
480
|
-
* @param {Error} err - TODO: add param description.
|
|
481
|
-
* @returns {undefined}
|
|
482
|
-
*/
|
|
483
|
-
_onError(err) {
|
|
484
|
-
this.emit('error', err);
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* TODO: add method description.
|
|
489
|
-
* @param {Stream} stream - TODO: add param description.
|
|
490
|
-
* @returns {mixed} - TODO: add return description.
|
|
491
|
-
*/
|
|
492
|
-
_setupStream(stream) {
|
|
493
|
-
stream.on('error', this._onError);
|
|
494
|
-
|
|
495
|
-
return stream;
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* TODO: add method description.
|
|
500
|
-
* @param {Stream} stream - TODO: add param description.
|
|
501
|
-
* @returns {mixed} - TODO: add return description.
|
|
502
|
-
*/
|
|
503
|
-
_cleanupStream(stream) {
|
|
504
|
-
stream.removeListener('error', this._onError);
|
|
505
|
-
|
|
506
|
-
return stream;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/**
|
|
510
|
-
* TODO: add method description.
|
|
511
|
-
*/
|
|
512
|
-
_rotateFile() {
|
|
513
|
-
this._incFile(() => this.open());
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
/**
|
|
517
|
-
* Unpipe from the stream that has been marked as full and end it so it
|
|
518
|
-
* flushes to disk.
|
|
519
|
-
*
|
|
520
|
-
* @param {function} callback - Callback for when the current file has closed.
|
|
521
|
-
* @private
|
|
522
|
-
*/
|
|
523
|
-
_endStream(callback = () => {}) {
|
|
524
|
-
if (this._dest) {
|
|
525
|
-
this._stream.unpipe(this._dest);
|
|
526
|
-
this._dest.end(() => {
|
|
527
|
-
this._cleanupStream(this._dest);
|
|
528
|
-
callback();
|
|
529
|
-
});
|
|
530
|
-
} else {
|
|
531
|
-
callback(); // eslint-disable-line callback-return
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/**
|
|
536
|
-
* Returns the WritableStream for the active file on this instance. If we
|
|
537
|
-
* should gzip the file then a zlib stream is returned.
|
|
538
|
-
*
|
|
539
|
-
* @param {ReadableStream} source – PassThrough to pipe to the file when open.
|
|
540
|
-
* @returns {WritableStream} Stream that writes to disk for the active file.
|
|
541
|
-
*/
|
|
542
|
-
_createStream(source) {
|
|
543
|
-
const fullpath = path.join(this.dirname, this.filename);
|
|
544
|
-
|
|
545
|
-
debug('create stream start', fullpath, this.options);
|
|
546
|
-
const dest = fs.createWriteStream(fullpath, this.options)
|
|
547
|
-
// TODO: What should we do with errors here?
|
|
548
|
-
.on('error', err => debug(err))
|
|
549
|
-
.on('close', () => debug('close', dest.path, dest.bytesWritten))
|
|
550
|
-
.on('open', () => {
|
|
551
|
-
debug('file open ok', fullpath);
|
|
552
|
-
this.emit('open', fullpath);
|
|
553
|
-
source.pipe(dest);
|
|
554
|
-
|
|
555
|
-
// If rotation occured during the open operation then we immediately
|
|
556
|
-
// start writing to a new PassThrough, begin opening the next file
|
|
557
|
-
// and cleanup the previous source and dest once the source has drained.
|
|
558
|
-
if (this.rotatedWhileOpening) {
|
|
559
|
-
this._stream = new PassThrough();
|
|
560
|
-
this._stream.setMaxListeners(30);
|
|
561
|
-
this._rotateFile();
|
|
562
|
-
this.rotatedWhileOpening = false;
|
|
563
|
-
this._cleanupStream(dest);
|
|
564
|
-
source.end();
|
|
565
|
-
}
|
|
566
|
-
});
|
|
567
|
-
|
|
568
|
-
debug('create stream ok', fullpath);
|
|
569
|
-
if (this.zippedArchive) {
|
|
570
|
-
const gzip = zlib.createGzip();
|
|
571
|
-
gzip.pipe(dest);
|
|
572
|
-
return gzip;
|
|
573
|
-
}
|
|
574
|
-
|
|
575
|
-
return dest;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* TODO: add method description.
|
|
580
|
-
* @param {function} callback - TODO: add param description.
|
|
581
|
-
* @returns {undefined}
|
|
582
|
-
*/
|
|
583
|
-
_incFile(callback) {
|
|
584
|
-
debug('_incFile', this.filename);
|
|
585
|
-
const ext = path.extname(this._basename);
|
|
586
|
-
const basename = path.basename(this._basename, ext);
|
|
587
|
-
|
|
588
|
-
if (!this.tailable) {
|
|
589
|
-
this._created += 1;
|
|
590
|
-
this._checkMaxFilesIncrementing(ext, basename, callback);
|
|
591
|
-
} else {
|
|
592
|
-
this._checkMaxFilesTailable(ext, basename, callback);
|
|
593
|
-
}
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Gets the next filename to use for this instance in the case that log
|
|
598
|
-
* filesizes are being capped.
|
|
599
|
-
* @returns {string} - TODO: add return description.
|
|
600
|
-
* @private
|
|
601
|
-
*/
|
|
602
|
-
_getFile() {
|
|
603
|
-
const ext = path.extname(this._basename);
|
|
604
|
-
const basename = path.basename(this._basename, ext);
|
|
605
|
-
const isRotation = this.rotationFormat
|
|
606
|
-
? this.rotationFormat()
|
|
607
|
-
: this._created;
|
|
608
|
-
|
|
609
|
-
// Caveat emptor (indexzero): rotationFormat() was broken by design When
|
|
610
|
-
// combined with max files because the set of files to unlink is never
|
|
611
|
-
// stored.
|
|
612
|
-
const target = !this.tailable && this._created
|
|
613
|
-
? `${basename}${isRotation}${ext}`
|
|
614
|
-
: `${basename}${ext}`;
|
|
615
|
-
|
|
616
|
-
return this.zippedArchive && !this.tailable
|
|
617
|
-
? `${target}.gz`
|
|
618
|
-
: target;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* Increment the number of files created or checked by this instance.
|
|
623
|
-
* @param {mixed} ext - TODO: add param description.
|
|
624
|
-
* @param {mixed} basename - TODO: add param description.
|
|
625
|
-
* @param {mixed} callback - TODO: add param description.
|
|
626
|
-
* @returns {undefined}
|
|
627
|
-
* @private
|
|
628
|
-
*/
|
|
629
|
-
_checkMaxFilesIncrementing(ext, basename, callback) {
|
|
630
|
-
// Check for maxFiles option and delete file.
|
|
631
|
-
if (!this.maxFiles || this._created < this.maxFiles) {
|
|
632
|
-
return setImmediate(callback);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
const oldest = this._created - this.maxFiles;
|
|
636
|
-
const isOldest = oldest !== 0 ? oldest : '';
|
|
637
|
-
const isZipped = this.zippedArchive ? '.gz' : '';
|
|
638
|
-
const filePath = `${basename}${isOldest}${ext}${isZipped}`;
|
|
639
|
-
const target = path.join(this.dirname, filePath);
|
|
640
|
-
|
|
641
|
-
fs.unlink(target, callback);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Roll files forward based on integer, up to maxFiles. e.g. if base if
|
|
646
|
-
* file.log and it becomes oversized, roll to file1.log, and allow file.log
|
|
647
|
-
* to be re-used. If file is oversized again, roll file1.log to file2.log,
|
|
648
|
-
* roll file.log to file1.log, and so on.
|
|
649
|
-
* @param {mixed} ext - TODO: add param description.
|
|
650
|
-
* @param {mixed} basename - TODO: add param description.
|
|
651
|
-
* @param {mixed} callback - TODO: add param description.
|
|
652
|
-
* @returns {undefined}
|
|
653
|
-
* @private
|
|
654
|
-
*/
|
|
655
|
-
_checkMaxFilesTailable(ext, basename, callback) {
|
|
656
|
-
const tasks = [];
|
|
657
|
-
if (!this.maxFiles) {
|
|
658
|
-
return;
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
// const isZipped = this.zippedArchive ? '.gz' : '';
|
|
662
|
-
const isZipped = this.zippedArchive ? '.gz' : '';
|
|
663
|
-
for (let x = this.maxFiles - 1; x > 1; x--) {
|
|
664
|
-
tasks.push(function (i, cb) {
|
|
665
|
-
let fileName = `${basename}${(i - 1)}${ext}${isZipped}`;
|
|
666
|
-
const tmppath = path.join(this.dirname, fileName);
|
|
667
|
-
|
|
668
|
-
fs.exists(tmppath, exists => {
|
|
669
|
-
if (!exists) {
|
|
670
|
-
return cb(null);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
fileName = `${basename}${i}${ext}${isZipped}`;
|
|
674
|
-
fs.rename(tmppath, path.join(this.dirname, fileName), cb);
|
|
675
|
-
});
|
|
676
|
-
}.bind(this, x));
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
asyncSeries(tasks, () => {
|
|
680
|
-
fs.rename(
|
|
681
|
-
path.join(this.dirname, `${basename}${ext}`),
|
|
682
|
-
path.join(this.dirname, `${basename}1${ext}${isZipped}`),
|
|
683
|
-
callback
|
|
684
|
-
);
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
_createLogDirIfNotExist(dirPath) {
|
|
689
|
-
/* eslint-disable no-sync */
|
|
690
|
-
if (!fs.existsSync(dirPath)) {
|
|
691
|
-
fs.mkdirSync(dirPath, { recursive: true });
|
|
692
|
-
}
|
|
693
|
-
/* eslint-enable no-sync */
|
|
694
|
-
}
|
|
695
|
-
};
|
|
1
|
+
/* eslint-disable complexity,max-statements */
|
|
2
|
+
/**
|
|
3
|
+
* file.js: Transport for outputting to a local log file.
|
|
4
|
+
*
|
|
5
|
+
* (C) 2010 Charlie Robbins
|
|
6
|
+
* MIT LICENCE
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const asyncSeries = require('async/series');
|
|
14
|
+
const zlib = require('zlib');
|
|
15
|
+
const { MESSAGE } = require('triple-beam');
|
|
16
|
+
const { Stream, PassThrough } = require('readable-stream');
|
|
17
|
+
const TransportStream = require('winston-transport');
|
|
18
|
+
const debug = require('@dabh/diagnostics')('winston:file');
|
|
19
|
+
const os = require('os');
|
|
20
|
+
const tailFile = require('../tail-file');
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Transport for outputting to a local log file.
|
|
24
|
+
* @type {File}
|
|
25
|
+
* @extends {TransportStream}
|
|
26
|
+
*/
|
|
27
|
+
module.exports = class File extends TransportStream {
|
|
28
|
+
/**
|
|
29
|
+
* Constructor function for the File transport object responsible for
|
|
30
|
+
* persisting log messages and metadata to one or more files.
|
|
31
|
+
* @param {Object} options - Options for this instance.
|
|
32
|
+
*/
|
|
33
|
+
constructor(options = {}) {
|
|
34
|
+
super(options);
|
|
35
|
+
|
|
36
|
+
// Expose the name of this Transport on the prototype.
|
|
37
|
+
this.name = options.name || 'file';
|
|
38
|
+
|
|
39
|
+
// Helper function which throws an `Error` in the event that any of the
|
|
40
|
+
// rest of the arguments is present in `options`.
|
|
41
|
+
function throwIf(target, ...args) {
|
|
42
|
+
args.slice(1).forEach(name => {
|
|
43
|
+
if (options[name]) {
|
|
44
|
+
throw new Error(`Cannot set ${name} and ${target} together`);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Setup the base stream that always gets piped to to handle buffering.
|
|
50
|
+
this._stream = new PassThrough();
|
|
51
|
+
this._stream.setMaxListeners(30);
|
|
52
|
+
|
|
53
|
+
// Bind this context for listener methods.
|
|
54
|
+
this._onError = this._onError.bind(this);
|
|
55
|
+
|
|
56
|
+
if (options.filename || options.dirname) {
|
|
57
|
+
throwIf('filename or dirname', 'stream');
|
|
58
|
+
this._basename = this.filename = options.filename
|
|
59
|
+
? path.basename(options.filename)
|
|
60
|
+
: 'winston.log';
|
|
61
|
+
|
|
62
|
+
this.dirname = options.dirname || path.dirname(options.filename);
|
|
63
|
+
this.options = options.options || { flags: 'a' };
|
|
64
|
+
} else if (options.stream) {
|
|
65
|
+
// eslint-disable-next-line no-console
|
|
66
|
+
console.warn('options.stream will be removed in winston@4. Use winston.transports.Stream');
|
|
67
|
+
throwIf('stream', 'filename', 'maxsize');
|
|
68
|
+
this._dest = this._stream.pipe(this._setupStream(options.stream));
|
|
69
|
+
this.dirname = path.dirname(this._dest.path);
|
|
70
|
+
// We need to listen for drain events when write() returns false. This
|
|
71
|
+
// can make node mad at times.
|
|
72
|
+
} else {
|
|
73
|
+
throw new Error('Cannot log to file without filename or stream.');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
this.maxsize = options.maxsize || null;
|
|
77
|
+
this.rotationFormat = options.rotationFormat || false;
|
|
78
|
+
this.zippedArchive = options.zippedArchive || false;
|
|
79
|
+
this.maxFiles = options.maxFiles || null;
|
|
80
|
+
this.eol = (typeof options.eol === 'string') ? options.eol : os.EOL;
|
|
81
|
+
this.tailable = options.tailable || false;
|
|
82
|
+
|
|
83
|
+
// Internal state variables representing the number of files this instance
|
|
84
|
+
// has created and the current size (in bytes) of the current logfile.
|
|
85
|
+
this._size = 0;
|
|
86
|
+
this._pendingSize = 0;
|
|
87
|
+
this._created = 0;
|
|
88
|
+
this._drain = false;
|
|
89
|
+
this._opening = false;
|
|
90
|
+
this._ending = false;
|
|
91
|
+
|
|
92
|
+
if (this.dirname) this._createLogDirIfNotExist(this.dirname);
|
|
93
|
+
this.open();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
finishIfEnding() {
|
|
97
|
+
if (this._ending) {
|
|
98
|
+
if (this._opening) {
|
|
99
|
+
this.once('open', () => {
|
|
100
|
+
this._stream.once('finish', () => this.emit('finish'));
|
|
101
|
+
setImmediate(() => this._stream.end());
|
|
102
|
+
});
|
|
103
|
+
} else {
|
|
104
|
+
this._stream.once('finish', () => this.emit('finish'));
|
|
105
|
+
setImmediate(() => this._stream.end());
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Core logging method exposed to Winston. Metadata is optional.
|
|
113
|
+
* @param {Object} info - TODO: add param description.
|
|
114
|
+
* @param {Function} callback - TODO: add param description.
|
|
115
|
+
* @returns {undefined}
|
|
116
|
+
*/
|
|
117
|
+
log(info, callback = () => {}) {
|
|
118
|
+
// Remark: (jcrugzz) What is necessary about this callback(null, true) now
|
|
119
|
+
// when thinking about 3.x? Should silent be handled in the base
|
|
120
|
+
// TransportStream _write method?
|
|
121
|
+
if (this.silent) {
|
|
122
|
+
callback();
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Output stream buffer is full and has asked us to wait for the drain event
|
|
127
|
+
if (this._drain) {
|
|
128
|
+
this._stream.once('drain', () => {
|
|
129
|
+
this._drain = false;
|
|
130
|
+
this.log(info, callback);
|
|
131
|
+
});
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
if (this._rotate) {
|
|
135
|
+
this._stream.once('rotate', () => {
|
|
136
|
+
this._rotate = false;
|
|
137
|
+
this.log(info, callback);
|
|
138
|
+
});
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Grab the raw string and append the expected EOL.
|
|
143
|
+
const output = `${info[MESSAGE]}${this.eol}`;
|
|
144
|
+
const bytes = Buffer.byteLength(output);
|
|
145
|
+
|
|
146
|
+
// After we have written to the PassThrough check to see if we need
|
|
147
|
+
// to rotate to the next file.
|
|
148
|
+
//
|
|
149
|
+
// Remark: This gets called too early and does not depict when data
|
|
150
|
+
// has been actually flushed to disk.
|
|
151
|
+
function logged() {
|
|
152
|
+
this._size += bytes;
|
|
153
|
+
this._pendingSize -= bytes;
|
|
154
|
+
|
|
155
|
+
debug('logged %s %s', this._size, output);
|
|
156
|
+
this.emit('logged', info);
|
|
157
|
+
|
|
158
|
+
// Do not attempt to rotate files while opening
|
|
159
|
+
if (this._opening) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check to see if we need to end the stream and create a new one.
|
|
164
|
+
if (!this._needsNewFile()) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// End the current stream, ensure it flushes and create a new one.
|
|
169
|
+
// This could potentially be optimized to not run a stat call but its
|
|
170
|
+
// the safest way since we are supporting `maxFiles`.
|
|
171
|
+
this._rotate = true;
|
|
172
|
+
this._endStream(() => this._rotateFile());
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Keep track of the pending bytes being written while files are opening
|
|
176
|
+
// in order to properly rotate the PassThrough this._stream when the file
|
|
177
|
+
// eventually does open.
|
|
178
|
+
this._pendingSize += bytes;
|
|
179
|
+
if (this._opening
|
|
180
|
+
&& !this.rotatedWhileOpening
|
|
181
|
+
&& this._needsNewFile(this._size + this._pendingSize)) {
|
|
182
|
+
this.rotatedWhileOpening = true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const written = this._stream.write(output, logged.bind(this));
|
|
186
|
+
if (!written) {
|
|
187
|
+
this._drain = true;
|
|
188
|
+
this._stream.once('drain', () => {
|
|
189
|
+
this._drain = false;
|
|
190
|
+
callback();
|
|
191
|
+
});
|
|
192
|
+
} else {
|
|
193
|
+
callback(); // eslint-disable-line callback-return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
debug('written', written, this._drain);
|
|
197
|
+
|
|
198
|
+
this.finishIfEnding();
|
|
199
|
+
|
|
200
|
+
return written;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Query the transport. Options object is optional.
|
|
205
|
+
* @param {Object} options - Loggly-like query options for this instance.
|
|
206
|
+
* @param {function} callback - Continuation to respond to when complete.
|
|
207
|
+
* TODO: Refactor me.
|
|
208
|
+
*/
|
|
209
|
+
query(options, callback) {
|
|
210
|
+
if (typeof options === 'function') {
|
|
211
|
+
callback = options;
|
|
212
|
+
options = {};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
options = normalizeQuery(options);
|
|
216
|
+
const file = path.join(this.dirname, this.filename);
|
|
217
|
+
let buff = '';
|
|
218
|
+
let results = [];
|
|
219
|
+
let row = 0;
|
|
220
|
+
|
|
221
|
+
const stream = fs.createReadStream(file, {
|
|
222
|
+
encoding: 'utf8'
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
stream.on('error', err => {
|
|
226
|
+
if (stream.readable) {
|
|
227
|
+
stream.destroy();
|
|
228
|
+
}
|
|
229
|
+
if (!callback) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return err.code !== 'ENOENT' ? callback(err) : callback(null, results);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
stream.on('data', data => {
|
|
237
|
+
data = (buff + data).split(/\n+/);
|
|
238
|
+
const l = data.length - 1;
|
|
239
|
+
let i = 0;
|
|
240
|
+
|
|
241
|
+
for (; i < l; i++) {
|
|
242
|
+
if (!options.start || row >= options.start) {
|
|
243
|
+
add(data[i]);
|
|
244
|
+
}
|
|
245
|
+
row++;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
buff = data[l];
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
stream.on('close', () => {
|
|
252
|
+
if (buff) {
|
|
253
|
+
add(buff, true);
|
|
254
|
+
}
|
|
255
|
+
if (options.order === 'desc') {
|
|
256
|
+
results = results.reverse();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// eslint-disable-next-line callback-return
|
|
260
|
+
if (callback) callback(null, results);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
function add(buff, attempt) {
|
|
264
|
+
try {
|
|
265
|
+
const log = JSON.parse(buff);
|
|
266
|
+
if (check(log)) {
|
|
267
|
+
push(log);
|
|
268
|
+
}
|
|
269
|
+
} catch (e) {
|
|
270
|
+
if (!attempt) {
|
|
271
|
+
stream.emit('error', e);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function push(log) {
|
|
277
|
+
if (
|
|
278
|
+
options.rows &&
|
|
279
|
+
results.length >= options.rows &&
|
|
280
|
+
options.order !== 'desc'
|
|
281
|
+
) {
|
|
282
|
+
if (stream.readable) {
|
|
283
|
+
stream.destroy();
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (options.fields) {
|
|
289
|
+
log = options.fields.reduce((obj, key) => {
|
|
290
|
+
obj[key] = log[key];
|
|
291
|
+
return obj;
|
|
292
|
+
}, {});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (options.order === 'desc') {
|
|
296
|
+
if (results.length >= options.rows) {
|
|
297
|
+
results.shift();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
results.push(log);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function check(log) {
|
|
304
|
+
if (!log) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (typeof log !== 'object') {
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const time = new Date(log.timestamp);
|
|
313
|
+
if (
|
|
314
|
+
(options.from && time < options.from) ||
|
|
315
|
+
(options.until && time > options.until) ||
|
|
316
|
+
(options.level && options.level !== log.level)
|
|
317
|
+
) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function normalizeQuery(options) {
|
|
325
|
+
options = options || {};
|
|
326
|
+
|
|
327
|
+
// limit
|
|
328
|
+
options.rows = options.rows || options.limit || 10;
|
|
329
|
+
|
|
330
|
+
// starting row offset
|
|
331
|
+
options.start = options.start || 0;
|
|
332
|
+
|
|
333
|
+
// now
|
|
334
|
+
options.until = options.until || new Date();
|
|
335
|
+
if (typeof options.until !== 'object') {
|
|
336
|
+
options.until = new Date(options.until);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// now - 24
|
|
340
|
+
options.from = options.from || (options.until - (24 * 60 * 60 * 1000));
|
|
341
|
+
if (typeof options.from !== 'object') {
|
|
342
|
+
options.from = new Date(options.from);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 'asc' or 'desc'
|
|
346
|
+
options.order = options.order || 'desc';
|
|
347
|
+
|
|
348
|
+
return options;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Returns a log stream for this transport. Options object is optional.
|
|
354
|
+
* @param {Object} options - Stream options for this instance.
|
|
355
|
+
* @returns {Stream} - TODO: add return description.
|
|
356
|
+
* TODO: Refactor me.
|
|
357
|
+
*/
|
|
358
|
+
stream(options = {}) {
|
|
359
|
+
const file = path.join(this.dirname, this.filename);
|
|
360
|
+
const stream = new Stream();
|
|
361
|
+
const tail = {
|
|
362
|
+
file,
|
|
363
|
+
start: options.start
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
stream.destroy = tailFile(tail, (err, line) => {
|
|
367
|
+
if (err) {
|
|
368
|
+
return stream.emit('error', err);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
stream.emit('data', line);
|
|
373
|
+
line = JSON.parse(line);
|
|
374
|
+
stream.emit('log', line);
|
|
375
|
+
} catch (e) {
|
|
376
|
+
stream.emit('error', e);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
return stream;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Checks to see the filesize of.
|
|
385
|
+
* @returns {undefined}
|
|
386
|
+
*/
|
|
387
|
+
open() {
|
|
388
|
+
// If we do not have a filename then we were passed a stream and
|
|
389
|
+
// don't need to keep track of size.
|
|
390
|
+
if (!this.filename) return;
|
|
391
|
+
if (this._opening) return;
|
|
392
|
+
|
|
393
|
+
this._opening = true;
|
|
394
|
+
|
|
395
|
+
// Stat the target file to get the size and create the stream.
|
|
396
|
+
this.stat((err, size) => {
|
|
397
|
+
if (err) {
|
|
398
|
+
return this.emit('error', err);
|
|
399
|
+
}
|
|
400
|
+
debug('stat done: %s { size: %s }', this.filename, size);
|
|
401
|
+
this._size = size;
|
|
402
|
+
this._dest = this._createStream(this._stream);
|
|
403
|
+
this._opening = false;
|
|
404
|
+
this.once('open', () => {
|
|
405
|
+
if (this._stream.eventNames().includes('rotate')) {
|
|
406
|
+
this._stream.emit('rotate');
|
|
407
|
+
} else {
|
|
408
|
+
this._rotate = false;
|
|
409
|
+
}
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Stat the file and assess information in order to create the proper stream.
|
|
416
|
+
* @param {function} callback - TODO: add param description.
|
|
417
|
+
* @returns {undefined}
|
|
418
|
+
*/
|
|
419
|
+
stat(callback) {
|
|
420
|
+
const target = this._getFile();
|
|
421
|
+
const fullpath = path.join(this.dirname, target);
|
|
422
|
+
|
|
423
|
+
fs.stat(fullpath, (err, stat) => {
|
|
424
|
+
if (err && err.code === 'ENOENT') {
|
|
425
|
+
debug('ENOENT ok', fullpath);
|
|
426
|
+
// Update internally tracked filename with the new target name.
|
|
427
|
+
this.filename = target;
|
|
428
|
+
return callback(null, 0);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (err) {
|
|
432
|
+
debug(`err ${err.code} ${fullpath}`);
|
|
433
|
+
return callback(err);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (!stat || this._needsNewFile(stat.size)) {
|
|
437
|
+
// If `stats.size` is greater than the `maxsize` for this
|
|
438
|
+
// instance then try again.
|
|
439
|
+
return this._incFile(() => this.stat(callback));
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Once we have figured out what the filename is, set it
|
|
443
|
+
// and return the size.
|
|
444
|
+
this.filename = target;
|
|
445
|
+
callback(null, stat.size);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Closes the stream associated with this instance.
|
|
451
|
+
* @param {function} cb - TODO: add param description.
|
|
452
|
+
* @returns {undefined}
|
|
453
|
+
*/
|
|
454
|
+
close(cb) {
|
|
455
|
+
if (!this._stream) {
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
this._stream.end(() => {
|
|
460
|
+
if (cb) {
|
|
461
|
+
cb(); // eslint-disable-line callback-return
|
|
462
|
+
}
|
|
463
|
+
this.emit('flush');
|
|
464
|
+
this.emit('closed');
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* TODO: add method description.
|
|
470
|
+
* @param {number} size - TODO: add param description.
|
|
471
|
+
* @returns {undefined}
|
|
472
|
+
*/
|
|
473
|
+
_needsNewFile(size) {
|
|
474
|
+
size = size || this._size;
|
|
475
|
+
return this.maxsize && size >= this.maxsize;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* TODO: add method description.
|
|
480
|
+
* @param {Error} err - TODO: add param description.
|
|
481
|
+
* @returns {undefined}
|
|
482
|
+
*/
|
|
483
|
+
_onError(err) {
|
|
484
|
+
this.emit('error', err);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/**
|
|
488
|
+
* TODO: add method description.
|
|
489
|
+
* @param {Stream} stream - TODO: add param description.
|
|
490
|
+
* @returns {mixed} - TODO: add return description.
|
|
491
|
+
*/
|
|
492
|
+
_setupStream(stream) {
|
|
493
|
+
stream.on('error', this._onError);
|
|
494
|
+
|
|
495
|
+
return stream;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* TODO: add method description.
|
|
500
|
+
* @param {Stream} stream - TODO: add param description.
|
|
501
|
+
* @returns {mixed} - TODO: add return description.
|
|
502
|
+
*/
|
|
503
|
+
_cleanupStream(stream) {
|
|
504
|
+
stream.removeListener('error', this._onError);
|
|
505
|
+
|
|
506
|
+
return stream;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* TODO: add method description.
|
|
511
|
+
*/
|
|
512
|
+
_rotateFile() {
|
|
513
|
+
this._incFile(() => this.open());
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Unpipe from the stream that has been marked as full and end it so it
|
|
518
|
+
* flushes to disk.
|
|
519
|
+
*
|
|
520
|
+
* @param {function} callback - Callback for when the current file has closed.
|
|
521
|
+
* @private
|
|
522
|
+
*/
|
|
523
|
+
_endStream(callback = () => {}) {
|
|
524
|
+
if (this._dest) {
|
|
525
|
+
this._stream.unpipe(this._dest);
|
|
526
|
+
this._dest.end(() => {
|
|
527
|
+
this._cleanupStream(this._dest);
|
|
528
|
+
callback();
|
|
529
|
+
});
|
|
530
|
+
} else {
|
|
531
|
+
callback(); // eslint-disable-line callback-return
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Returns the WritableStream for the active file on this instance. If we
|
|
537
|
+
* should gzip the file then a zlib stream is returned.
|
|
538
|
+
*
|
|
539
|
+
* @param {ReadableStream} source – PassThrough to pipe to the file when open.
|
|
540
|
+
* @returns {WritableStream} Stream that writes to disk for the active file.
|
|
541
|
+
*/
|
|
542
|
+
_createStream(source) {
|
|
543
|
+
const fullpath = path.join(this.dirname, this.filename);
|
|
544
|
+
|
|
545
|
+
debug('create stream start', fullpath, this.options);
|
|
546
|
+
const dest = fs.createWriteStream(fullpath, this.options)
|
|
547
|
+
// TODO: What should we do with errors here?
|
|
548
|
+
.on('error', err => debug(err))
|
|
549
|
+
.on('close', () => debug('close', dest.path, dest.bytesWritten))
|
|
550
|
+
.on('open', () => {
|
|
551
|
+
debug('file open ok', fullpath);
|
|
552
|
+
this.emit('open', fullpath);
|
|
553
|
+
source.pipe(dest);
|
|
554
|
+
|
|
555
|
+
// If rotation occured during the open operation then we immediately
|
|
556
|
+
// start writing to a new PassThrough, begin opening the next file
|
|
557
|
+
// and cleanup the previous source and dest once the source has drained.
|
|
558
|
+
if (this.rotatedWhileOpening) {
|
|
559
|
+
this._stream = new PassThrough();
|
|
560
|
+
this._stream.setMaxListeners(30);
|
|
561
|
+
this._rotateFile();
|
|
562
|
+
this.rotatedWhileOpening = false;
|
|
563
|
+
this._cleanupStream(dest);
|
|
564
|
+
source.end();
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
debug('create stream ok', fullpath);
|
|
569
|
+
if (this.zippedArchive) {
|
|
570
|
+
const gzip = zlib.createGzip();
|
|
571
|
+
gzip.pipe(dest);
|
|
572
|
+
return gzip;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
return dest;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* TODO: add method description.
|
|
580
|
+
* @param {function} callback - TODO: add param description.
|
|
581
|
+
* @returns {undefined}
|
|
582
|
+
*/
|
|
583
|
+
_incFile(callback) {
|
|
584
|
+
debug('_incFile', this.filename);
|
|
585
|
+
const ext = path.extname(this._basename);
|
|
586
|
+
const basename = path.basename(this._basename, ext);
|
|
587
|
+
|
|
588
|
+
if (!this.tailable) {
|
|
589
|
+
this._created += 1;
|
|
590
|
+
this._checkMaxFilesIncrementing(ext, basename, callback);
|
|
591
|
+
} else {
|
|
592
|
+
this._checkMaxFilesTailable(ext, basename, callback);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Gets the next filename to use for this instance in the case that log
|
|
598
|
+
* filesizes are being capped.
|
|
599
|
+
* @returns {string} - TODO: add return description.
|
|
600
|
+
* @private
|
|
601
|
+
*/
|
|
602
|
+
_getFile() {
|
|
603
|
+
const ext = path.extname(this._basename);
|
|
604
|
+
const basename = path.basename(this._basename, ext);
|
|
605
|
+
const isRotation = this.rotationFormat
|
|
606
|
+
? this.rotationFormat()
|
|
607
|
+
: this._created;
|
|
608
|
+
|
|
609
|
+
// Caveat emptor (indexzero): rotationFormat() was broken by design When
|
|
610
|
+
// combined with max files because the set of files to unlink is never
|
|
611
|
+
// stored.
|
|
612
|
+
const target = !this.tailable && this._created
|
|
613
|
+
? `${basename}${isRotation}${ext}`
|
|
614
|
+
: `${basename}${ext}`;
|
|
615
|
+
|
|
616
|
+
return this.zippedArchive && !this.tailable
|
|
617
|
+
? `${target}.gz`
|
|
618
|
+
: target;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Increment the number of files created or checked by this instance.
|
|
623
|
+
* @param {mixed} ext - TODO: add param description.
|
|
624
|
+
* @param {mixed} basename - TODO: add param description.
|
|
625
|
+
* @param {mixed} callback - TODO: add param description.
|
|
626
|
+
* @returns {undefined}
|
|
627
|
+
* @private
|
|
628
|
+
*/
|
|
629
|
+
_checkMaxFilesIncrementing(ext, basename, callback) {
|
|
630
|
+
// Check for maxFiles option and delete file.
|
|
631
|
+
if (!this.maxFiles || this._created < this.maxFiles) {
|
|
632
|
+
return setImmediate(callback);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const oldest = this._created - this.maxFiles;
|
|
636
|
+
const isOldest = oldest !== 0 ? oldest : '';
|
|
637
|
+
const isZipped = this.zippedArchive ? '.gz' : '';
|
|
638
|
+
const filePath = `${basename}${isOldest}${ext}${isZipped}`;
|
|
639
|
+
const target = path.join(this.dirname, filePath);
|
|
640
|
+
|
|
641
|
+
fs.unlink(target, callback);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Roll files forward based on integer, up to maxFiles. e.g. if base if
|
|
646
|
+
* file.log and it becomes oversized, roll to file1.log, and allow file.log
|
|
647
|
+
* to be re-used. If file is oversized again, roll file1.log to file2.log,
|
|
648
|
+
* roll file.log to file1.log, and so on.
|
|
649
|
+
* @param {mixed} ext - TODO: add param description.
|
|
650
|
+
* @param {mixed} basename - TODO: add param description.
|
|
651
|
+
* @param {mixed} callback - TODO: add param description.
|
|
652
|
+
* @returns {undefined}
|
|
653
|
+
* @private
|
|
654
|
+
*/
|
|
655
|
+
_checkMaxFilesTailable(ext, basename, callback) {
|
|
656
|
+
const tasks = [];
|
|
657
|
+
if (!this.maxFiles) {
|
|
658
|
+
return;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// const isZipped = this.zippedArchive ? '.gz' : '';
|
|
662
|
+
const isZipped = this.zippedArchive ? '.gz' : '';
|
|
663
|
+
for (let x = this.maxFiles - 1; x > 1; x--) {
|
|
664
|
+
tasks.push(function (i, cb) {
|
|
665
|
+
let fileName = `${basename}${(i - 1)}${ext}${isZipped}`;
|
|
666
|
+
const tmppath = path.join(this.dirname, fileName);
|
|
667
|
+
|
|
668
|
+
fs.exists(tmppath, exists => {
|
|
669
|
+
if (!exists) {
|
|
670
|
+
return cb(null);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
fileName = `${basename}${i}${ext}${isZipped}`;
|
|
674
|
+
fs.rename(tmppath, path.join(this.dirname, fileName), cb);
|
|
675
|
+
});
|
|
676
|
+
}.bind(this, x));
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
asyncSeries(tasks, () => {
|
|
680
|
+
fs.rename(
|
|
681
|
+
path.join(this.dirname, `${basename}${ext}`),
|
|
682
|
+
path.join(this.dirname, `${basename}1${ext}${isZipped}`),
|
|
683
|
+
callback
|
|
684
|
+
);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
_createLogDirIfNotExist(dirPath) {
|
|
689
|
+
/* eslint-disable no-sync */
|
|
690
|
+
if (!fs.existsSync(dirPath)) {
|
|
691
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
692
|
+
}
|
|
693
|
+
/* eslint-enable no-sync */
|
|
694
|
+
}
|
|
695
|
+
};
|