react-hook-eslint 1.0.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.
- package/CONTRIBUTING.md +30 -0
- package/LICENSE +21 -0
- package/README.md +85 -0
- package/SECURITY.md +68 -0
- package/docs/api.md +1487 -0
- package/docs/asynchronous.md +40 -0
- package/docs/benchmarks.md +55 -0
- package/docs/browser.md +227 -0
- package/docs/bundling.md +40 -0
- package/docs/child-loggers.md +95 -0
- package/docs/ecosystem.md +84 -0
- package/docs/help.md +345 -0
- package/docs/lts.md +64 -0
- package/docs/pretty.md +35 -0
- package/docs/redaction.md +135 -0
- package/docs/transports.md +1238 -0
- package/docs/web.md +269 -0
- package/docsify/sidebar.md +26 -0
- package/favicon-16x16.png +0 -0
- package/favicon-32x32.png +0 -0
- package/favicon.ico +0 -0
- package/file.js +12 -0
- package/index.html +55 -0
- package/lib/caller.js +30 -0
- package/lib/constants.js +28 -0
- package/lib/deprecations.js +8 -0
- package/lib/levels.js +241 -0
- package/lib/meta.js +3 -0
- package/lib/multistream.js +188 -0
- package/lib/proto.js +234 -0
- package/lib/redaction.js +118 -0
- package/lib/symbols.js +74 -0
- package/lib/time.js +11 -0
- package/lib/tools.js +394 -0
- package/lib/transport-stream.js +56 -0
- package/lib/transport.js +167 -0
- package/lib/worker.js +194 -0
- package/lib/writer.js +44 -0
- package/package.json +38 -0
- package/pino-banner.png +0 -0
- package/pino-logo-hire.png +0 -0
- package/pino-tree.png +0 -0
- package/pino.d.ts +889 -0
- package/pino.js +38 -0
- package/pretty-demo.png +0 -0
- package/tsconfig.json +14 -0
package/lib/tools.js
ADDED
@@ -0,0 +1,394 @@
|
|
1
|
+
'use strict'
|
2
|
+
|
3
|
+
/* eslint no-prototype-builtins: 0 */
|
4
|
+
|
5
|
+
const format = require('quick-format-unescaped')
|
6
|
+
const { mapHttpRequest, mapHttpResponse } = require('pino-std-serializers')
|
7
|
+
const SonicBoom = require('sonic-boom')
|
8
|
+
const onExit = require('on-exit-leak-free')
|
9
|
+
const {
|
10
|
+
lsCacheSym,
|
11
|
+
chindingsSym,
|
12
|
+
writeSym,
|
13
|
+
serializersSym,
|
14
|
+
formatOptsSym,
|
15
|
+
endSym,
|
16
|
+
stringifiersSym,
|
17
|
+
stringifySym,
|
18
|
+
stringifySafeSym,
|
19
|
+
wildcardFirstSym,
|
20
|
+
nestedKeySym,
|
21
|
+
formattersSym,
|
22
|
+
messageKeySym,
|
23
|
+
errorKeySym,
|
24
|
+
nestedKeyStrSym,
|
25
|
+
msgPrefixSym
|
26
|
+
} = require('./symbols')
|
27
|
+
const { isMainThread } = require('worker_threads')
|
28
|
+
const transport = require('./transport')
|
29
|
+
|
30
|
+
function noop () {
|
31
|
+
}
|
32
|
+
|
33
|
+
function genLog (level, hook) {
|
34
|
+
if (!hook) return LOG
|
35
|
+
|
36
|
+
return function hookWrappedLog (...args) {
|
37
|
+
hook.call(this, args, LOG, level)
|
38
|
+
}
|
39
|
+
|
40
|
+
function LOG (o, ...n) {
|
41
|
+
if (typeof o === 'object') {
|
42
|
+
let msg = o
|
43
|
+
if (o !== null) {
|
44
|
+
if (o.method && o.headers && o.socket) {
|
45
|
+
o = mapHttpRequest(o)
|
46
|
+
} else if (typeof o.setHeader === 'function') {
|
47
|
+
o = mapHttpResponse(o)
|
48
|
+
}
|
49
|
+
}
|
50
|
+
let formatParams
|
51
|
+
if (msg === null && n.length === 0) {
|
52
|
+
formatParams = [null]
|
53
|
+
} else {
|
54
|
+
msg = n.shift()
|
55
|
+
formatParams = n
|
56
|
+
}
|
57
|
+
// We do not use a coercive check for `msg` as it is
|
58
|
+
// measurably slower than the explicit checks.
|
59
|
+
if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
|
60
|
+
msg = this[msgPrefixSym] + msg
|
61
|
+
}
|
62
|
+
this[writeSym](o, format(msg, formatParams, this[formatOptsSym]), level)
|
63
|
+
} else {
|
64
|
+
let msg = o === undefined ? n.shift() : o
|
65
|
+
|
66
|
+
// We do not use a coercive check for `msg` as it is
|
67
|
+
// measurably slower than the explicit checks.
|
68
|
+
if (typeof this[msgPrefixSym] === 'string' && msg !== undefined && msg !== null) {
|
69
|
+
msg = this[msgPrefixSym] + msg
|
70
|
+
}
|
71
|
+
this[writeSym](null, format(msg, n, this[formatOptsSym]), level)
|
72
|
+
}
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
// magically escape strings for json
|
77
|
+
// relying on their charCodeAt
|
78
|
+
// everything below 32 needs JSON.stringify()
|
79
|
+
// 34 and 92 happens all the time, so we
|
80
|
+
// have a fast case for them
|
81
|
+
function asString (str) {
|
82
|
+
let result = ''
|
83
|
+
let last = 0
|
84
|
+
let found = false
|
85
|
+
let point = 255
|
86
|
+
const l = str.length
|
87
|
+
if (l > 100) {
|
88
|
+
return JSON.stringify(str)
|
89
|
+
}
|
90
|
+
for (var i = 0; i < l && point >= 32; i++) {
|
91
|
+
point = str.charCodeAt(i)
|
92
|
+
if (point === 34 || point === 92) {
|
93
|
+
result += str.slice(last, i) + '\\'
|
94
|
+
last = i
|
95
|
+
found = true
|
96
|
+
}
|
97
|
+
}
|
98
|
+
if (!found) {
|
99
|
+
result = str
|
100
|
+
} else {
|
101
|
+
result += str.slice(last)
|
102
|
+
}
|
103
|
+
return point < 32 ? JSON.stringify(str) : '"' + result + '"'
|
104
|
+
}
|
105
|
+
|
106
|
+
function asJson (obj, msg, num, time) {
|
107
|
+
const stringify = this[stringifySym]
|
108
|
+
const stringifySafe = this[stringifySafeSym]
|
109
|
+
const stringifiers = this[stringifiersSym]
|
110
|
+
const end = this[endSym]
|
111
|
+
const chindings = this[chindingsSym]
|
112
|
+
const serializers = this[serializersSym]
|
113
|
+
const formatters = this[formattersSym]
|
114
|
+
const messageKey = this[messageKeySym]
|
115
|
+
const errorKey = this[errorKeySym]
|
116
|
+
let data = this[lsCacheSym][num] + time
|
117
|
+
|
118
|
+
// we need the child bindings added to the output first so instance logged
|
119
|
+
// objects can take precedence when JSON.parse-ing the resulting log line
|
120
|
+
data = data + chindings
|
121
|
+
|
122
|
+
let value
|
123
|
+
if (formatters.log) {
|
124
|
+
obj = formatters.log(obj)
|
125
|
+
}
|
126
|
+
const wildcardStringifier = stringifiers[wildcardFirstSym]
|
127
|
+
let propStr = ''
|
128
|
+
for (const key in obj) {
|
129
|
+
value = obj[key]
|
130
|
+
if (Object.prototype.hasOwnProperty.call(obj, key) && value !== undefined) {
|
131
|
+
if (serializers[key]) {
|
132
|
+
value = serializers[key](value)
|
133
|
+
} else if (key === errorKey && serializers.err) {
|
134
|
+
value = serializers.err(value)
|
135
|
+
}
|
136
|
+
|
137
|
+
const stringifier = stringifiers[key] || wildcardStringifier
|
138
|
+
|
139
|
+
switch (typeof value) {
|
140
|
+
case 'undefined':
|
141
|
+
case 'function':
|
142
|
+
continue
|
143
|
+
case 'number':
|
144
|
+
/* eslint no-fallthrough: "off" */
|
145
|
+
if (Number.isFinite(value) === false) {
|
146
|
+
value = null
|
147
|
+
}
|
148
|
+
// this case explicitly falls through to the next one
|
149
|
+
case 'boolean':
|
150
|
+
if (stringifier) value = stringifier(value)
|
151
|
+
break
|
152
|
+
case 'string':
|
153
|
+
value = (stringifier || asString)(value)
|
154
|
+
break
|
155
|
+
default:
|
156
|
+
value = (stringifier || stringify)(value, stringifySafe)
|
157
|
+
}
|
158
|
+
if (value === undefined) continue
|
159
|
+
const strKey = asString(key)
|
160
|
+
propStr += ',' + strKey + ':' + value
|
161
|
+
}
|
162
|
+
}
|
163
|
+
|
164
|
+
let msgStr = ''
|
165
|
+
if (msg !== undefined) {
|
166
|
+
value = serializers[messageKey] ? serializers[messageKey](msg) : msg
|
167
|
+
const stringifier = stringifiers[messageKey] || wildcardStringifier
|
168
|
+
|
169
|
+
switch (typeof value) {
|
170
|
+
case 'function':
|
171
|
+
break
|
172
|
+
case 'number':
|
173
|
+
/* eslint no-fallthrough: "off" */
|
174
|
+
if (Number.isFinite(value) === false) {
|
175
|
+
value = null
|
176
|
+
}
|
177
|
+
// this case explicitly falls through to the next one
|
178
|
+
case 'boolean':
|
179
|
+
if (stringifier) value = stringifier(value)
|
180
|
+
msgStr = ',"' + messageKey + '":' + value
|
181
|
+
break
|
182
|
+
case 'string':
|
183
|
+
value = (stringifier || asString)(value)
|
184
|
+
msgStr = ',"' + messageKey + '":' + value
|
185
|
+
break
|
186
|
+
default:
|
187
|
+
value = (stringifier || stringify)(value, stringifySafe)
|
188
|
+
msgStr = ',"' + messageKey + '":' + value
|
189
|
+
}
|
190
|
+
}
|
191
|
+
|
192
|
+
if (this[nestedKeySym] && propStr) {
|
193
|
+
// place all the obj properties under the specified key
|
194
|
+
// the nested key is already formatted from the constructor
|
195
|
+
return data + this[nestedKeyStrSym] + propStr.slice(1) + '}' + msgStr + end
|
196
|
+
} else {
|
197
|
+
return data + propStr + msgStr + end
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
function asChindings (instance, bindings) {
|
202
|
+
let value
|
203
|
+
let data = instance[chindingsSym]
|
204
|
+
const stringify = instance[stringifySym]
|
205
|
+
const stringifySafe = instance[stringifySafeSym]
|
206
|
+
const stringifiers = instance[stringifiersSym]
|
207
|
+
const wildcardStringifier = stringifiers[wildcardFirstSym]
|
208
|
+
const serializers = instance[serializersSym]
|
209
|
+
const formatter = instance[formattersSym].bindings
|
210
|
+
bindings = formatter(bindings)
|
211
|
+
|
212
|
+
for (const key in bindings) {
|
213
|
+
value = bindings[key]
|
214
|
+
const valid = key !== 'level' &&
|
215
|
+
key !== 'serializers' &&
|
216
|
+
key !== 'formatters' &&
|
217
|
+
key !== 'customLevels' &&
|
218
|
+
bindings.hasOwnProperty(key) &&
|
219
|
+
value !== undefined
|
220
|
+
if (valid === true) {
|
221
|
+
value = serializers[key] ? serializers[key](value) : value
|
222
|
+
value = (stringifiers[key] || wildcardStringifier || stringify)(value, stringifySafe)
|
223
|
+
if (value === undefined) continue
|
224
|
+
data += ',"' + key + '":' + value
|
225
|
+
}
|
226
|
+
}
|
227
|
+
return data
|
228
|
+
}
|
229
|
+
|
230
|
+
function hasBeenTampered (stream) {
|
231
|
+
return stream.write !== stream.constructor.prototype.write
|
232
|
+
}
|
233
|
+
|
234
|
+
const hasNodeCodeCoverage = process.env.NODE_V8_COVERAGE || process.env.V8_COVERAGE
|
235
|
+
|
236
|
+
function buildSafeSonicBoom (opts) {
|
237
|
+
const stream = new SonicBoom(opts)
|
238
|
+
stream.on('error', filterBrokenPipe)
|
239
|
+
// If we are sync: false, we must flush on exit
|
240
|
+
// We must disable this if there is node code coverage due to
|
241
|
+
// https://github.com/nodejs/node/issues/49344#issuecomment-1741776308.
|
242
|
+
if (!hasNodeCodeCoverage && !opts.sync && isMainThread) {
|
243
|
+
onExit.register(stream, autoEnd)
|
244
|
+
|
245
|
+
stream.on('close', function () {
|
246
|
+
onExit.unregister(stream)
|
247
|
+
})
|
248
|
+
}
|
249
|
+
return stream
|
250
|
+
|
251
|
+
function filterBrokenPipe (err) {
|
252
|
+
// Impossible to replicate across all operating systems
|
253
|
+
/* istanbul ignore next */
|
254
|
+
if (err.code === 'EPIPE') {
|
255
|
+
// If we get EPIPE, we should stop logging here
|
256
|
+
// however we have no control to the consumer of
|
257
|
+
// SonicBoom, so we just overwrite the write method
|
258
|
+
stream.write = noop
|
259
|
+
stream.end = noop
|
260
|
+
stream.flushSync = noop
|
261
|
+
stream.destroy = noop
|
262
|
+
return
|
263
|
+
}
|
264
|
+
stream.removeListener('error', filterBrokenPipe)
|
265
|
+
stream.emit('error', err)
|
266
|
+
}
|
267
|
+
}
|
268
|
+
|
269
|
+
function autoEnd (stream, eventName) {
|
270
|
+
// This check is needed only on some platforms
|
271
|
+
/* istanbul ignore next */
|
272
|
+
if (stream.destroyed) {
|
273
|
+
return
|
274
|
+
}
|
275
|
+
|
276
|
+
if (eventName === 'beforeExit') {
|
277
|
+
// We still have an event loop, let's use it
|
278
|
+
stream.flush()
|
279
|
+
stream.on('drain', function () {
|
280
|
+
stream.end()
|
281
|
+
})
|
282
|
+
} else {
|
283
|
+
// For some reason istanbul is not detecting this, but it's there
|
284
|
+
/* istanbul ignore next */
|
285
|
+
// We do not have an event loop, so flush synchronously
|
286
|
+
stream.flushSync()
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
290
|
+
function createArgsNormalizer (defaultOptions) {
|
291
|
+
return function normalizeArgs (instance, caller, opts = {}, stream) {
|
292
|
+
// support stream as a string
|
293
|
+
if (typeof opts === 'string') {
|
294
|
+
stream = buildSafeSonicBoom({ dest: opts })
|
295
|
+
opts = {}
|
296
|
+
} else if (typeof stream === 'string') {
|
297
|
+
if (opts && opts.transport) {
|
298
|
+
throw Error('only one of option.transport or stream can be specified')
|
299
|
+
}
|
300
|
+
stream = buildSafeSonicBoom({ dest: stream })
|
301
|
+
} else if (opts instanceof SonicBoom || opts.writable || opts._writableState) {
|
302
|
+
stream = opts
|
303
|
+
opts = {}
|
304
|
+
} else if (opts.transport) {
|
305
|
+
if (opts.transport instanceof SonicBoom || opts.transport.writable || opts.transport._writableState) {
|
306
|
+
throw Error('option.transport do not allow stream, please pass to option directly. e.g. pino(transport)')
|
307
|
+
}
|
308
|
+
if (opts.transport.targets && opts.transport.targets.length && opts.formatters && typeof opts.formatters.level === 'function') {
|
309
|
+
throw Error('option.transport.targets do not allow custom level formatters')
|
310
|
+
}
|
311
|
+
|
312
|
+
let customLevels
|
313
|
+
if (opts.customLevels) {
|
314
|
+
customLevels = opts.useOnlyCustomLevels ? opts.customLevels : Object.assign({}, opts.levels, opts.customLevels)
|
315
|
+
}
|
316
|
+
stream = transport({ caller, ...opts.transport, levels: customLevels })
|
317
|
+
}
|
318
|
+
opts = Object.assign({}, defaultOptions, opts)
|
319
|
+
opts.serializers = Object.assign({}, defaultOptions.serializers, opts.serializers)
|
320
|
+
opts.formatters = Object.assign({}, defaultOptions.formatters, opts.formatters)
|
321
|
+
|
322
|
+
if (opts.prettyPrint) {
|
323
|
+
throw new Error('prettyPrint option is no longer supported, see the pino-pretty package (https://github.com/pinojs/pino-pretty)')
|
324
|
+
}
|
325
|
+
|
326
|
+
const { enabled, onChild } = opts
|
327
|
+
if (enabled === false) opts.level = 'silent'
|
328
|
+
if (!onChild) opts.onChild = noop
|
329
|
+
if (!stream) {
|
330
|
+
if (!hasBeenTampered(process.stdout)) {
|
331
|
+
// If process.stdout.fd is undefined, it means that we are running
|
332
|
+
// in a worker thread. Let's assume we are logging to file descriptor 1.
|
333
|
+
stream = buildSafeSonicBoom({ fd: process.stdout.fd || 1 })
|
334
|
+
} else {
|
335
|
+
stream = process.stdout
|
336
|
+
}
|
337
|
+
}
|
338
|
+
return { opts, stream }
|
339
|
+
}
|
340
|
+
}
|
341
|
+
|
342
|
+
function stringify (obj, stringifySafeFn) {
|
343
|
+
try {
|
344
|
+
return JSON.stringify(obj)
|
345
|
+
} catch (_) {
|
346
|
+
try {
|
347
|
+
const stringify = stringifySafeFn || this[stringifySafeSym]
|
348
|
+
return stringify(obj)
|
349
|
+
} catch (_) {
|
350
|
+
return '"[unable to serialize, circular reference is too complex to analyze]"'
|
351
|
+
}
|
352
|
+
}
|
353
|
+
}
|
354
|
+
|
355
|
+
function buildFormatters (level, bindings, log) {
|
356
|
+
return {
|
357
|
+
level,
|
358
|
+
bindings,
|
359
|
+
log
|
360
|
+
}
|
361
|
+
}
|
362
|
+
|
363
|
+
/**
|
364
|
+
* Convert a string integer file descriptor to a proper native integer
|
365
|
+
* file descriptor.
|
366
|
+
*
|
367
|
+
* @param {string} destination The file descriptor string to attempt to convert.
|
368
|
+
*
|
369
|
+
* @returns {Number}
|
370
|
+
*/
|
371
|
+
function normalizeDestFileDescriptor (destination) {
|
372
|
+
const fd = Number(destination)
|
373
|
+
if (typeof destination === 'string' && Number.isFinite(fd)) {
|
374
|
+
return fd
|
375
|
+
}
|
376
|
+
// destination could be undefined if we are in a worker
|
377
|
+
if (destination === undefined) {
|
378
|
+
// This is stdout in UNIX systems
|
379
|
+
return 1
|
380
|
+
}
|
381
|
+
return destination
|
382
|
+
}
|
383
|
+
|
384
|
+
module.exports = {
|
385
|
+
noop,
|
386
|
+
buildSafeSonicBoom,
|
387
|
+
asChindings,
|
388
|
+
asJson,
|
389
|
+
genLog,
|
390
|
+
createArgsNormalizer,
|
391
|
+
stringify,
|
392
|
+
buildFormatters,
|
393
|
+
normalizeDestFileDescriptor
|
394
|
+
}
|
@@ -0,0 +1,56 @@
|
|
1
|
+
'use strict'
|
2
|
+
|
3
|
+
const { realImport, realRequire } = require('real-require')
|
4
|
+
|
5
|
+
module.exports = loadTransportStreamBuilder
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Loads & returns a function to build transport streams
|
9
|
+
* @param {string} target
|
10
|
+
* @returns {Promise<function(object): Promise<import('node:stream').Writable>>}
|
11
|
+
* @throws {Error} In case the target module does not export a function
|
12
|
+
*/
|
13
|
+
async function loadTransportStreamBuilder (target) {
|
14
|
+
let fn
|
15
|
+
try {
|
16
|
+
const toLoad = target.startsWith('file://') ? target : 'file://' + target
|
17
|
+
|
18
|
+
if (toLoad.endsWith('.ts') || toLoad.endsWith('.cts')) {
|
19
|
+
// TODO: add support for the TSM modules loader ( https://github.com/lukeed/tsm ).
|
20
|
+
if (process[Symbol.for('ts-node.register.instance')]) {
|
21
|
+
realRequire('ts-node/register')
|
22
|
+
} else if (process.env && process.env.TS_NODE_DEV) {
|
23
|
+
realRequire('ts-node-dev')
|
24
|
+
}
|
25
|
+
// TODO: Support ES imports once tsc, tap & ts-node provide better compatibility guarantees.
|
26
|
+
fn = realRequire(decodeURIComponent(target))
|
27
|
+
} else {
|
28
|
+
fn = (await realImport(toLoad))
|
29
|
+
}
|
30
|
+
} catch (error) {
|
31
|
+
// See this PR for details: https://github.com/pinojs/thread-stream/pull/34
|
32
|
+
if ((error.code === 'ENOTDIR' || error.code === 'ERR_MODULE_NOT_FOUND')) {
|
33
|
+
fn = realRequire(target)
|
34
|
+
} else if (error.code === undefined || error.code === 'ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING') {
|
35
|
+
// When bundled with pkg, an undefined error is thrown when called with realImport
|
36
|
+
// When bundled with pkg and using node v20, an ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING error is thrown when called with realImport
|
37
|
+
// More info at: https://github.com/pinojs/thread-stream/issues/143
|
38
|
+
try {
|
39
|
+
fn = realRequire(decodeURIComponent(target))
|
40
|
+
} catch {
|
41
|
+
throw error
|
42
|
+
}
|
43
|
+
} else {
|
44
|
+
throw error
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
// Depending on how the default export is performed, and on how the code is
|
49
|
+
// transpiled, we may find cases of two nested "default" objects.
|
50
|
+
// See https://github.com/pinojs/pino/issues/1243#issuecomment-982774762
|
51
|
+
if (typeof fn === 'object') fn = fn.default
|
52
|
+
if (typeof fn === 'object') fn = fn.default
|
53
|
+
if (typeof fn !== 'function') throw Error('exported worker is not a function')
|
54
|
+
|
55
|
+
return fn
|
56
|
+
}
|
package/lib/transport.js
ADDED
@@ -0,0 +1,167 @@
|
|
1
|
+
'use strict'
|
2
|
+
|
3
|
+
const { createRequire } = require('module')
|
4
|
+
const getCallers = require('./caller')
|
5
|
+
const { join, isAbsolute, sep } = require('node:path')
|
6
|
+
const sleep = require('atomic-sleep')
|
7
|
+
const onExit = require('on-exit-leak-free')
|
8
|
+
const ThreadStream = require('thread-stream')
|
9
|
+
|
10
|
+
function setupOnExit (stream) {
|
11
|
+
// This is leak free, it does not leave event handlers
|
12
|
+
onExit.register(stream, autoEnd)
|
13
|
+
onExit.registerBeforeExit(stream, flush)
|
14
|
+
|
15
|
+
stream.on('close', function () {
|
16
|
+
onExit.unregister(stream)
|
17
|
+
})
|
18
|
+
}
|
19
|
+
|
20
|
+
function buildStream (filename, workerData, workerOpts, sync) {
|
21
|
+
const stream = new ThreadStream({
|
22
|
+
filename,
|
23
|
+
workerData,
|
24
|
+
workerOpts,
|
25
|
+
sync
|
26
|
+
})
|
27
|
+
|
28
|
+
stream.on('ready', onReady)
|
29
|
+
stream.on('close', function () {
|
30
|
+
process.removeListener('exit', onExit)
|
31
|
+
})
|
32
|
+
|
33
|
+
process.on('exit', onExit)
|
34
|
+
|
35
|
+
function onReady () {
|
36
|
+
process.removeListener('exit', onExit)
|
37
|
+
stream.unref()
|
38
|
+
|
39
|
+
if (workerOpts.autoEnd !== false) {
|
40
|
+
setupOnExit(stream)
|
41
|
+
}
|
42
|
+
}
|
43
|
+
|
44
|
+
function onExit () {
|
45
|
+
/* istanbul ignore next */
|
46
|
+
if (stream.closed) {
|
47
|
+
return
|
48
|
+
}
|
49
|
+
stream.flushSync()
|
50
|
+
// Apparently there is a very sporadic race condition
|
51
|
+
// that in certain OS would prevent the messages to be flushed
|
52
|
+
// because the thread might not have been created still.
|
53
|
+
// Unfortunately we need to sleep(100) in this case.
|
54
|
+
sleep(100)
|
55
|
+
stream.end()
|
56
|
+
}
|
57
|
+
|
58
|
+
return stream
|
59
|
+
}
|
60
|
+
|
61
|
+
function autoEnd (stream) {
|
62
|
+
stream.ref()
|
63
|
+
stream.flushSync()
|
64
|
+
stream.end()
|
65
|
+
stream.once('close', function () {
|
66
|
+
stream.unref()
|
67
|
+
})
|
68
|
+
}
|
69
|
+
|
70
|
+
function flush (stream) {
|
71
|
+
stream.flushSync()
|
72
|
+
}
|
73
|
+
|
74
|
+
function transport (fullOptions) {
|
75
|
+
const { pipeline, targets, levels, dedupe, worker = {}, caller = getCallers(), sync = false } = fullOptions
|
76
|
+
|
77
|
+
const options = {
|
78
|
+
...fullOptions.options
|
79
|
+
}
|
80
|
+
|
81
|
+
// Backwards compatibility
|
82
|
+
const callers = typeof caller === 'string' ? [caller] : caller
|
83
|
+
|
84
|
+
// This will be eventually modified by bundlers
|
85
|
+
const bundlerOverrides = '__bundlerPathsOverrides' in globalThis ? globalThis.__bundlerPathsOverrides : {}
|
86
|
+
|
87
|
+
let target = fullOptions.target
|
88
|
+
|
89
|
+
if (target && targets) {
|
90
|
+
throw new Error('only one of target or targets can be specified')
|
91
|
+
}
|
92
|
+
|
93
|
+
if (targets) {
|
94
|
+
target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
|
95
|
+
options.targets = targets.filter(dest => dest.target).map((dest) => {
|
96
|
+
return {
|
97
|
+
...dest,
|
98
|
+
target: fixTarget(dest.target)
|
99
|
+
}
|
100
|
+
})
|
101
|
+
options.pipelines = targets.filter(dest => dest.pipeline).map((dest) => {
|
102
|
+
return dest.pipeline.map((t) => {
|
103
|
+
return {
|
104
|
+
...t,
|
105
|
+
level: dest.level, // duplicate the pipeline `level` property defined in the upper level
|
106
|
+
target: fixTarget(t.target)
|
107
|
+
}
|
108
|
+
})
|
109
|
+
})
|
110
|
+
} else if (pipeline) {
|
111
|
+
target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
|
112
|
+
options.pipelines = [pipeline.map((dest) => {
|
113
|
+
return {
|
114
|
+
...dest,
|
115
|
+
target: fixTarget(dest.target)
|
116
|
+
}
|
117
|
+
})]
|
118
|
+
}
|
119
|
+
|
120
|
+
if (levels) {
|
121
|
+
options.levels = levels
|
122
|
+
}
|
123
|
+
|
124
|
+
if (dedupe) {
|
125
|
+
options.dedupe = dedupe
|
126
|
+
}
|
127
|
+
|
128
|
+
options.pinoWillSendConfig = true
|
129
|
+
|
130
|
+
return buildStream(fixTarget(target), options, worker, sync)
|
131
|
+
|
132
|
+
function fixTarget (origin) {
|
133
|
+
origin = bundlerOverrides[origin] || origin
|
134
|
+
|
135
|
+
if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
|
136
|
+
return origin
|
137
|
+
}
|
138
|
+
|
139
|
+
if (origin === 'pino/file') {
|
140
|
+
return join(__dirname, '..', 'file.js')
|
141
|
+
}
|
142
|
+
|
143
|
+
let fixTarget
|
144
|
+
|
145
|
+
for (const filePath of callers) {
|
146
|
+
try {
|
147
|
+
const context = filePath === 'node:repl'
|
148
|
+
? process.cwd() + sep
|
149
|
+
: filePath
|
150
|
+
|
151
|
+
fixTarget = createRequire(context).resolve(origin)
|
152
|
+
break
|
153
|
+
} catch (err) {
|
154
|
+
// Silent catch
|
155
|
+
continue
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
if (!fixTarget) {
|
160
|
+
throw new Error(`unable to determine transport target for "${origin}"`)
|
161
|
+
}
|
162
|
+
|
163
|
+
return fixTarget
|
164
|
+
}
|
165
|
+
}
|
166
|
+
|
167
|
+
module.exports = transport
|