Node.jsで実行中の処理の行番号や関数名を取得する方法 令和最新版(Node.js v22.9.0以降のgetCallSite)

はじめに

これまでNode.jsで行番号や関数名を取得する際は、わざと例外を発生させてスタックトレースから値を拾ってくる方法がよく使われていました。
Node.js v22.9.0からは getCallSite() というAPIが使えるようになったので、今後はこっちを使う方が良さそうです。

環境

  • Node.js v22.9.0
  • macOS Sonoma 14.6.1

サンプルコード

github.com

リリースノートに載っていたコードを参考に、以下のようなコードを書いて動かしてみます。

import util from 'node:util';

function main() {
  const callSites = util.getCallSite();

  console.log('Call Sites:');
  callSites.forEach((callSite, index) => {
    console.log(`CallSite ${index + 1}:`);
    console.log(`Function Name: ${callSite.functionName}`);
    console.log(`Script Name: ${callSite.scriptName}`);
    console.log(`Line Number: ${callSite.lineNumber}`);
    console.log(`Column Number: ${callSite.column}`);
    console.log('-------------------');
  });
}

main();

github.com

package.json"type": "module" と書いてESMとして実行すると、以下のような出力が得られます。

$ npm start

> get-call-site-example@0.0.1 start
> node main.js

Call Sites:
CallSite 1:
Function Name: main
Script Name: file:///Users/path/to/repository/node-get-call-site-example/main.js
Line Number: 4
Column Number: 26
-------------------
CallSite 2:
Function Name: 
Script Name: file:///Users/path/to/repository/node-get-call-site-example/main.js
Line Number: 17
Column Number: 1
-------------------
CallSite 3:
Function Name: run
Script Name: node:internal/modules/esm/module_job
Line Number: 262
Column Number: 25

どうやら getCallSite() を呼ぶと、エントリーポイントを呼び出すNode.js側の処理からコールスタックが得られるようです。
Node.jsのコードを追ってみたところ、ESMでは以下のような順序でエントリーポイントの関数が実行されるようです。

  1. src/node.cc:436
  2. lib/internal/main/run_main_module.js:21
  3. lib/internal/modules/run_main.js:173
  4. lib/internal/modules/esm/loader.js:480
  5. lib/internal/modules/esm/module_job.js:253

おわりに

C言語では __LINE__ __func__ のようなマクロを使って、ログに行番号や関数名を入れることができます。
一方で、Node.jsにはそのような情報を直接提供するAPIはこれまで存在せず、例外を発生させて prepareStackTrace から取得するというハック的な方法がよく使われていました。
v22.9.0から getCallSite() が使えるようになったことで、より分かりやすい形で行番号や関数名を取得できるようになります。
コードの可読性の観点からも、メリットが大きい機能だと感じました。

参考記事

nodejs.org
github.com