Skip to content

console: write timeEnd/timeLog to stdout instead of stderr - #32423

Closed
robobun wants to merge 3 commits into
mainfrom
farm/c1343155/console-time-stdout
Closed

console: write timeEnd/timeLog to stdout instead of stderr#32423
robobun wants to merge 3 commits into
mainfrom
farm/c1343155/console-time-stdout

Conversation

@robobun

@robobun robobun commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Fixes #12031.

Repro

$ bun -e 'console.time(); console.timeEnd()' > /dev/null
[0.00ms] default

$ node -e 'console.time(); console.timeEnd()' > /dev/null
(no output)

Node routes console.timeEnd() and console.timeLog() through console.log, so the timing output goes to stdout. Bun wrote it to stderr, which meant redirecting stdout did not capture the timing output and it leaked past pipes.

Cause

Bun__ConsoleObject__timeEnd and Bun__ConsoleObject__timeLog in src/jsc/ConsoleObject.rs used the stderr helpers: Output::print_elapsed (which targets stderr via pretty_error!), Output::print_error/print_errorln, the console's error_writer(), and enable_ansi_colors_stderr(). console.count() in the same file already goes to stdout.

Fix

Switch to the stdout equivalents: print_elapsed_stdout, print/println, writer(), and enable_ansi_colors_stdout(). print_elapsed_stdout already existed in bun_core::output.

Verification

test/js/web/console/console-timeLog.test.ts updated to assert output lands on stdout with stderr empty, covering timeEnd (default, empty, and named labels) and timeLog (with and without extra args).

  • USE_SYSTEM_BUN=1 bun test test/js/web/console/console-timeLog.test.ts → 5 fail
  • bun bd test test/js/web/console/console-timeLog.test.ts → 5 pass

The reporter also noted that Node routes through console.log itself (so overriding console.log intercepts the output). Bun's global console is native and does not call back into console.log; that is a larger design question left out of scope here.

Node.js routes console.timeEnd() and console.timeLog() output through
console.log, which writes to stdout. Bun was writing to stderr, so
redirecting stdout did not capture the timing output.

Switch the elapsed-time print, the label print, and the extra-argument
formatter in timeLog to their stdout equivalents.

Fixes #12031
@coderabbitai

coderabbitai Bot commented Jun 16, 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

Run ID: a7a8d041-4bdb-4adc-adcb-11dac4b8a16a

📥 Commits

Reviewing files that changed from the base of the PR and between f6ed445 and 76c5882.

📒 Files selected for processing (1)
  • test/js/web/console/console-timeLog.test.ts

Walkthrough

Bun__ConsoleObject__timeEnd and Bun__ConsoleObject__timeLog in ConsoleObject.rs are changed to route all timing output (elapsed duration and labels) to stdout instead of stderr. The test file adds new spawn-based tests asserting stderr is empty and stdout contains the timing lines, and corrects an existing integration test to read from stdout.

Changes

console.timeEnd/timeLog stdout redirect

Layer / File(s) Summary
timeEnd and timeLog stdout routing
src/jsc/ConsoleObject.rs
Bun__ConsoleObject__timeEnd switches Output::print_elapsed_stderr/errorln calls to Output::print_elapsed_stdout/println. Bun__ConsoleObject__timeLog similarly switches its prefix/body printing and per-argument writer from error_writer()/stderr ANSI coloring to writer()/stdout ANSI coloring.
Updated and new stdout-routing tests
test/js/web/console/console-timeLog.test.ts
Existing timeEnd empty-label and non-empty-label tests are updated to assert stderr is empty and match formatted output on stdout. Three new spawn-based tests are added to verify timeEnd (default label) and timeLog (multiple lines) write to stdout only, and the integration test is fixed to read from stdout and concurrently await process output.

Possibly related PRs

  • oven-sh/bun#32360: Touches Bun__ConsoleObject__timeEnd formatting for console.timeEnd("") to fix emitted newline/line behavior — the same function modified here, and this PR's tests assert the same "exactly one trailing newline" invariant.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: switching console.timeEnd and console.timeLog output from stderr to stdout.
Description check ✅ Passed The description includes a clear repro, identifies the cause, explains the fix, and documents verification steps, matching the template structure well.
Linked Issues check ✅ Passed The PR fully addresses issue #12031 by routing console.timeEnd and console.timeLog output to stdout instead of stderr, with comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the console.timeEnd/timeLog stdout routing; no out-of-scope modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@robobun

robobun commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 3:33 PM PT - Jun 16th, 2026

@robobun, your commit 76c5882 has 1 failures in Build #62936 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 32423

That installs a local version of the PR into your bun-32423 executable, so you can run:

bun-32423 --bun

@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.

LGTM — mechanical swap from stderr to stdout helpers, matches Node behavior, with regression tests.

Extended reasoning...

Overview

This PR fixes #12031 by routing console.timeEnd() and console.timeLog() output to stdout instead of stderr, matching Node.js behavior. The change touches two files:

  • src/jsc/ConsoleObject.rs (~10 lines): swaps Output::print_elapsedprint_elapsed_stdout, print_error/print_errorlnprint/println, error_writer()writer(), and enable_ansi_colors_stderr()enable_ansi_colors_stdout(). Each replacement is the direct stdout counterpart of the original helper, all of which already exist in bun_core::output and ConsoleObject.
  • test/js/web/console/console-timeLog.test.ts: flips existing assertions from stderr to stdout and adds two new regression tests verifying timeEnd/timeLog write to stdout with stderr empty.

Security risks

