Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/jsc/ConsoleObject.rs
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ fn message_with_type_and_level_(
// Lock/unlock a mutex incase two JS threads are console.log'ing at the same
// time. We do this the slightly annoying way to avoid assigning a pointer.
let use_stderr = matches!(level, MessageLevel::Warning | MessageLevel::Error)
|| message_type == MessageType::Assert;
|| message_type == MessageType::Assert
|| message_type == MessageType::Trace;
let _stream_lock = ConsoleStreamLock::acquire(use_stderr);

if message_type == MessageType::Clear {
Expand All @@ -431,7 +432,7 @@ fn message_with_type_and_level_(
return Ok(());
}

let enable_colors = if matches!(level, MessageLevel::Warning | MessageLevel::Error) {
let enable_colors = if use_stderr {
Output::enable_ansi_colors_stderr()
} else {
Output::enable_ansi_colors_stdout()
Expand All @@ -450,7 +451,7 @@ fn message_with_type_and_level_(
// long-lived `&mut ConsoleObject` across the re-derive in the empty-`Log`
// arm below.
let raw_writer: &mut bun_core::io::Writer = unsafe {
if matches!(level, MessageLevel::Warning | MessageLevel::Error) {
if use_stderr {
(*console).error_writer()
} else {
(*console).writer()
Expand Down
43 changes: 43 additions & 0 deletions test/js/web/console/console-trace.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { expect, it } from "bun:test";
import { bunEnv, bunExe } from "harness";

// https://github.com/oven-sh/bun/issues/19952
it.concurrent("console.trace() writes to stderr, not stdout", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", `console.trace("marker");`],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toBe("");
expect(stderr).toContain("marker");
expect(stderr).toContain("at ");
expect(exitCode).toBe(0);
});

it.concurrent("console.trace() with no arguments writes the stack to stderr", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", `console.trace();`],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toBe("");
expect(stderr).toContain("at ");
expect(exitCode).toBe(0);
});

it.concurrent("console.trace() does not interleave with console.log() on stdout", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "-e", `console.log("before"); console.trace("traced"); console.log("after");`],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
expect(stdout).toBe("before\nafter\n");
expect(stderr).toContain("traced");
expect(exitCode).toBe(0);
});
Loading