From be21eaa778be73a7ca026a220e08f52cffd321bc Mon Sep 17 00:00:00 2001 From: Claude Bot Date: Wed, 4 Feb 2026 00:58:47 +0000 Subject: [PATCH] fix(fetch): remove vm.log.level coupling from verbose fetch logging Previously, verbose fetch logging would be enabled when vm.log.level was set to debug or verbose. This created an unexpected coupling between compiler/transpiler logging levels and runtime fetch verbosity. Now, verbose fetch logging is only enabled via explicit opt-in: 1. BUN_CONFIG_VERBOSE_FETCH environment variable 2. explicit `verbose: true` or `verbose: "curl"` option in fetch options This fixes #26724 where users experienced unexpected [fetch] verbose logs during `bun test` when using testcontainers/dockerode. Co-Authored-By: Claude Opus 4.5 --- src/bun.js/webcore/fetch.zig | 10 +- test/regression/issue/26724.test.ts | 140 ++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+), 4 deletions(-) create mode 100644 test/regression/issue/26724.test.ts diff --git a/src/bun.js/webcore/fetch.zig b/src/bun.js/webcore/fetch.zig index 48259f6ad8..1d73d4dbc1 100644 --- a/src/bun.js/webcore/fetch.zig +++ b/src/bun.js/webcore/fetch.zig @@ -219,10 +219,12 @@ fn fetchImpl( var disable_timeout = false; var disable_keepalive = false; var disable_decompression = false; - var verbose: http.HTTPVerboseLevel = if (vm.log.level.atLeast(.debug)) .headers else .none; - if (verbose == .none) { - verbose = vm.getVerboseFetch(); - } + // Only enable verbose fetch logging via explicit opt-in: + // 1. BUN_CONFIG_VERBOSE_FETCH environment variable + // 2. explicit `verbose: true` or `verbose: "curl"` option in fetch options + // Note: We no longer check vm.log.level here, as that's for compiler/transpiler + // logging and shouldn't affect runtime fetch verbosity. + var verbose: http.HTTPVerboseLevel = vm.getVerboseFetch(); var proxy: ?ZigURL = null; var redirect_type: FetchRedirect = FetchRedirect.follow; diff --git a/test/regression/issue/26724.test.ts b/test/regression/issue/26724.test.ts new file mode 100644 index 0000000000..c469dc6fd2 --- /dev/null +++ b/test/regression/issue/26724.test.ts @@ -0,0 +1,140 @@ +import { describe, expect, test } from "bun:test"; +import { bunEnv, bunExe, tempDir } from "harness"; + +describe("issue #26724: verbose fetch logging should not be enabled by default", () => { + test("fetch should not output verbose logs without BUN_CONFIG_VERBOSE_FETCH", async () => { + using dir = tempDir("issue-26724", { + "test.ts": ` + const response = await fetch("https://httpbin.org/get"); + console.log("status:", response.status); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "run", "test.ts"], + env: { + ...bunEnv, + BUN_CONFIG_VERBOSE_FETCH: undefined, // Explicitly ensure it's not set + }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should not contain verbose fetch output (> or [fetch]) + expect(stderr).not.toContain("[fetch]"); + expect(stderr).not.toContain("> HTTP/1.1"); + expect(stdout).toContain("status: 200"); + expect(exitCode).toBe(0); + }); + + test("fetch should output verbose logs when BUN_CONFIG_VERBOSE_FETCH=1", async () => { + using dir = tempDir("issue-26724-verbose", { + "test.ts": ` + const response = await fetch("https://httpbin.org/get"); + console.log("status:", response.status); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "run", "test.ts"], + env: { + ...bunEnv, + BUN_CONFIG_VERBOSE_FETCH: "1", + }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should contain verbose fetch output + expect(stderr + stdout).toContain("> HTTP/1.1"); + expect(stdout).toContain("status: 200"); + expect(exitCode).toBe(0); + }); + + test("node:http requests should not output verbose logs without BUN_CONFIG_VERBOSE_FETCH", async () => { + using dir = tempDir("issue-26724-nodehttp", { + "test.ts": ` + import http from 'node:http'; + + const options = { + hostname: 'httpbin.org', + port: 80, + path: '/get', + method: 'GET', + }; + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { data += chunk; }); + res.on('end', () => { + console.log('status:', res.statusCode); + }); + }); + + req.on('error', (err) => { + console.error('error:', err.message); + }); + + req.end(); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "run", "test.ts"], + env: { + ...bunEnv, + BUN_CONFIG_VERBOSE_FETCH: undefined, // Explicitly ensure it's not set + }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should not contain verbose fetch output (> or [fetch]) + expect(stderr).not.toContain("[fetch]"); + expect(stderr).not.toContain("> HTTP/1.1"); + expect(stdout).toContain("status: 200"); + expect(exitCode).toBe(0); + }); + + test("bun test should not output verbose fetch logs without BUN_CONFIG_VERBOSE_FETCH", async () => { + using dir = tempDir("issue-26724-buntest", { + "fetch.test.ts": ` + import { test, expect } from "bun:test"; + + test("fetch works", async () => { + const response = await fetch("https://httpbin.org/get"); + expect(response.status).toBe(200); + }); + `, + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "test", "fetch.test.ts"], + env: { + ...bunEnv, + BUN_CONFIG_VERBOSE_FETCH: undefined, // Explicitly ensure it's not set + }, + cwd: String(dir), + stdout: "pipe", + stderr: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // Should not contain verbose fetch output (> or [fetch]) + const output = stdout + stderr; + expect(output).not.toContain("[fetch]"); + expect(output).not.toContain("> HTTP/1.1"); + expect(output).toContain("1 pass"); + expect(exitCode).toBe(0); + }); +});