Skip to content

fix: honor per-target level filter on single-element targets array#2432

Open
mcollina wants to merge 1 commit intomainfrom
fix/1996-single-target-level-filter
Open

fix: honor per-target level filter on single-element targets array#2432
mcollina wants to merge 1 commit intomainfrom
fix/1996-single-target-level-filter

Conversation

@mcollina
Copy link
Copy Markdown
Member

@mcollina mcollina commented May 5, 2026

Summary

  • Fixes level filter not work for transports #1996 — the per-target level filter was silently ignored when pino.transport({ targets: [...] }) contained a single element.
  • The single-target shortcut in lib/worker.js returned the raw stream directly, bypassing pino.multistream (which is the only place that consults targets[i].level).
  • Now, when level is set on the single target/pipeline, we fall through to the multistream path that already enforces per-target filtering. When level is not set, behavior is unchanged.

Root cause

lib/worker.js:126:

if (targetStreams.length === 1) {
  return targetStreams[0].stream
}

The level captured at line 85 (level: t.level) was collected and then discarded for the one-element case. So targets: [{ level: 'error', target: '...' }] emitted info/warn/debug logs anyway — the exact symptom reported in #1996.

Fix

lib/worker.js:

if (targetStreams.length === 1 && targetStreams[0].level == null) {
  return targetStreams[0].stream
}

lib/transport.js — extend usesMultistream so transportUsesMultistreamSym is set whenever per-target filtering will run in the worker. This keeps the existing formatters.level warning (pino.js:169) consistent: if filtering happens, the numeric level invariant must hold.

Scope

Intentionally minimal — fixes the case where level is set (the #1996 reproducer) without altering the no-level case. The remaining asymmetry (single target with no level inherits logger.level instead of defaulting to info like multistream does) is a separate semver-major decision and is left for a follow-up.

Test plan

  • New TDD test reproducing level filter not work for transports #1996 verbatim (targets: [{ level: 'error', ... }] → only error reaches destination)
  • New regression guard for the no-level case (single target with no per-target level keeps logger.level gating)
  • test/transport/core.test.js (38 tests) — all pass
  • test/transport/{pipeline,targets,sync-true,sync-false,crash,uses-pino-config}.test.js — all pass
  • test/transport-stream.test.js, test/multistream.test.js — all pass
  • test/levels.test.js, test/is-level-enabled.test.js — all pass
  • npm run lint — clean

Closes #1996

The single-target shortcut in `lib/worker.js` returned the raw stream
directly, bypassing `pino.multistream` and silently dropping the
`targets[i].level` filter. As a result, `pino.transport({ targets:
[{ level: 'error', ... }] })` emitted info/warn/debug logs even
though the user explicitly requested error-only filtering.

Skip the shortcut when a `level` is set on the single target/pipeline
and route through the multistream path that already enforces per-target
filtering. Update `usesMultistream` in `lib/transport.js` so
`transportUsesMultistreamSym` is set whenever per-target filtering will
run, keeping the existing `formatters.level` warning consistent.

Behavior is unchanged when no `level` is set on the single target.

Closes #1996
@jsumners
Copy link
Copy Markdown
Member

jsumners commented May 5, 2026

CI is failing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

level filter not work for transports

2 participants