Skip to content

fix(console): send console.trace to stderr with a "Trace:" prefix - #32638

Open
0xfandom wants to merge 4 commits into
oven-sh:mainfrom
0xfandom:claude/console-trace-stderr
Open

fix(console): send console.trace to stderr with a "Trace:" prefix#32638
0xfandom wants to merge 4 commits into
oven-sh:mainfrom
0xfandom:claude/console-trace-stderr

Conversation

@0xfandom

Copy link
Copy Markdown

What does this PR do?

Fixes #19952. console.trace() wrote its output to stdout with no prefix; Node writes to stderr prefixed with Trace:.

$ bun  -e 'console.trace("hi")'   # before
hi                                # ← stdout, no prefix
      at [eval]:1:9

$ node -e 'console.trace("hi")'
Trace: hi                         # ← stderr, "Trace:" prefix
    at [eval]:1:9

The native console handler (src/jsc/ConsoleObject.rs) only routed Warning/Error/Assert messages to the stderr writer, and never emitted a label for the Trace message type. This change routes Trace to the stderr writer (stream selection + color mode) and prints Trace: before the formatted arguments, or a bare Trace when called with no arguments — matching Node:

console.trace("hi")      -> Trace: hi
console.trace("x=%d", 5) -> Trace: x=5
console.trace({a:1})     -> Trace: { a: 1 }
console.trace()          -> Trace

The .zig reference sibling has the same gap; left untouched since it is not compiled.

How did you verify your code works?

Added console.trace tests in test/js/node/console/console.test.ts that spawn a subprocess and assert stdout is empty, stderr starts with Trace: hi\n / bare Trace\n (no-args) and contains the stack frames, plus format-specifier handling. Tests pass on the debug build and fail on the released bun (USE_SYSTEM_BUN=1). Existing console suites (console.test.ts, console-log.test.ts) still pass: 14 / 14.

console.trace wrote the formatted arguments and stack trace to stdout with no
prefix. Node writes them to stderr, prefixed with "Trace: " (or a bare "Trace"
when called with no arguments). Route the Trace message type to the stderr
writer and emit the prefix before the formatted arguments.

Fixes oven-sh#19952

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ef3b512c-6852-46b7-a2a1-04b8342149e9

📥 Commits

Reviewing files that changed from the base of the PR and between d699e1b and c02d6da.

📒 Files selected for processing (1)
  • test/js/node/console/console.test.ts

Walkthrough

console.trace now writes to stderr, emits a Trace or Trace: prefix, and preserves stack trace output. Tests cover stream routing, formatting, stack lines, and group indentation.

Changes

console.trace stderr routing and prefix

Layer / File(s) Summary
Route and format console.trace output
src/jsc/ConsoleObject.rs
MessageType::Trace uses stderr locking, coloring, and writing. FormatOptions carries a prefix, which formatting paths use to emit Trace: or Trace before values and stack traces.
Spawn-based tests for console.trace behavior
test/js/node/console/console.test.ts
Adds a Bun process helper and tests stderr-only output, Trace prefixes, stack at lines, printf-style formatting, and group indentation.

Possibly related PRs

  • oven-sh/bun#36404: Both modify FormatOptions and format2 in ConsoleObject.rs.
  • oven-sh/bun#36470: Both modify shared console formatting and indentation behavior in ConsoleObject.rs.
  • oven-sh/bun#35040: Both update console.trace() routing to stderr in Bun console implementations.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and concisely describes the main change: routing console.trace output to stderr and adding the Trace: prefix.
