diff --git a/lib/transport.js b/lib/transport.js index f830d0a55..210337125 100644 --- a/lib/transport.js +++ b/lib/transport.js @@ -233,7 +233,10 @@ function transport (fullOptions) { }) }) - usesMultistream = options.targets.length + options.pipelines.length > 1 + const targetsHaveLevel = options.targets.some(t => t.level != null) + const pipelinesHaveLevel = options.pipelines.some(p => p.some(t => t.level != null)) + usesMultistream = options.targets.length + options.pipelines.length > 1 || + targetsHaveLevel || pipelinesHaveLevel } else if (pipeline) { target = bundlerOverrides['pino-worker'] || join(__dirname, 'worker.js') options.pipelines = [pipeline.map((dest) => { diff --git a/lib/worker.js b/lib/worker.js index 0bc035a00..9d3d65775 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -123,7 +123,10 @@ module.exports = async function ({ targets, pipelines, levels, dedupe }) { // OR // // pino.transport({ pipeline: ... }) - if (targetStreams.length === 1) { + // + // When a `level` is set on the single target/pipeline, fall through to the + // multistream path so the per-target level filter is honored (#1996). + if (targetStreams.length === 1 && targetStreams[0].level == null) { return targetStreams[0].stream } else { return build(process, { diff --git a/test/transport/core.test.js b/test/transport/core.test.js index 2d7b0e905..b8f7e0d23 100644 --- a/test/transport/core.test.js +++ b/test/transport/core.test.js @@ -267,6 +267,59 @@ test('pino.transport with two files and dedupe', async (t) => { }) }) +test('pino.transport with single target in targets array honors level filter (#1996)', async (t) => { + const destination = file() + const transport = pino.transport({ + targets: [{ + level: 'error', + target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), + options: { destination } + }] + }) + t.after(transport.end.bind(transport)) + const instance = pino({ level: 'debug' }, transport) + instance.debug('debug-msg') + instance.info('info-msg') + instance.warn('warn-msg') + instance.error('error-msg') + await watchForWrite(destination, 'error-msg') + const lines = (await readFile(destination, 'utf8')).trim().split('\n').filter(Boolean) + assert.equal(lines.length, 1, 'only error should be written to destination') + const result = JSON.parse(lines[0]) + delete result.time + assert.deepEqual(result, { + pid, + hostname, + level: 50, + msg: 'error-msg' + }) +}) + +test('pino.transport with single target in targets array without level keeps logger.level gating', async (t) => { + const destination = file() + const transport = pino.transport({ + targets: [{ + target: join(__dirname, '..', 'fixtures', 'to-file-transport.js'), + options: { destination } + }] + }) + t.after(transport.end.bind(transport)) + const instance = pino({ level: 'debug' }, transport) + instance.debug('debug-msg') + instance.info('info-msg') + await watchForWrite(destination, 'info-msg') + const lines = (await readFile(destination, 'utf8')).trim().split('\n').filter(Boolean) + assert.equal(lines.length, 2, 'both debug and info reach the single target when no per-target level is set') + const first = JSON.parse(lines[0]) + delete first.time + assert.deepEqual(first, { + pid, + hostname, + level: 20, + msg: 'debug-msg' + }) +}) + test('pino.transport with an array including a pino-pretty destination', async (t) => { const dest1 = file() const dest2 = file()