Skip to content

Bun は WebKit インスペクタープロトコル を使用するため、インタラクティブなデバッガーでコードをデバッグできます。説明のために、以下のシンプルな Web サーバーを考えてください。

JavaScript と TypeScript のデバッグ

typescript
Bun.serve({
  fetch(req) {
    console.log(req.url);
    return new Response("Hello, world!");
  },
});

--inspect

Bun でコードを実行する際にデバッグを有効にするには、--inspect フラグを使用します。これにより、利用可能なポートで WebSocket サーバーが自動的に起動し、実行中の Bun プロセスを調べることができます。

sh
bun --inspect server.ts
txt
------------------ 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 プレフィックス、またはその両方を指定できます。

sh
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 ベースのデバッガーをホストしています。これは Safari ユーザーにはおなじみと思われる WebKit の Web Inspector Interface の修正バージョンです。

ブラウザで提供された debug.bun.sh URL を開いて、デバッグセッションを開始します。このインターフェースから、実行中のファイルのソースコードを表示し、ブレークポイントを設定して確認し、組み込みのコンソールを使用してコードを実行できます。

ブレークポイントを設定しましょう。Sources タブに移動します。以前見たコードが表示されるはずです。行番号 3 をクリックして、console.log(req.url) 文にブレークポイントを設定します。

次に、Web ブラウザーで http://localhost:3000 にアクセスします。これにより、ローカル Web サーバーに HTTP リクエストが送信されます。ページが読み込まれていないように見えます。なぜでしょうか?プログラムが以前に設定したブレークポイントで実行を一時停止しているからです。

UI がどのように変化したかに注目してください。

この時点で、現在の実行環境を調べるために多くのことができます。下部にあるコンソールを使用して、ブレークポイントでスコープ内の変数に完全にアクセスしながら、プログラムのコンテキスト内で任意のコードを実行できます。

Sources ペインの右側で、現在スコープ内のすべてのローカル変数を確認し、それらのプロパティとメソッドを詳しく見ることができます。ここでは、req 変数を検査しています。

Sources ペインの左上で、プログラムの実行を制御できます。

以下は、制御フローボタンの機能を説明するチートシートです。

  • Continue script execution — 次のブレークポイントまたは例外までプログラムを実行し続けます。
  • Step over — プログラムは次の行に進みます。
  • Step into — 現在の文が関数呼び出しを含む場合、デバッガーは呼び出された関数に「ステップイン」します。
  • Step out — 現在の文が関数呼び出しである場合、デバッガーは呼び出しの実行を完了し、呼び出された場所に関数を「ステップアウト」します。

Visual Studio Code デバッガー

Bun スクリプトをデバッグするための実験的なサポートが Visual Studio Code で利用可能です。使用するには、Bun VSCode 拡張機能 をインストールする必要があります。


ネットワークリクエストのデバッグ

BUN_CONFIG_VERBOSE_FETCH 環境変数を使用すると、fetch() または node:http で行われたネットワークリクエストを自動的にログ出力できます。

説明
curlリクエストを curl コマンドとして出力
trueリクエストとレスポンスの情報を出力
false何も出力しない(デフォルト)

fetch と node:http リクエストを curl コマンドとして出力

Bun は、環境変数 BUN_CONFIG_VERBOSE_FETCHcurl に設定することで、fetch() および node:http ネットワークリクエストを curl コマンドとして出力することもサポートしています。これにより、fetch リクエストが単一行の curl コマンドとして出力され、ターミナルにコピーペーストしてリクエストを再現できます。

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" }),
});
txt
[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 コマンドなしで出力するには、BUN_CONFIG_VERBOSE_FETCHtrue に設定します。

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" }),
});
txt
[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

スタックトレースとソースマップ

Bun はすべてのファイルを変換するため、コンソールに表示されるスタックトレースが変換された出力を指して役に立たないように思えるかもしれません。これに対処するために、Bun は変換するすべてのファイルに対してソースマップを自動的に生成して提供します。コンソールでスタックトレースを表示するときは、ファイルパスをクリックすると、TypeScript や JSX で書かれていたり、他の変換が適用されていたりしても、元のソースコードに移動できます。

Bun は、オンデマンドでファイルを変換する際の実行時と、事前にファイルを事前コンパイルするために bun build を使用する際の両方で、ソースマップを自動的に読み込みます。

構文強調されたソースコードプレビュー

デバッグを支援するために、Bun は未処理の例外または拒否が発生したときに、自動的に小さなソースコードプレビューを出力します。この動作は Bun.inspect(error) を呼び出すことでシミュレートできます。

ts
// エラーを作成
const err = new Error("Something went wrong");
console.log(Bun.inspect(err, { colors: true }));

これにより、エラーメッセージとスタックトレースとともに、エラーが発生した場所の構文強調されたソースコードプレビューが出力されます。

ts
1 | // Create an error
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 をログ出力すると、error.stack のフォーマットは Node.js の V8 エンジンと同じになります。これは、V8 スタックトレースを期待するライブラリを使用する場合に特に役立ちます。

V8 スタックトレース API

Bun は V8 スタックトレース API を実装しています。これはスタックトレースを操作するための一連の関数です。

Error.prepareStackTrace

Error.prepareStackTrace 関数は、スタックトレース出力をカスタマイズできるグローバル関数です。この関数はエラーオブジェクトと CallSite オブジェクトの配列で呼び出され、カスタムスタックトレースを返すことができます。

ts
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
getTypeNametypeof this
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 の第 2 引数は、スタックトレースを開始する関数です。

例えば、以下のコードは、エラーが myInner でスローされても、err.stackfn() を呼び出すコードを指すようにします。

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();
txt
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)

Bun by www.bunjs.com.cn 編集