-
-
Notifications
You must be signed in to change notification settings - Fork 11
/
pino-roll.js
executable file
·144 lines (129 loc) · 4.26 KB
/
pino-roll.js
1
2
3
4
5
6
7
8
9
10
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
'use strict'
const SonicBoom = require('sonic-boom')
const {
buildFileName,
checkFileRemoval,
createSymlink,
detectLastNumber,
parseSize,
parseFrequency,
getNext,
getFileSize,
validateLimitOptions,
parseDate,
validateDateFormat
} = require('./lib/utils')
/**
* A function that returns a string path to the base file name
*
* @typedef {function} LogFilePath
* @returns {string}
*/
/**
* @typedef {object} Options
*
* @property {string|LogFilePath} file - Absolute or relative path to the log file.
* Your application needs the write right on the parent folder.
* Number will be appended to this file name.
* When the parent folder already contains numbered files, numbering will continue based on the highest number.
* If this path does not exist, the logger with throw an error unless you set `mkdir` to `true`.
*
* @property {string|number} size? - When specified, the maximum size of a given log file.
* Can be combined with frequency.
* Use 'k', 'm' and 'g' to express values in KB, MB or GB.
* Numerical values will be considered as MB.
*
* @property {string|number} frequency? - When specified, the amount of time a given log file is used.
* Can be combined with size.
* Use 'daily' or 'hourly' to rotate file every day (or every hour).
* Existing file within the current day (or hour) will be re-used.
* Numerical values will be considered as a number of milliseconds.
* Using a numerical value will always create a new file upon startup.
*
* @property {string} extension? - When specified, appends a file extension after the file number.
*
* @property {boolean} symlink? - When specified, creates a symlink to the current log file.
*
* @property {LimitOptions} limit? - strategy used to remove oldest files when rotating them.
*
* @property {string} dateFormat? - When specified, appends the current date/time to the file name in the provided format.
* Supports date formats from `date-fns` (see: https://date-fns.org/v4.1.0/docs/format), such as 'yyyy-MM-dd' and 'yyyy-MM-dd-hh'.
*/
/**
* @typedef {object} LimitOptions
*
* @property {number} count? -number of log files, **in addition to the currently used file**.
*/
/**
* @typedef {Options & import('sonic-boom').SonicBoomOpts} PinoRollOptions
*/
/**
* Creates a Pino transport (a Sonic-boom stream) to writing into files.
* Automatically rolls your files based on a given frequency, size, or both.
*
* @param {PinoRollOptions} options - to configure file destionation, and rolling rules.
* @returns {SonicBoom} the Sonic boom steam, usabled as Pino transport.
*/
module.exports = async function ({
file,
size,
frequency,
extension,
limit,
symlink,
dateFormat,
...opts
} = {}) {
validateLimitOptions(limit)
validateDateFormat(dateFormat)
const frequencySpec = parseFrequency(frequency)
let date = parseDate(dateFormat, frequencySpec, true)
let number = await detectLastNumber(file, frequencySpec?.start)
let fileName = buildFileName(file, date, number, extension)
const createdFileNames = [fileName]
let currentSize = await getFileSize(fileName)
const maxSize = parseSize(size)
const destination = new SonicBoom({ ...opts, dest: fileName })
if (symlink) {
createSymlink(fileName)
}
let rollTimeout
if (frequencySpec) {
destination.once('close', () => {
clearTimeout(rollTimeout)
})
scheduleRoll()
}
if (maxSize) {
destination.on('write', writtenSize => {
currentSize += writtenSize
if (fileName === destination.file && currentSize >= maxSize) {
currentSize = 0
fileName = buildFileName(file, date, ++number, extension)
// delay to let the destination finish its write
destination.once('drain', roll)
}
})
}
function roll () {
destination.reopen(fileName)
if (symlink) {
createSymlink(fileName)
}
if (limit) {
createdFileNames.push(fileName)
checkFileRemoval(createdFileNames, limit)
}
}
function scheduleRoll () {
clearTimeout(rollTimeout)
rollTimeout = setTimeout(() => {
date = parseDate(dateFormat, frequencySpec)
fileName = buildFileName(file, date, ++number, extension)
roll()
frequencySpec.next = getNext(frequency)
scheduleRoll()
}, frequencySpec.next - Date.now())
}
return destination
}