pino
Version:
super fast, all natural json logger
135 lines (110 loc) • 3.37 kB
JavaScript
const { createRequire } = require('module')
const getCaller = require('./caller')
const { join, isAbsolute } = require('path')
let onExit
if (global.WeakRef && global.WeakMap && global.FinalizationRegistry) {
// This require MUST be top level otherwise the transport would
// not work from within Jest as it hijacks require.
onExit = require('on-exit-leak-free')
}
const ThreadStream = require('thread-stream')
function setupOnExit (stream) {
/* istanbul ignore next */
if (onExit) {
// This is leak free, it does not leave event handlers
onExit.register(stream, autoEnd)
stream.on('close', function () {
onExit.unregister(stream)
})
} else {
const fn = autoEnd.bind(null, stream)
process.once('beforeExit', fn)
process.once('exit', fn)
stream.on('close', function () {
process.removeListener('beforeExit', fn)
process.removeListener('exit', fn)
})
}
}
function buildStream (filename, workerData, workerOpts) {
const stream = new ThreadStream({
filename,
workerData,
workerOpts
})
stream.on('ready', onReady)
stream.on('close', function () {
process.removeListener('exit', onExit)
})
process.on('exit', onExit)
function onReady () {
process.removeListener('exit', onExit)
stream.unref()
if (workerOpts.autoEnd !== false) {
setupOnExit(stream)
}
}
function onExit () {
if (stream.closed) {
return
}
stream.flushSync()
stream.end()
}
return stream
}
function autoEnd (stream) {
stream.ref()
stream.flushSync()
stream.end()
stream.once('close', function () {
stream.unref()
})
}
function transport (fullOptions) {
const { pipeline, targets, options = {}, worker = {}, caller = getCaller() } = fullOptions
// This function call MUST stay in the top-level function of this module
const callerRequire = createRequire(caller)
// This will be eventually modified by bundlers
const bundlerOverrides = '__bundlerPathsOverrides' in globalThis ? globalThis.__bundlerPathsOverrides : {}
let target = fullOptions.target
if (target && targets) {
throw new Error('only one of target or targets can be specified')
}
if (targets) {
target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js')
options.targets = targets.map((dest) => {
return {
...dest,
target: fixTarget(dest.target)
}
})
} else if (fullOptions.pipeline) {
target = bundlerOverrides['pino-pipeline-worker'] || join(__dirname, 'worker-pipeline.js')
options.targets = pipeline.map((dest) => {
return {
...dest,
target: fixTarget(dest.target)
}
})
}
return buildStream(fixTarget(target), options, worker)
function fixTarget (origin) {
origin = bundlerOverrides[origin] || origin
if (isAbsolute(origin) || origin.indexOf('file://') === 0) {
return origin
}
switch (origin) {
// This hack should not be needed, however it would not work otherwise
// when testing it from this module and in examples.
case 'pino/file':
return join(__dirname, '..', 'file.js')
/* istanbul ignore next */
default:
// TODO we cannot test this on Windows.. might not work.
return callerRequire.resolve(origin)
}
}
}
module.exports = transport