None. This only changes which file descriptor receives diagnostic timing output. No user input handling, auth, crypto, or permission logic is involved.

Level of scrutiny

Low. The change is a one-to-one substitution of well-established, symmetric output helpers — I verified each stdout counterpart exists and behaves as the mirror of the stderr version (print_elapsed_stdout uses pretty! vs pretty_error!; print/println target Destination::Stdout; ConsoleObject::writer() is the stdout buffered writer). console.count() in the same file already follows this stdout pattern, so this brings timeEnd/timeLog in line with it.

Other factors

  • No CODEOWNERS entries cover the modified files.
  • The bug hunting system found no issues.
  • The PR includes before/after verification (USE_SYSTEM_BUN=1 fails, bun bd passes) and the test updates assert both that stdout receives the output and that stderr is empty, which would catch any partial regression.
  • The author explicitly scoped out the larger "route through console.log override" question, which is reasonable for this fix.

@robobun

robobun commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Diff is ready and tests pass locally (bun bd test test/js/web/console/console-timeLog.test.ts → 5 pass; fails with USE_SYSTEM_BUN=1).

CI builds 62914 and 62926 both failed on infrastructure before any build job started: every lane reports Failed to create agent / Image not found: linux-*-v37. No test jobs ran. Needs a re-run once the agent images are available.

@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/web/console/console-timeLog.test.ts`:
- Line 64: The test "should log to console correctly" should be marked as
concurrent to align with the other tests in the file and improve test suite
parallelism. Change the test declaration from it("should log to console
correctly", async () => { to it.concurrent("should log to console correctly",
async () => { by adding .concurrent to the it function call.
🪄 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: cf375811-f67e-4e03-bc50-7557a1044a19

📥 Commits

Reviewing files that changed from the base of the PR and between 66cd2fd and f6ed445.

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

Comment thread test/js/web/console/console-timeLog.test.ts Outdated

@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.

LGTM — mechanical stderr→stdout swap for console.timeEnd/timeLog with matching test updates.

Extended reasoning...

Overview

This PR fixes #12031 by changing console.timeEnd() and console.timeLog() to write to stdout instead of stderr, matching Node.js behavior. The change is in src/jsc/ConsoleObject.rs (two functions, ~6 lines each) and swaps stderr-targeting helpers for their existing stdout equivalents: print_elapsedprint_elapsed_stdout, print_error/print_errorlnprint/println, error_writer()writer(), and enable_ansi_colors_stderr()enable_ansi_colors_stdout(). All target functions already exist in bun_core::output and on ConsoleObject. Tests in console-timeLog.test.ts are updated to assert output on stdout with stderr empty, plus two new regression tests linked to the issue.

Security risks

None. This only changes which file descriptor timing output is written to. No auth, crypto, parsing, or untrusted-input handling is touched.

Level of scrutiny

Low. The change is mechanical (1:1 substitution of stderr helpers with stdout helpers), localized to two adjacent functions, and follows the same pattern already used by console.count() in the same file. The PR description verified before/after test results.

Other factors

  • No prior human reviewer comments to address.
  • Test coverage is good: existing tests were flipped to assert stdout/stderr correctly, and new tests cover the default label, named labels, and timeLog with extra args.
  • I confirmed print_elapsed_stdout, enable_ansi_colors_stdout, and ConsoleObject::writer() all exist with the expected signatures.
  • The PR explicitly scopes out the larger question of routing through a JS-overridable console.log, which is reasonable.

@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.

LGTM — mechanical stderr→stdout swap for console.timeEnd/timeLog to match Node, with tests.

Extended reasoning...

Overview

This PR changes Bun__ConsoleObject__timeEnd and Bun__ConsoleObject__timeLog in src/jsc/ConsoleObject.rs to write timing output to stdout instead of stderr, matching Node.js behavior (fixes #12031). The source change is 8 lines: each stderr helper is swapped for its existing stdout counterpart (print_elapsedprint_elapsed_stdout, print_errorln/print_errorprintln/print, error_writer()writer(), enable_ansi_colors_stderr()enable_ansi_colors_stdout()). I verified all target functions already exist in bun_core::output and on ConsoleObject with the expected stdout semantics. The test file is updated to assert stderr is empty and stdout contains the timing lines, and adds three new spawn-based tests covering the default label and timeLog with extra args.

Security risks

None. This only changes which file descriptor receives formatted timing strings; no input parsing, auth, or privilege boundaries are touched.

Level of scrutiny

Low. The change is a 1:1 mechanical substitution between symmetric helper pairs that already exist, mirrors how console.count() in the same file already routes to stdout, and is covered by 5 subprocess tests that pass on the new build and fail on system Bun (demonstrating the behavior change). The one bot nitpick (mark the integration test it.concurrent) was addressed in 76c5882.

Other factors

No CODEOWNERS entries cover these paths, no outstanding human review comments, and the only CI failures noted in the thread were infrastructure (agent image not found) rather than test failures. The PR description explicitly scopes out the larger question of routing through a JS-overridable console.log, which is reasonable for this fix.

@robobun

robobun commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #34240, which also switches to Node's label: <duration> grammar and the 1000 ms seconds threshold (this PR kept the [<n>ms] label format and 1500 ms threshold).

@robobun

robobun commented Aug 13, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: timeEnd/timeLog moving to stdout is being fixed as part of #37128, which reworks console output as a whole. (#34240, which had already superseded this PR, is closed for the same reason.)

@robobun robobun closed this Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

console.timeEnd behaviour different in bun from node

1 participant