Skip to main content
Bun 支持 WebKit Inspector Protocol,因此你可以通过交互式调试器调试你的代码。为了演示,考虑下面这个简单的 Web 服务器。

调试 JavaScript 和 TypeScript

https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79server.ts
Bun.serve({
  fetch(req) {
    console.log(req.url);
    return new Response("Hello, world!");
  },
});

--inspect

运行 Bun 代码时使用 --inspect 标志,可以启用调试功能。该标志会自动启动一个 WebSocket 服务器,监听一个可用端口,用于检查运行中的 Bun 进程。
terminal
bun --inspect server.ts
------------------ Bun Inspector ------------------
Listening at:
  ws://localhost:6499/0tqxs9exrgrm

Inspect in browser:
  https://debug.bun.sh/#localhost:6499/0tqxs9exrgrm
------------------ Bun Inspector ------------------

--inspect-brk

--inspect-brk 标志行为与 --inspect 相同,不同的是它会在执行脚本的第一行自动插入一个断点。这对于调试运行很快并立即退出的脚本非常有用。

--inspect-wait

--inspect-wait 标志的行为与 --inspect 相同,但代码会等待直到调试器附加到运行进程才开始执行。

为调试器设置端口或 URL

无论使用哪个标志,都可以选择性地指定端口号、URL 前缀或两者。
terminal
bun --inspect=4000 server.ts
bun --inspect=localhost:4000 server.ts
bun --inspect=localhost:4000/prefix server.ts

调试器

各种调试工具可以连接到此服务器,提供交互式调试体验。

debug.bun.sh

Bun 在 debug.bun.sh 提供了一个基于 Web 的调试器。它是 WebKit 的 Web Inspector Interface 的修改版本,Safari 用户会觉得很熟悉。 打开提供的 debug.bun.sh URL 在浏览器中启动调试会话。在该界面,你可以查看正在运行文件的源代码,查看和设置断点,并使用内置控制台执行代码。
Bun 调试器控制台标签截图
现在我们设置一个断点。切换到 Sources 标签;你应该能看到之前的代码。点击第 3 行的行号,在 console.log(req.url) 语句上设置断点。
Bun 调试器截图
然后在浏览器访问 http://localhost:3000 。这会向我们的本地服务器发送 HTTP 请求。页面似乎无法加载,这是因为程序已在先前设置的断点处暂停执行。 注意界面发生的变化。
Bun 调试器截图
此时我们可以做很多事情来检查当前执行环境。我们可以使用底部的控制台,在程序上下文中运行任意代码,完全访问断点处范围内的变量。
Bun 调试器控制台
在 Sources 窗格的右侧,可以看到当前作用域中所有的局部变量,并可展开查看它们的属性和方法。这里我们检查了 req 变量。
Bun 调试器变量面板
在 Sources 窗格左上角,我们可以控制程序的执行流程。
Bun 调试器控制按钮
下面是对这些执行控制按钮功能的说明:
  • 继续执行脚本 — 程序继续运行直到遇到下一个断点或异常。
  • 单步跳过 — 程序继续执行到下一行。
  • 单步进入 — 如果当前语句包含函数调用,则调试器会“进入”被调用函数内部。
  • 单步跳出 — 如果当前语句是函数调用,调试器会完成该函数执行,然后“跳出”函数回到调用处。
Bun 调试器执行控制

Visual Studio Code 调试器

Visual Studio Code 提供了对 Bun 脚本调试的实验性支持。使用前需要安装 Bun VSCode 扩展

调试网络请求

BUN_CONFIG_VERBOSE_FETCH 环境变量允许你自动打印通过 fetch()node:http 发起的网络请求。
描述
curlcurl 命令格式打印请求
true打印请求和响应信息
false不打印任何内容,默认值

以 curl 命令打印 fetch 和 node:http 请求

Bun 也支持将 fetch()node:http 网络请求打印为 curl 命令,只需把 BUN_CONFIG_VERBOSE_FETCH 环境变量设置为 curl。这会将 fetch 请求打印为一行 curl 命令,方便你复制粘贴到终端中复现请求。
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79index.ts
process.env.BUN_CONFIG_VERBOSE_FETCH = "curl";

await fetch("https://example.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ foo: "bar" }),
});
[fetch] $ curl --http1.1 "https://example.com/" -X POST -H "content-type: application/json" -H "Connection: keep-alive" -H "User-Agent: Bun/1.3.3" -H "Accept: */*" -H "Host: example.com" -H "Accept-Encoding: gzip, deflate, br" --compressed -H "Content-Length: 13" --data-raw "{\"foo\":\"bar\"}"
[fetch] > HTTP/1.1 POST https://example.com/
[fetch] > content-type: application/json
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[fetch] > Content-Length: 13

[fetch] < 200 OK
[fetch] < Accept-Ranges: bytes
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Tue, 18 Jun 2024 05:12:07 GMT
[fetch] < Etag: "3147526947"
[fetch] < Expires: Tue, 25 Jun 2024 05:12:07 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: EOS (vny/044F)
[fetch] < Content-Length: 1256
[fetch] > 标记的行是本地代码发出的请求,以 [fetch] < 标记的行是远程服务器的响应。 BUN_CONFIG_VERBOSE_FETCH 环境变量在 fetch()node:http 请求中均支持,因此应该都能正常工作。 如果只想打印请求和响应信息,但不打印 curl 命令,可以将该环境变量设置为 true
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79index.ts
process.env.BUN_CONFIG_VERBOSE_FETCH = "true";

