TypeScript+Node.jsでsourcemap対応を加えてエラーログ調査を行いやすくしたい

2022.02.02

吉川@広島です。

Node.js+TypeScriptで書いたコードをバンドルしてデプロイした場合に、sourcemap設定することでログに出力されたエラーを追うのが容易になります。本記事ではその設定方法を紹介します。

環境

  • node 16.13.2
  • typescript 4.5.5
  • esbuild 0.14.17
  • source-map-support 0.5.21
  • date-fns 2.28.0

サンプルを用意する

esbuildでバンドルを行うのでインストールします。あと、何でも良かったのですがライブラリをバンドルしたコードを吐きたいのでdate-fnsを導入して使うようにします。

npm i date-fns && npm i -D typescript esbuild

で、サンプルとしてこんなコードを書きます。

// index.ts
import { format } from 'date-fns'

console.log(format(new Date(), 'yyyy-MM-dd')) // 今日の日付を出力(特に意味はない)

throw new Error('MY_ERROR') // エラーを投げる

大事なのは5行目のエラーを投げる記述です。esbuildでバンドルして実行してみましょう。

npx esbuild index.ts --bundle --platform=node --target=node14 --outfile=index.js && node index.js

するとこのようなエラー出力を得ます。

Error: MY_ERROR
    at Object.<anonymous> (/path/to/sourcemap-support-playground/index.js:12074:7)

index.js:12074:7 だと元のコードの位置を特定するのに手間取りそうですね。

--minify オプションをつけたパターンも試してみます。

npx esbuild index.ts --bundle --minify --platform=node --target=node14 --outfile=index.js && node index.js
Error: MY_ERROR
    at Object.<anonymous> (/path/to/sourcemap-support-playground/index.js:1:177156)

index.js:1:177156 もちょっと困りますね。

[方法1]source-map-supportを導入

この出力はsourcemap出力を行って対応させることで改善できます。

まず、source-map-supportを導入します。

evanw/node-source-map-support: Adds source map support to node.js (for stack traces)

npm i -D source-map-support
+import 'source-map-support/register'
import { format } from 'date-fns'

console.log(format(new Date(), 'yyyy-MM-dd'))

throw new Error('MY_ERROR')

そして、バンドルする際にsourcemapを出力するよう設定します。esbuildに --sourcemap オプションを加えて実行します。

npx esbuild index.ts --bundle --sourcemap --platform=node --target=node14 --outfile=index.js && node index.js
/path/to/sourcemap-support-playground/index.ts:6
throw new Error('MY_ERROR')

元のTypeScriptのコードの通り、 index.ts:6 と出してくれていますね。これでエラーの際の調査がしやすくなりました。また、 --minify 付きでも同じ結果が得られました。

[追記][方法2] --enable-source-maps

社内より「--enable-source-mapsでもできるよ」と助言頂いたのでこちらの方法も追記します。

Node.js v12.12.0から使えるようになったenable-source-mapsオプションを加えることでsource-map-supportをインストールしなくてもsourcemap対応ができるようです。 node --enable-source-maps index.js のように実行します。

最初のsource-map-supportをimportしていないコードのまま、

npx esbuild index.ts --bundle --sourcemap --platform=node --target=node14 --outfile=index.js && node --enable-source-maps index.js

を実行して

Error: MY_ERROR
    at Object.<anonymous> (/path/to/sourcemap-support-playground/index.ts:5:7)

の結果を得ることができました。

また、AWS Lambdaなど実行時のオプションを指定することが難しい環境では

NODE_OPTIONS=--enable-source-maps

のように環境変数NODE_OPTIONSの仕組みを利用して有効化することもできるようです。

手元では

npx esbuild index.ts --bundle --sourcemap --platform=node --target=node14 --outfile=index.js && NODE_OPTIONS=--enable-source-maps node index.js

と実行したところ、期待通りの結果 ( index.ts:5:7 の出力) を得られました。

どっちの方法が良いの?

Node v12.12.0未満の場合、--enable-source-mapがないので、source-map-supportをインストールして使うことになるでしょう。

Node v12.12.0以上なら、

  • できるだけ公式の仕組みに寄せる
  • 依存ライブラリは少ない方が良い

という観点から、--enable-source-mapを使う方が良いように思います。ただし、このオプションはNode.js v14.x未満ではexperimentalステータスだったため、その点も加味して判断するのが良さそうです。

参考になれば幸いです。

参考