@tryghost/bunyan-rotating-filestream 0.0.7 → 0.0.8
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/LICENSE +3 -2
- package/README.md +30 -22
- package/index.js +20 -10
- package/lib/customEvents.js +2 -2
- package/lib/fileRotator.js +22 -15
- package/lib/periodTrigger.js +76 -64
- package/lib/thresholdTrigger.js +4 -4
- package/lib/writeQueue.js +3 -3
- package/package.json +24 -19
- package/util/configProcessors.js +15 -14
- package/.editorconfig +0 -26
- package/.eslintrc.js +0 -6
- package/.github/workflows/test.yml +0 -23
- package/renovate.json +0 -5
- package/test/.eslintrc.js +0 -6
- package/test/filerotate.test.js +0 -98
- package/test/utils/assertions.js +0 -11
- package/test/utils/index.js +0 -11
- package/test/utils/overrides.js +0 -10
package/LICENSE
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2013-2026 Ghost Foundation
|
|
2
4
|
|
|
3
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -17,4 +19,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
17
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
21
|
SOFTWARE.
|
|
20
|
-
|
package/README.md
CHANGED
|
@@ -6,51 +6,59 @@
|
|
|
6
6
|
|
|
7
7
|
or
|
|
8
8
|
|
|
9
|
-
`
|
|
9
|
+
`pnpm add @tryghost/bunyan-rotating-filestream`
|
|
10
10
|
|
|
11
|
+
## Purpose
|
|
12
|
+
|
|
13
|
+
A rotating file stream for the [bunyan](https://github.com/trentm/node-bunyan) logger, supporting period-based and size-based rotation, gzip compression of archives, and limits on the number and total size of archived files.
|
|
11
14
|
|
|
12
15
|
## Usage
|
|
13
16
|
|
|
14
17
|
Create a bunyan logger using the stream:
|
|
15
18
|
|
|
16
19
|
```js
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const log = bunyan.createLogger({
|
|
21
|
+
name: 'foo',
|
|
22
|
+
streams: [
|
|
23
|
+
{
|
|
20
24
|
stream: new RotatingFileStream({
|
|
21
25
|
path: '/var/log/foo.log',
|
|
22
|
-
period: '1d',
|
|
23
|
-
totalFiles: 10,
|
|
24
|
-
rotateExisting: true,
|
|
25
|
-
threshold: '10m',
|
|
26
|
-
totalSize: '20m',
|
|
27
|
-
gzip: true
|
|
28
|
-
})
|
|
29
|
-
}
|
|
30
|
-
|
|
26
|
+
period: '1d', // daily rotation
|
|
27
|
+
totalFiles: 10, // keep up to 10 backup copies
|
|
28
|
+
rotateExisting: true, // Give ourselves a clean file when we start up, based on period
|
|
29
|
+
threshold: '10m', // Rotate log files larger than 10 megabytes
|
|
30
|
+
totalSize: '20m', // Don't keep more than 20mb of archived log files
|
|
31
|
+
gzip: true, // Compress the archive log files to save space
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
});
|
|
31
36
|
```
|
|
32
37
|
|
|
33
38
|
Other options include `startNewFile` to always open a new file on start-up.
|
|
34
39
|
|
|
35
40
|
## Develop
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
2. Run `yarn` to install top-level dependencies.
|
|
42
|
+
This is a mono repository, managed with [Nx](https://nx.dev).
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
Follow the instructions for the top-level repo.
|
|
41
45
|
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
1. `git clone` this repo & `cd` into it as usual
|
|
47
|
+
2. Run `pnpm install` to install top-level dependencies.
|
|
48
|
+
|
|
49
|
+
## Run
|
|
44
50
|
|
|
51
|
+
- `pnpm dev`
|
|
45
52
|
|
|
46
|
-
##
|
|
53
|
+
## Test
|
|
47
54
|
|
|
48
|
-
- `
|
|
55
|
+
- `pnpm lint` runs oxlint
|
|
56
|
+
- `pnpm test` runs lint and tests
|
|
49
57
|
|
|
50
58
|
## Credit
|
|
51
59
|
|
|
52
60
|
Many thanks to @Rcomian for their work on the original bunyan-rotating-file-stream project, this project borrows lots of the code and all of the ideas in the original.
|
|
53
61
|
|
|
54
|
-
# Copyright & License
|
|
62
|
+
# Copyright & License
|
|
55
63
|
|
|
56
|
-
Copyright (c) 2013-
|
|
64
|
+
Copyright (c) 2013-2026 Ghost Foundation - Released under the [MIT license](LICENSE).
|
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const WriteQueue = require('./lib/writeQueue');
|
|
|
2
2
|
const PeriodTrigger = require('./lib/periodTrigger');
|
|
3
3
|
const ThresholdTrigger = require('./lib/thresholdTrigger');
|
|
4
4
|
const FileRotator = require('./lib/fileRotator');
|
|
5
|
-
const {BytesWritten, Rotate, NewFile} = require('./lib/customEvents');
|
|
5
|
+
const { BytesWritten, Rotate, NewFile } = require('./lib/customEvents');
|
|
6
6
|
|
|
7
7
|
const fileStreams = [];
|
|
8
8
|
|
|
@@ -25,10 +25,11 @@ class RotatingFileStream {
|
|
|
25
25
|
}
|
|
26
26
|
if (config.threshold) {
|
|
27
27
|
const thresholdTrigger = new ThresholdTrigger(config.threshold);
|
|
28
|
-
this._queue.on(BytesWritten, bytes => thresholdTrigger.updateWritten(bytes));
|
|
28
|
+
this._queue.on(BytesWritten, (bytes) => thresholdTrigger.updateWritten(bytes));
|
|
29
29
|
this._triggers.push(thresholdTrigger);
|
|
30
30
|
}
|
|
31
31
|
this._rotatingLock = false;
|
|
32
|
+
this._closed = false;
|
|
32
33
|
this._triggers.forEach((trigger) => {
|
|
33
34
|
trigger.on(Rotate, () => {
|
|
34
35
|
this._rotate();
|
|
@@ -36,21 +37,24 @@ class RotatingFileStream {
|
|
|
36
37
|
});
|
|
37
38
|
this._rotator.on(NewFile, (fileInfo) => {
|
|
38
39
|
this._queue.setFileHandle(this._rotator.getCurrentHandle());
|
|
39
|
-
this._triggers.forEach(trigger => trigger.newFile(fileInfo));
|
|
40
|
+
this._triggers.forEach((trigger) => trigger.newFile(fileInfo));
|
|
40
41
|
});
|
|
41
42
|
this._initialised = this._rotator.initialise();
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
async _rotate() {
|
|
45
|
-
if (this._rotatingLock) {
|
|
46
|
-
// Already rotating
|
|
46
|
+
if (this._rotatingLock || this._closed) {
|
|
47
|
+
// Already rotating or shutting down
|
|
47
48
|
return;
|
|
48
49
|
}
|
|
49
50
|
this._rotatingLock = true;
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
this._rotating = (async () => {
|
|
52
|
+
await this._queue.pause();
|
|
53
|
+
const nextFileHandle = await this._rotator.rotate();
|
|
54
|
+
this._queue.setFileHandle(nextFileHandle);
|
|
55
|
+
this._rotatingLock = false;
|
|
56
|
+
})();
|
|
57
|
+
await this._rotating;
|
|
54
58
|
}
|
|
55
59
|
|
|
56
60
|
async write(data) {
|
|
@@ -58,10 +62,16 @@ class RotatingFileStream {
|
|
|
58
62
|
}
|
|
59
63
|
|
|
60
64
|
async end() {
|
|
65
|
+
// Block new rotations immediately - the threshold trigger has no timer
|
|
66
|
+
// to shut down and can still fire while the queue drains below
|
|
67
|
+
this._closed = true;
|
|
61
68
|
await this._initialised;
|
|
69
|
+
// Stop triggers first so no new rotation can start, then wait for any
|
|
70
|
+
// in-flight rotation before closing the file handles
|
|
71
|
+
this._triggers.forEach((trigger) => trigger.shutdown());
|
|
72
|
+
await this._rotating;
|
|
62
73
|
await this._queue.shutdown();
|
|
63
74
|
await this._rotator.shutdown();
|
|
64
|
-
this._triggers.forEach(trigger => trigger.shutdown());
|
|
65
75
|
const fileStreamIndex = fileStreams.indexOf(this._path);
|
|
66
76
|
if (fileStreamIndex >= 0) {
|
|
67
77
|
fileStreams.splice(fileStreamIndex, 1);
|
package/lib/customEvents.js
CHANGED
|
@@ -4,5 +4,5 @@ module.exports = {
|
|
|
4
4
|
// Emitted when a new file handle is acquired with the file info
|
|
5
5
|
NewFile: Symbol('newFile'),
|
|
6
6
|
// Emitted whenever data is written to the file with the number of bytes
|
|
7
|
-
BytesWritten: Symbol('bytesWritten')
|
|
8
|
-
};
|
|
7
|
+
BytesWritten: Symbol('bytesWritten'),
|
|
8
|
+
};
|
package/lib/fileRotator.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const zlib = require('zlib');
|
|
2
|
-
const {pipeline} = require('stream');
|
|
3
|
-
const {promisify} = require('util');
|
|
4
|
-
const {createReadStream, createWriteStream} = require('fs');
|
|
2
|
+
const { pipeline } = require('stream');
|
|
3
|
+
const { promisify } = require('util');
|
|
4
|
+
const { createReadStream, createWriteStream } = require('fs');
|
|
5
5
|
const fs = require('fs').promises;
|
|
6
6
|
const pipe = promisify(pipeline);
|
|
7
7
|
const path = require('path');
|
|
8
|
-
const {processSize} = require('../util/configProcessors');
|
|
8
|
+
const { processSize } = require('../util/configProcessors');
|
|
9
9
|
|
|
10
|
-
const {EventEmitter} = require('events');
|
|
11
|
-
const {NewFile} = require('./customEvents');
|
|
10
|
+
const { EventEmitter } = require('events');
|
|
11
|
+
const { NewFile } = require('./customEvents');
|
|
12
12
|
|
|
13
13
|
class FileRotator extends EventEmitter {
|
|
14
14
|
constructor(config) {
|
|
@@ -75,7 +75,7 @@ class FileRotator extends EventEmitter {
|
|
|
75
75
|
const outputName = this._getFileName(0, true);
|
|
76
76
|
|
|
77
77
|
try {
|
|
78
|
-
fs.stat(inputName);
|
|
78
|
+
await fs.stat(inputName);
|
|
79
79
|
} catch (err) {
|
|
80
80
|
if (err.code === 'ENOENT') {
|
|
81
81
|
return;
|
|
@@ -97,11 +97,12 @@ class FileRotator extends EventEmitter {
|
|
|
97
97
|
const parsedPath = path.parse(this._path);
|
|
98
98
|
let base = `${parsedPath.name}.${String(number - 1)}${parsedPath.ext}`;
|
|
99
99
|
if (number === 0) {
|
|
100
|
-
base =
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
base =
|
|
101
|
+
parsedPath.name
|
|
102
|
+
.replace('.%N', '')
|
|
103
|
+
.replace('_%N', '')
|
|
104
|
+
.replace('-%N', '')
|
|
105
|
+
.replace('%N', '') + parsedPath.ext;
|
|
105
106
|
} else if (parsedPath.name.indexOf('%N') >= 0) {
|
|
106
107
|
base = parsedPath.name.replace('%N', String(number - 1)) + parsedPath.ext;
|
|
107
108
|
}
|
|
@@ -124,7 +125,10 @@ class FileRotator extends EventEmitter {
|
|
|
124
125
|
try {
|
|
125
126
|
const result = await fs.stat(name);
|
|
126
127
|
bytesTotal += result.size;
|
|
127
|
-
if (
|
|
128
|
+
if (
|
|
129
|
+
(this._totalSize && bytesTotal > this._totalSize) ||
|
|
130
|
+
(this._totalFiles && fileNumber >= this._totalFiles)
|
|
131
|
+
) {
|
|
128
132
|
filesToDelete.push(name);
|
|
129
133
|
}
|
|
130
134
|
} catch (err) {
|
|
@@ -159,7 +163,10 @@ class FileRotator extends EventEmitter {
|
|
|
159
163
|
// Set to last file in sequence
|
|
160
164
|
fileNumber -= 1;
|
|
161
165
|
while (fileNumber >= 0) {
|
|
162
|
-
await fs.rename(
|
|
166
|
+
await fs.rename(
|
|
167
|
+
this._getFileName(fileNumber, true),
|
|
168
|
+
this._getFileName(fileNumber + 1, true),
|
|
169
|
+
);
|
|
163
170
|
fileNumber -= 1;
|
|
164
171
|
}
|
|
165
172
|
}
|
|
@@ -173,4 +180,4 @@ class FileRotator extends EventEmitter {
|
|
|
173
180
|
}
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
module.exports = FileRotator;
|
|
183
|
+
module.exports = FileRotator;
|
package/lib/periodTrigger.js
CHANGED
|
@@ -1,68 +1,77 @@
|
|
|
1
|
-
const {EventEmitter} = require('events');
|
|
2
|
-
const {Rotate} = require('./customEvents');
|
|
3
|
-
const {processPeriod} = require('../util/configProcessors');
|
|
4
|
-
const {setTimeout, clearTimeout} = require('long-timeout');
|
|
1
|
+
const { EventEmitter } = require('events');
|
|
2
|
+
const { Rotate } = require('./customEvents');
|
|
3
|
+
const { processPeriod } = require('../util/configProcessors');
|
|
4
|
+
const { setTimeout, clearTimeout } = require('long-timeout');
|
|
5
5
|
|
|
6
6
|
const getNextRotation = (period, lastRotation) => {
|
|
7
7
|
var date = new Date();
|
|
8
8
|
|
|
9
9
|
let nextRotation;
|
|
10
10
|
switch (period.unit) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
11
|
+
case 'ms':
|
|
12
|
+
// Hidden millisecond period for debugging.
|
|
13
|
+
if (lastRotation) {
|
|
14
|
+
nextRotation = lastRotation + period.num;
|
|
15
|
+
} else {
|
|
16
|
+
nextRotation = Date.now() + period.num;
|
|
17
|
+
}
|
|
18
|
+
break;
|
|
19
|
+
case 'h':
|
|
20
|
+
if (lastRotation) {
|
|
21
|
+
nextRotation = lastRotation + period.num * 60 * 60 * 1000;
|
|
22
|
+
} else {
|
|
23
|
+
// First time: top of the next hour.
|
|
24
|
+
nextRotation = Date.UTC(
|
|
25
|
+
date.getUTCFullYear(),
|
|
26
|
+
date.getUTCMonth(),
|
|
27
|
+
date.getUTCDate(),
|
|
28
|
+
date.getUTCHours() + 1,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
case 'd':
|
|
33
|
+
if (lastRotation) {
|
|
34
|
+
nextRotation = lastRotation + period.num * 24 * 60 * 60 * 1000;
|
|
35
|
+
} else {
|
|
36
|
+
// First time: start of tomorrow (i.e. at the coming midnight) UTC.
|
|
37
|
+
nextRotation = Date.UTC(
|
|
38
|
+
date.getUTCFullYear(),
|
|
39
|
+
date.getUTCMonth(),
|
|
40
|
+
date.getUTCDate() + 1,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
case 'w':
|
|
45
|
+
// Currently, always on Sunday morning at 00:00:00 (UTC).
|
|
46
|
+
if (lastRotation) {
|
|
47
|
+
nextRotation = lastRotation + period.num * 7 * 24 * 60 * 60 * 1000;
|
|
48
|
+
} else {
|
|
49
|
+
// First time: this coming Sunday.
|
|
50
|
+
nextRotation = Date.UTC(
|
|
51
|
+
date.getUTCFullYear(),
|
|
52
|
+
date.getUTCMonth(),
|
|
53
|
+
date.getUTCDate() + (7 - date.getUTCDay()),
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
case 'm':
|
|
58
|
+
if (lastRotation) {
|
|
59
|
+
nextRotation = Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + period.num, 1);
|
|
60
|
+
} else {
|
|
61
|
+
// First time: the start of the next month.
|
|
62
|
+
nextRotation = Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 1);
|
|
63
|
+
}
|
|
64
|
+
break;
|
|
65
|
+
case 'y':
|
|
66
|
+
if (lastRotation) {
|
|
67
|
+
nextRotation = Date.UTC(date.getUTCFullYear() + period.num, 0, 1);
|
|
68
|
+
} else {
|
|
69
|
+
// First time: the start of the next year.
|
|
70
|
+
nextRotation = Date.UTC(date.getUTCFullYear() + 1, 0, 1);
|
|
71
|
+
}
|
|
72
|
+
break;
|
|
73
|
+
default:
|
|
74
|
+
throw new Error(`Invalid period scope: "${period.unit}"`);
|
|
66
75
|
}
|
|
67
76
|
|
|
68
77
|
return nextRotation;
|
|
@@ -97,10 +106,13 @@ class PeriodTrigger extends EventEmitter {
|
|
|
97
106
|
clearTimeout(this._task);
|
|
98
107
|
}
|
|
99
108
|
this._rotateAt = getNextRotation(this._period, this._rotateAt);
|
|
100
|
-
this._task = setTimeout(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
109
|
+
this._task = setTimeout(
|
|
110
|
+
() => {
|
|
111
|
+
this._emitRotate();
|
|
112
|
+
this.setNextTask();
|
|
113
|
+
},
|
|
114
|
+
Math.max(this._rotateAt - Date.now(), 0),
|
|
115
|
+
);
|
|
104
116
|
// Prevent timeout from keeping Node.js alive
|
|
105
117
|
this._task.unref();
|
|
106
118
|
}
|
|
@@ -110,4 +122,4 @@ class PeriodTrigger extends EventEmitter {
|
|
|
110
122
|
}
|
|
111
123
|
}
|
|
112
124
|
|
|
113
|
-
module.exports = PeriodTrigger;
|
|
125
|
+
module.exports = PeriodTrigger;
|
package/lib/thresholdTrigger.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
const {EventEmitter} = require('events');
|
|
2
|
-
const {Rotate} = require('./customEvents');
|
|
3
|
-
const {processSize} = require('../util/configProcessors');
|
|
1
|
+
const { EventEmitter } = require('events');
|
|
2
|
+
const { Rotate } = require('./customEvents');
|
|
3
|
+
const { processSize } = require('../util/configProcessors');
|
|
4
4
|
|
|
5
5
|
class ThresholdTrigger extends EventEmitter {
|
|
6
6
|
constructor(threshold) {
|
|
@@ -29,4 +29,4 @@ class ThresholdTrigger extends EventEmitter {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
module.exports = ThresholdTrigger;
|
|
32
|
+
module.exports = ThresholdTrigger;
|
package/lib/writeQueue.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const {EventEmitter} = require('events');
|
|
2
|
-
const {BytesWritten} = require('./customEvents');
|
|
1
|
+
const { EventEmitter } = require('events');
|
|
2
|
+
const { BytesWritten } = require('./customEvents');
|
|
3
3
|
|
|
4
4
|
class WriteQueue extends EventEmitter {
|
|
5
5
|
constructor() {
|
|
@@ -49,4 +49,4 @@ class WriteQueue extends EventEmitter {
|
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
-
module.exports = WriteQueue;
|
|
52
|
+
module.exports = WriteQueue;
|
package/package.json
CHANGED
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tryghost/bunyan-rotating-filestream",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"repository": "git@github.com:TryGhost/bunyan-rotating-filestream.git",
|
|
5
|
-
"author": "Ghost Foundation",
|
|
3
|
+
"version": "0.0.8",
|
|
6
4
|
"license": "MIT",
|
|
7
|
-
"
|
|
5
|
+
"author": "Ghost Foundation",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/TryGhost/framework.git",
|
|
9
|
+
"directory": "packages/bunyan-rotating-filestream"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"index.js",
|
|
13
|
+
"lib",
|
|
14
|
+
"util"
|
|
15
|
+
],
|
|
8
16
|
"main": "index.js",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"
|
|
14
|
-
"ship": "STATUS=$(git status --porcelain); echo $STATUS; if [ -z \"$STATUS\" ]; then yarn publish --access public && git push --follow-tags; fi"
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"long-timeout": "0.1.1"
|
|
15
22
|
},
|
|
16
23
|
"devDependencies": {
|
|
17
24
|
"bunyan": "1.8.15",
|
|
18
|
-
"
|
|
19
|
-
"eslint-plugin-ghost": "2.0.0",
|
|
20
|
-
"mkdirp": "1.0.4",
|
|
21
|
-
"mocha": "8.3.0",
|
|
22
|
-
"should": "13.2.3",
|
|
23
|
-
"sinon": "9.2.4"
|
|
25
|
+
"sinon": "22.0.0"
|
|
24
26
|
},
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
+
"scripts": {
|
|
28
|
+
"dev": "echo \"Implement me!\"",
|
|
29
|
+
"test": "NODE_ENV=testing vitest run --coverage",
|
|
30
|
+
"lint": "oxlint -c ../../.oxlintrc.json .",
|
|
31
|
+
"posttest": "pnpm run lint"
|
|
27
32
|
}
|
|
28
|
-
}
|
|
33
|
+
}
|
package/util/configProcessors.js
CHANGED
|
@@ -3,10 +3,10 @@ const processSize = (size) => {
|
|
|
3
3
|
b: 1,
|
|
4
4
|
k: 1024,
|
|
5
5
|
m: 1024 * 1024,
|
|
6
|
-
g: 1024 * 1024 * 1024
|
|
6
|
+
g: 1024 * 1024 * 1024,
|
|
7
7
|
};
|
|
8
|
-
|
|
9
|
-
if (typeof
|
|
8
|
+
|
|
9
|
+
if (typeof size === 'string') {
|
|
10
10
|
const [num, unit] = /^([1-9][0-9]*)([bkmg])$/.exec(size).slice(1);
|
|
11
11
|
return Number(num) * multipliers[unit];
|
|
12
12
|
} else {
|
|
@@ -17,21 +17,22 @@ const processSize = (size) => {
|
|
|
17
17
|
const processPeriod = (period) => {
|
|
18
18
|
const result = {
|
|
19
19
|
num: 1,
|
|
20
|
-
unit: 'd'
|
|
20
|
+
unit: 'd',
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
// Parse `period`.
|
|
24
24
|
if (period) {
|
|
25
|
-
var crackedperiod =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
var crackedperiod =
|
|
26
|
+
{
|
|
27
|
+
hourly: '1h',
|
|
28
|
+
daily: '1d',
|
|
29
|
+
weekly: '1w',
|
|
30
|
+
monthly: '1m',
|
|
31
|
+
yearly: '1y',
|
|
32
|
+
}[period] || period;
|
|
32
33
|
|
|
33
34
|
const [num, unit] = /^([1-9][0-9]*)([hdwmy]|ms)$/.exec(crackedperiod).slice(1);
|
|
34
|
-
|
|
35
|
+
|
|
35
36
|
result.num = Number(num);
|
|
36
37
|
result.unit = unit;
|
|
37
38
|
}
|
|
@@ -41,5 +42,5 @@ const processPeriod = (period) => {
|
|
|
41
42
|
|
|
42
43
|
module.exports = {
|
|
43
44
|
processSize,
|
|
44
|
-
processPeriod
|
|
45
|
-
};
|
|
45
|
+
processPeriod,
|
|
46
|
+
};
|
package/.editorconfig
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# http://editorconfig.org
|
|
2
|
-
|
|
3
|
-
root = true
|
|
4
|
-
|
|
5
|
-
[*]
|
|
6
|
-
charset = utf-8
|
|
7
|
-
indent_style = space
|
|
8
|
-
indent_size = 4
|
|
9
|
-
end_of_line = lf
|
|
10
|
-
insert_final_newline = true
|
|
11
|
-
trim_trailing_whitespace = true
|
|
12
|
-
|
|
13
|
-
[*.hbs]
|
|
14
|
-
insert_final_newline = false
|
|
15
|
-
|
|
16
|
-
[*.json]
|
|
17
|
-
indent_size = 2
|
|
18
|
-
|
|
19
|
-
[*.md]
|
|
20
|
-
trim_trailing_whitespace = false
|
|
21
|
-
|
|
22
|
-
[*.{yml,yaml}]
|
|
23
|
-
indent_size = 2
|
|
24
|
-
|
|
25
|
-
[Makefile]
|
|
26
|
-
indent_style = tab
|
package/.eslintrc.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
name: Test
|
|
2
|
-
on:
|
|
3
|
-
pull_request:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- main
|
|
7
|
-
jobs:
|
|
8
|
-
build:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
strategy:
|
|
11
|
-
matrix:
|
|
12
|
-
node: [ 10, 12 ]
|
|
13
|
-
env:
|
|
14
|
-
FORCE_COLOR: 1
|
|
15
|
-
name: Node ${{ matrix.node }}
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v2
|
|
18
|
-
- uses: actions/setup-node@v2
|
|
19
|
-
with:
|
|
20
|
-
node-version: ${{ matrix.node }}
|
|
21
|
-
|
|
22
|
-
- run: yarn
|
|
23
|
-
- run: yarn test
|
package/test/.eslintrc.js
DELETED
package/test/filerotate.test.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
const sinon = require('sinon');
|
|
2
|
-
const assert = require('assert');
|
|
3
|
-
const sandbox = sinon.createSandbox();
|
|
4
|
-
const bunyan = require('bunyan');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const RotatingFileStream = require('../index');
|
|
8
|
-
const fs = require('fs').promises;
|
|
9
|
-
const testConfig = {
|
|
10
|
-
path: 'logs/foo.log',
|
|
11
|
-
totalFiles: 10,
|
|
12
|
-
threshold: '1k',
|
|
13
|
-
rotateExisting: true,
|
|
14
|
-
gzip: true
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
function delay(ms) {
|
|
18
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
describe('RotatingFileStream', function () {
|
|
22
|
-
before(async function () {
|
|
23
|
-
await fs.mkdir(path.parse(testConfig.path).dir, {
|
|
24
|
-
recursive: true
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
afterEach(function () {
|
|
29
|
-
sandbox.restore();
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
it('Processes client configuration', async function () {
|
|
33
|
-
const rfs = new RotatingFileStream(testConfig);
|
|
34
|
-
assert.strictEqual(rfs._path, testConfig.path);
|
|
35
|
-
await rfs.end();
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('Writes log files', async function () {
|
|
39
|
-
const stream = new RotatingFileStream(testConfig);
|
|
40
|
-
const logger = bunyan.createLogger({
|
|
41
|
-
name: 'foo',
|
|
42
|
-
streams: [{
|
|
43
|
-
stream
|
|
44
|
-
}]
|
|
45
|
-
});
|
|
46
|
-
for (let i = 0; i < 100; i++) {
|
|
47
|
-
logger.info('Testing ' + i);
|
|
48
|
-
}
|
|
49
|
-
await delay(0); // immediate delay causes logs to sync
|
|
50
|
-
await stream.end();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('Sets up period correctly', async function () {
|
|
54
|
-
const stream = new RotatingFileStream({
|
|
55
|
-
period: '1w',
|
|
56
|
-
...testConfig
|
|
57
|
-
});
|
|
58
|
-
bunyan.createLogger({
|
|
59
|
-
name: 'foo',
|
|
60
|
-
streams: [{
|
|
61
|
-
stream
|
|
62
|
-
}]
|
|
63
|
-
});
|
|
64
|
-
await stream.end();
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it('Sets up long periods correctly', async function () {
|
|
68
|
-
const stream = new RotatingFileStream({
|
|
69
|
-
period: '1y',
|
|
70
|
-
...testConfig
|
|
71
|
-
});
|
|
72
|
-
bunyan.createLogger({
|
|
73
|
-
name: 'foo',
|
|
74
|
-
streams: [{
|
|
75
|
-
stream
|
|
76
|
-
}]
|
|
77
|
-
});
|
|
78
|
-
await stream.end();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('Goes fast with short periods', async function () {
|
|
82
|
-
const stream = new RotatingFileStream({
|
|
83
|
-
period: '50ms',
|
|
84
|
-
...testConfig
|
|
85
|
-
});
|
|
86
|
-
const logger = bunyan.createLogger({
|
|
87
|
-
name: 'foo',
|
|
88
|
-
streams: [{
|
|
89
|
-
stream
|
|
90
|
-
}]
|
|
91
|
-
});
|
|
92
|
-
for (let i = 0; i < 10; i++) {
|
|
93
|
-
logger.info(`Testing ${i}!`);
|
|
94
|
-
await delay(50);
|
|
95
|
-
}
|
|
96
|
-
await stream.end();
|
|
97
|
-
});
|
|
98
|
-
});
|
package/test/utils/assertions.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Custom Should Assertions
|
|
3
|
-
*
|
|
4
|
-
* Add any custom assertions to this file.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
// Example Assertion
|
|
8
|
-
// should.Assertion.add('ExampleAssertion', function () {
|
|
9
|
-
// this.params = {operator: 'to be a valid Example Assertion'};
|
|
10
|
-
// this.obj.should.be.an.Object;
|
|
11
|
-
// });
|
package/test/utils/index.js
DELETED
package/test/utils/overrides.js
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
// This file is required before any test is run
|
|
2
|
-
|
|
3
|
-
// Taken from the should wiki, this is how to make should global
|
|
4
|
-
// Should is a global in our eslint test config
|
|
5
|
-
global.should = require('should').noConflict();
|
|
6
|
-
should.extend();
|
|
7
|
-
|
|
8
|
-
// Sinon is a simple case
|
|
9
|
-
// Sinon is a global in our eslint test config
|
|
10
|
-
global.sinon = require('sinon');
|