Description check ✅ Passed The description fully addresses both required sections with clear explanation of the fix and comprehensive verification details including test coverage.
Linked Issues check ✅ Passed The PR directly addresses issue #19952 by routing console.trace to stderr, adding Trace: prefix, and preserving stack traces—all requirements met.
Out of Scope Changes check ✅ Passed All changes align with the scope of fixing console.trace behavior: stdout/stderr routing, prefix formatting, and test coverage match the linked issue requirements.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/node/console/console.test.ts`:
- Around line 115-126: The test cases "no arguments prints bare 'Trace'" and
"applies format specifiers" do not verify the subprocess exit code, which means
they could pass even if the execution fails. In both tests, destructure exitCode
from the run() result (along with stdout and stderr), and add an
expect(exitCode).toBe(0) assertion after the output assertions to ensure the
subprocess executed successfully before verifying the console output.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 88c702e5-5d6d-427c-8592-7a792c2ff0db

📥 Commits

Reviewing files that changed from the base of the PR and between 126e800 and 8195f15.

📒 Files selected for processing (2)
  • src/jsc/ConsoleObject.rs
  • test/js/node/console/console.test.ts

Comment thread test/js/node/console/console.test.ts
@0xfandom

Copy link
Copy Markdown
Author

Addressed in the latest commit: the no-args and format-specifier cases now assert stdout === "" and exitCode === 0, matching the first test.

@robobun

robobun commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

I ended up writing the same fix before finding this PR, so I'm not opening a competing one. Branch is at farm/4c33c10c/console-trace-stderr if anything there is useful.

One behavior worth folding in either way: Node puts the label after the console.group indent, and indents the bare header too.

console.group("G");
console.trace("x");
console.trace();
console.groupEnd();
node:                 this PR:
  Trace: x            Trace:   x
      at ...              at ...
  Trace               Trace
      at ...              at ...

It happens because format2 writes default_indent as its first bytes, so a write_all(b"Trace: ") placed before the format2 call lands ahead of the indent, and the no-args Trace\n path skips the indent entirely. I threaded the label through FormatOptions as a prefix that format2 emits right after write_indent, which keeps nested object lines indented under the group as well.

Happy either way, just flagging so it doesn't get lost.

format2 writes the indent as its first bytes, so writing "Trace: " before
calling it put the label ahead of the group indent, and the no-args path
skipped the indent entirely. Thread the label through FormatOptions as a
prefix that format2 emits right after the indent, and indent the bare
header, matching Node.
@0xfandom

Copy link
Copy Markdown
Author

Good catch — folded in as d699e1b. Threaded the label through FormatOptions as a prefix that format2 emits right after write_indent, and indented the bare Trace header too, so grouped output now matches Node:

console.group("G"); console.trace("x"); console.trace(); console.groupEnd(); console.trace("top");
  Trace: x
      at ...
  Trace
      at ...
Trace: top
      at ...

Added a test covering the grouped case. Full console suites still pass (90 pass / 0 fail).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
test/js/node/console/console.test.ts (2)

107-141: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Run independent subprocess cases concurrently.

These tests use isolated child processes and can use test.concurrent without sharing state. This avoids unnecessary serialization.

As per coding guidelines, independent subprocess suites should use test.concurrent.

Proposed fix
-  test("goes to stderr, not stdout", async () => {
+  test.concurrent("goes to stderr, not stdout", async () => {
-  test("no arguments prints bare 'Trace'", async () => {
+  test.concurrent("no arguments prints bare 'Trace'", async () => {
-  test("applies format specifiers", async () => {
+  test.concurrent("applies format specifiers", async () => {
-  test("label goes after the console.group indent", async () => {
+  test.concurrent("label goes after the console.group indent", async () => {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/node/console/console.test.ts` around lines 107 - 141, Update the four
independent console.trace tests in the surrounding test suite to use
test.concurrent, preserving each test’s existing assertions and subprocess
setup. Do not alter the test behavior or shared state handling.

Source: Coding guidelines


97-100: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Spread bunEnv for the child environment.

Use env: { ...bunEnv } instead of passing the shared harness object directly. This preserves test isolation if per-test environment overrides are added, especially once these cases run concurrently.

As per coding guidelines, “Follow existing harness conventions: spread bunEnv.”

Proposed fix
-      env: bunEnv,
+      env: { ...bunEnv },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/js/node/console/console.test.ts` around lines 97 - 100, Update the
Bun.spawn invocation in the console test to pass a shallow copy of bunEnv via
object spread in the env option, preserving the existing child process
configuration and isolating per-test environment changes.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/node/console/console.test.ts`:
- Around line 130-139: Update the test case around console.group and
console.trace to validate the ordered grouped trace headers rather than merely
checking their presence. Extract header lines from stderr while ignoring
stack-frame text, then assert the exact sequence “  Trace: x”, “  Trace”, and
“Trace: top”; keep the existing stdout and exitCode assertions unchanged.

---

Outside diff comments:
In `@test/js/node/console/console.test.ts`:
- Around line 107-141: Update the four independent console.trace tests in the
surrounding test suite to use test.concurrent, preserving each test’s existing
assertions and subprocess setup. Do not alter the test behavior or shared state
handling.
- Around line 97-100: Update the Bun.spawn invocation in the console test to
pass a shallow copy of bunEnv via object spread in the env option, preserving
the existing child process configuration and isolating per-test environment
changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e13aa570-4ab4-42cc-a12c-135a0332c7d9

📥 Commits

Reviewing files that changed from the base of the PR and between 9e634ca and d699e1b.

📒 Files selected for processing (2)
  • src/jsc/ConsoleObject.rs
  • test/js/node/console/console.test.ts

Comment thread test/js/node/console/console.test.ts
@0xfandom

Copy link
Copy Markdown
Author

Addressed in c02d6da — the group-indent test now extracts the header lines and asserts the exact ordered sequence [" Trace: x", " Trace", "Trace: top"] instead of the looser presence checks.

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator

Triage note: the stderr half of this is also being fixed in #37128, which reworks console output as a whole. What #37128 does not add is the Trace: / Trace header (its current head prints the bare message and the stack on stderr), so this PR is the one being kept for that, and #20020 was closed in its favor.

Once #37128 lands this will need a rebase: message_with_type_and_level_ is restructured there, and the stream selection hunks here drop out, leaving the header handling.

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.

console.trace() goes to stdout instead of stderr

2 participants