Chatwork Creator's Note

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

ビジネスチャット「Chatwork」のエンジニアとデザイナーのブログです。

読者になる

WebFrontendのモニタリングツールとSourceMap

こんにちは、フロントエンド開発部のcw-suetake 🐧です。この記事はChatwork Product Day 2023の応援記事です。

lp.chatwork.com

今回はSourceMapとモニタリングツールの話をしていきます。

SourceMapとは

早速ですがSourceMapについて軽く中身をご紹介します。

JavaScript周辺のビルドツールでは多くの最適化・圧縮等が行われ、最終的な生成物のみではデバッグの難易度が高いという問題がありました。この問題に対応するために生成物と元のコードを相互にマッピングし、生成物を実行しつつ元コードを見ながらデバッグができるという仕組みのために主にビルドツールが作成するファイルです。Source Map Revision 3 Proposal にて仕様が提案されtc39/source-map-spec にて整理されています。

SourceMapファイルの基本構造は以下のプロパティーを持ったJsonです。

{
  "version" : 3,
  "file": "out.js",
  "sourceRoot": "",
  "sources": ["foo.js", "bar.js"],
  "sourcesContent": [null, null],
  "names": ["src", "maps", "are", "fun"],
  "mappings": "A,AAAB;;ABCDE;"
}

この構造の中で生成物のn行m文字目が元ファイルのn'行m'文字目に該当するという情報が記録されています。さらに仕様ではこのほか幾つかの情報が付与されることも想定されています。

WebpackやVite等のビルドツールやWebフレームワークによっては標準でSourceMapファイルが生成されるため開発時に存在や生成内容を特段意識する必要はないかもしれません。

モニタリングツールとSourceMap

WebFrontendやサーバーサイドにおいてDatadoc, NewRelic等のモニタリングツールを用いて動作状況やエラーの収集を行う場合があります。モニタリングツールは実行時に発生したエラー名やcallstackなどを記録し分析結果をダッシュボードに表示する機能を備えているものもいくつかあります。

JavaScriptのビルドの中で最適化を行なっている場合、ブラウザやサーバー上のランタイムでは最適化後のファイルが実行されているためモニタリングによるエラーやログを収集すると当然実行されている最適化後のコードについての情報が得られることになります。すると計測されたログには最適化され圧縮された変数名が記録されることがあります。

最適化され圧縮された変数名をモニタリングツール上で確認して元の名前がなんだったのかを予想するのは大変なのでツール側で復元してもらったほうが楽です。モニタリングツールにSoucreMapを渡すことで自動的に名前を復元してもうことができます。

モニタリングツールにSourceMapを渡すメリットは変数名の復元だけではなくアラート等の集計等にも役立ちます。Datadog RUMでは同一視できるエラーについて集計してくれるError Tracking機能があります。この機能では同一視できると判断したエラーについて別のビルドにて変数名がそれぞれ異なっていてもSourceMapから復元した元の名前を用いて分類を行なってくれます。

モニタリングツールのためのSourceMap

一口にSoruceMapといっても多くのオプションや方式を許容しています。SourceMapという単一の仕組みで多くのユースケースをカバーするためだとは思いますが先に挙げたようにモニタリングツールに利用する上で採用するSourceMapの方式によって活用の幅が変わってきます。

SourceMapは生成物と元ファイルとのマッピング情報を記録したデータなのでSourceMap自体にはそれぞれのファイルへの参照のみが記録される形式がとられることがあります。この形式は仕様内のSection 2: File listingsに記載されていますがSourceMapを解釈する側が生成物と元ファイルへアクセス可能な状態にする必要があります。アクセスの方法としてはURLでアクセスできるように公開することです。しかし元ファイルを公開するのは難しい可能性もあると思います、また公開可能としてもビルドごとに元ファイルが変化してはいけないのでSourceMapを生成するごとに固有のURLで元ファイルの公開をする必要があるためあまり現実的では無いように思います。

そこで元ファイルを参照ではなく元ファイルそのものをSourceMapにハードコードして埋め込んだSourceContentという形式が利用できます。この形式であればビルドツールにてバンドルした元ファイルをまるっと全て埋め込むことでURLで公開せずにSourceMapファイルのみで完結します。

モニタリングツールに渡すSourceMapにて元ファイルへのURL参照の方式を取るとツール自身が元ファイルを見つけられず想定通り機能しなくなる可能性があるため後者のようなSourceContent形式のSourceMapを利用するとより多くの情報が得られることがあります。

monorepoとSourceMap

モノレポやsub packageなど開発スタイルによっては最終的な生成物を作成するまでに複数のビルドプロセスを順番に行う場合があります。SourceMapは生成物と元ファイルのmappingを行う仕組みであるため基本的に1回のビルドにおいて生成物と元ファイルのmappingを行います。そのため2つ以上前にビルドされた元ファイルまでmappingが行われない場合があります。

モノレポ等で分割した側の実装はモニタリングツールに検出されなくなってしまう等の状態に陥ることがあります。実際にChatworkでもChatwork LiveというプロダクトにてReact実装の部分はmappingされるもののScala.js実装部分はmappingされないといった事象が発生したことがあります。

このように複数のビルドプロセスを経由することにより元ファイルへのmappingが失われてしまうことを避けるための仕様はMulti-level Mappingという部分で紹介されてはいるものの、明確な解決策はまだ規定されていないようです。ビルドツールによってはMulti-level Mappingに対応しているためビルド時に元ファイルを指定する際に合わせてそのSourceMapを渡すことができるものがあります。

また、複数のSourceMapを結合してマッピングの再割り当てを行うazu/multi-stage-sourcemap というツールを利用することもできます。解説のブログ記事の利用方法が参考になります。

efcl.info

まとめ

以上SourceMapをより活用するためのSourceMapの作り方のご紹介でした。Chatworkではこのように開発体験がより良いものになるような様々な工夫をしています。

lp.chatwork.com

10/24のChatwork Product Dayでは私末竹が今回ご紹介したこと以外にもリファクタリングの視点でより開発しやすくするための工夫等をお話予定となっています!ぜひご視聴ください!