diff --git a/docs/api.md b/docs/api.md index 1c503c6e3..6789e3fe3 100644 --- a/docs/api.md +++ b/docs/api.md @@ -1307,6 +1307,9 @@ module.exports = function build () { } ``` +Note that _any `'error'`_ event emitted by the transport must be considered a fatal error and the process must be terminated. +Error events are not recoverable. + For more on transports, how they work, and how to create them see the [`Transports documentation`](/docs/transports.md). * See [`Transports`](/docs/transports.md) diff --git a/test/fixtures/crashing-transport.js b/test/fixtures/crashing-transport.js new file mode 100644 index 000000000..1f3d46ee9 --- /dev/null +++ b/test/fixtures/crashing-transport.js @@ -0,0 +1,13 @@ +const { Writable } = require('node:stream') + +module.exports = () => + new Writable({ + autoDestroy: true, + write (chunk, enc, cb) { + setImmediate(() => { + /* eslint-disable no-empty */ + for (let i = 0; i < 1e3; i++) {} + process.exit(0) + }) + } + }) diff --git a/test/transport/core.test.js b/test/transport/core.test.js index 44449db22..3bd55fedd 100644 --- a/test/transport/core.test.js +++ b/test/transport/core.test.js @@ -3,6 +3,7 @@ const os = require('node:os') const { join } = require('node:path') const { once } = require('node:events') +const { setImmediate: immediate } = require('node:timers/promises') const { readFile, writeFile } = require('node:fs').promises const { watchFileCreated, watchForWrite, file } = require('../helper') const { test } = require('tap') @@ -12,10 +13,8 @@ const strip = require('strip-ansi') const execa = require('execa') const writer = require('flush-write-stream') const rimraf = require('rimraf') -const { promisify } = require('node:util') const { tmpdir } = os -const immediate = promisify(setImmediate) const pid = process.pid const hostname = os.hostname() diff --git a/test/transport/crash.test.js b/test/transport/crash.test.js new file mode 100644 index 000000000..b5e31fe2a --- /dev/null +++ b/test/transport/crash.test.js @@ -0,0 +1,34 @@ +'use strict' + +const { join } = require('node:path') +const { once } = require('node:events') +const { setImmediate: immediate } = require('node:timers/promises') +const { test } = require('tap') +const pino = require('../../') + +test('pino.transport emits error if the worker exits with 0 unexpectably', async ({ same, teardown, equal }) => { + // This test will take 10s, because flushSync waits for 10s + const transport = pino.transport({ + target: join(__dirname, '..', 'fixtures', 'crashing-transport.js'), + sync: true + }) + teardown(transport.end.bind(transport)) + + await once(transport, 'ready') + + let maybeError + transport.on('error', (err) => { + maybeError = err + }) + + const logger = pino(transport) + for (let i = 0; i < 100000; i++) { + logger.info('hello') + } + + await once(transport.worker, 'exit') + + await immediate() + + same(maybeError.message, 'the worker has exited') +})