Skip to content

Commit

Permalink
fix: building sonic boom safe (#316)
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonmulia authored Mar 17, 2022
1 parent 3d471da commit 482a474
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 3 deletions.
5 changes: 2 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ const { isColorSupported } = require('colorette')
const pump = require('pump')
const { Transform } = require('readable-stream')
const abstractTransport = require('pino-abstract-transport')
const sonic = require('sonic-boom')
const sjs = require('secure-json-parse')

const colors = require('./lib/colors')
const { ERROR_LIKE_KEYS, MESSAGE_KEY, TIMESTAMP_KEY, LEVEL_KEY, LEVEL_NAMES } = require('./lib/constants')
const {
Expand All @@ -17,6 +15,7 @@ const {
prettifyMetadata,
prettifyObject,
prettifyTime,
buildSafeSonicBoom,
filterLog
} = require('./lib/utils')

Expand Down Expand Up @@ -227,7 +226,7 @@ function build (opts = {}) {
if (typeof opts.destination === 'object' && typeof opts.destination.write === 'function') {
destination = opts.destination
} else {
destination = sonic({
destination = buildSafeSonicBoom({
dest: opts.destination || 1,
append: opts.append,
mkdir: opts.mkdir,
Expand Down
67 changes: 67 additions & 0 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

const clone = require('rfdc')({ circles: true })
const dateformat = require('dateformat')
const SonicBoom = require('sonic-boom')
const stringifySafe = require('fast-safe-stringify')
const { isMainThread } = require('worker_threads')
const defaultColorizer = require('./colors')()
const {
DATE_FORMAT,
Expand All @@ -23,6 +25,7 @@ module.exports = {
prettifyMetadata,
prettifyObject,
prettifyTime,
buildSafeSonicBoom,
filterLog
}

Expand Down Expand Up @@ -580,3 +583,67 @@ function filterLog (log, ignoreKeys) {
})
return logCopy
}

function noop () {}

/**
* Creates a safe SonicBoom instance
*
* @param {object} opts Options for SonicBoom
*
* @returns {object} A new SonicBoom stream
*/
function buildSafeSonicBoom (opts) {
const stream = new SonicBoom(opts)
stream.on('error', filterBrokenPipe)
// if we are sync: false, we must flush on exit
if (!opts.sync && isMainThread) {
setupOnExit(stream)
}
return stream

function filterBrokenPipe (err) {
if (err.code === 'EPIPE') {
stream.write = noop
stream.end = noop
stream.flushSync = noop
stream.destroy = noop
return
}
stream.removeListener('error', filterBrokenPipe)
}
}

function setupOnExit (stream) {
/* istanbul ignore next */
if (global.WeakRef && global.WeakMap && global.FinalizationRegistry) {
// This is leak free, it does not leave event handlers
const onExit = require('on-exit-leak-free')

onExit.register(stream, autoEnd)

stream.on('close', function () {
onExit.unregister(stream)
})
}
}

/* istanbul ignore next */
function autoEnd (stream, eventName) {
// This check is needed only on some platforms

if (stream.destroyed) {
return
}

if (eventName === 'beforeExit') {
// We still have an event loop, let's use it
stream.flush()
stream.on('drain', function () {
stream.end()
})
} else {
// We do not have an event loop, so flush synchronously
stream.flushSync()
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
"dateformat": "^4.6.3",
"fast-safe-stringify": "^2.0.7",
"joycon": "^3.1.1",
"on-exit-leak-free": "^0.2.0",
"pino-abstract-transport": "^0.5.0",
"pump": "^3.0.0",
"readable-stream": "^3.6.0",
Expand Down
48 changes: 48 additions & 0 deletions test/lib/utils.public.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
const tap = require('tap')
const getColorizer = require('../../lib/colors')
const utils = require('../../lib/utils')
const rimraf = require('rimraf')
const { join } = require('path')
const fs = require('fs')

tap.test('prettifyErrorLog', t => {
const { prettifyErrorLog } = utils
Expand Down Expand Up @@ -428,3 +431,48 @@ tap.test('#filterLog with circular references', t => {

t.end()
})

tap.test('buildSafeSonicBoom', t => {
const { buildSafeSonicBoom } = utils

function noop () {}

const file = () => {
const dest = join(__dirname, `${process.pid}-${process.hrtime().toString()}`)
const fd = fs.openSync(dest, 'w')
return { dest, fd }
}

t.test('should not write when error emitted and code is "EPIPE"', async t => {
t.plan(1)

const { fd, dest } = file()
const stream = buildSafeSonicBoom({ sync: true, fd, mkdir: true })
t.teardown(() => rimraf(dest, noop))

stream.emit('error', { code: 'EPIPE' })
stream.write('will not work')

const dataFile = fs.readFileSync(dest)
t.equal(dataFile.length, 0)
})

t.test('should stream.write works when error code is not "EPIPE"', async t => {
t.plan(3)
const { fd, dest } = file()
const stream = buildSafeSonicBoom({ sync: true, fd, mkdir: true })

t.teardown(() => rimraf(dest, noop))

stream.on('error', () => t.pass('error emitted'))

stream.emit('error', 'fake error description')

t.ok(stream.write('will work'))

const dataFile = fs.readFileSync(dest)
t.equal(dataFile.toString(), 'will work')
})

t.end()
})

0 comments on commit 482a474

Please sign in to comment.