await fetch("https://example.com", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ foo: "bar" }),
});
[fetch] > HTTP/1.1 POST https://example.com/
[fetch] > content-type: application/json
[fetch] > Connection: keep-alive
[fetch] > User-Agent: Bun/1.3.3
[fetch] > Accept: */*
[fetch] > Host: example.com
[fetch] > Accept-Encoding: gzip, deflate, br
[fetch] > Content-Length: 13

[fetch] < 200 OK
[fetch] < Accept-Ranges: bytes
[fetch] < Cache-Control: max-age=604800
[fetch] < Content-Type: text/html; charset=UTF-8
[fetch] < Date: Tue, 18 Jun 2024 05:12:07 GMT
[fetch] < Etag: "3147526947"
[fetch] < Expires: Tue, 25 Jun 2024 05:12:07 GMT
[fetch] < Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
[fetch] < Server: EOS (vny/044F)
[fetch] < Content-Length: 1256

堆栈跟踪和 sourcemaps

Bun 会对每个文件进行转译,这看起来似乎会导致控制台中显示的堆栈跟踪不准确,只指向转译后的文件。为了解决这个问题,Bun 会自动为转译的每个文件生成并提供 sourcemap。当你在控制台看到堆栈跟踪时,点击文件路径即可跳转到原始源代码,即使它是 TypeScript 或 JSX 编写的,或经过了其他转换。 Bun 会在运行时按需转译文件时自动加载 sourcemaps,也会在使用 bun build 预编译文件时加载。

带语法高亮的源代码预览

为了帮助调试,当发生未捕获的异常或拒绝时,Bun 会自动打印一段小的源代码预览。你可以通过调用 Bun.inspect(error) 来模拟此行为:
// 创建一个错误
const err = new Error("Something went wrong");
console.log(Bun.inspect(err, { colors: true }));
这会打印出错误发生处的带语法高亮的源代码预览,以及错误信息和堆栈跟踪。
1 | // 创建一个错误
2 | const err = new Error("Something went wrong");
                ^
error: Something went wrong
      at file.js:2:13

V8 堆栈跟踪

Bun 使用 JavaScriptCore 作为其引擎,但许多 Node.js 生态和 npm 期望使用 V8。不同的 JavaScript 引擎对 error.stack 的格式不同。Bun 旨在作为 Node.js 的无缝替代品,因此我们负责确保即使引擎不同,堆栈跟踪的格式尽可能相似。 这就是为什么在 Bun 中打印 error.stack,格式与 Node.js 的 V8 引擎一致。这在使用期望 V8 堆栈跟踪的库时尤其有用。

V8 堆栈跟踪 API

Bun 实现了 V8 堆栈跟踪 API,它是一组允许你操作堆栈跟踪的函数。
Error.prepareStackTrace
Error.prepareStackTrace 是一个全局函数,可以定制堆栈跟踪的输出。该函数接收错误对象和一个 CallSite 对象数组,并返回自定义的堆栈跟踪内容。
Error.prepareStackTrace = (err, stack) => {
  return stack.map(callSite => {
    return callSite.getFileName();
  });
};

const err = new Error("Something went wrong");
console.log(err.stack);
// [ "error.js" ]
CallSite 对象的可用方法如下:
方法返回值
getThis函数调用中的 this
getTypeNamethis 的类型名
getFunction函数对象
getFunctionName函数名称,字符串
getMethodName方法名称,字符串
getFileName文件名或 URL
getLineNumber行号
getColumnNumber列号
getEvalOriginundefined
getScriptNameOrSourceURL源码 URL
isToplevel如果函数在全局作用域中,返回 true
isEval如果函数是 eval 调用,返回 true
isNative如果函数是原生的,返回 true
isConstructor如果函数是构造函数,返回 true
isAsync如果函数是 async,返回 true
isPromiseAll尚未实现
getPromiseIndex尚未实现
toString返回调用位置的字符串描述
有时 Function 对象可能已被垃圾回收,因此部分方法可能返回 undefined
Error.captureStackTrace(error, startFn)
Error.captureStackTrace 函数允许你在代码的特定位置捕获堆栈跟踪,而非错误抛出的地方。 这在有回调或异步代码时非常有用,因为它们使得确定错误源头变得困难。Error.captureStackTrace 的第二个参数是你希望堆栈跟踪起始的函数。 例如,下面代码会使 err.stack 指向调用 fn() 的代码,而不是错误实际上被抛出的 myInner
https://mintcdn.com/ikxin/RzFFGbzo0-4huILA/icons/typescript.svg?fit=max&auto=format&n=RzFFGbzo0-4huILA&q=85&s=a3dffd2241f05776d3bd25171d0c5a79index.ts
const fn = () => {
  function myInner() {
    throw err;
  }

  try {
    myInner();
  } catch (err) {
    console.log(err.stack);
    console.log("");
    console.log("-- captureStackTrace --");
    console.log("");
    Error.captureStackTrace(err, fn);
    console.log(err.stack);
  }
};

fn();
Error: here!
    at myInner (file.js:4:15)
    at fn (file.js:8:5)
    at module code (file.js:17:1)
    at moduleEvaluation (native)
    at moduleEvaluation (native)
    at <anonymous> (native)

-- captureStackTrace --

Error: here!
    at module code (file.js:17:1)
    at moduleEvaluation (native)
    at moduleEvaluation (native)
    at <anonymous> (native)