JavaScript のオブザーバビリティを修正する:ライブラリ単位での改善

Article by:

 

ここ数週間、私たちはクロスエコシステムでの取り組みとして、現在すべての JavaScript APM ツールを支えている「モンキーパッチング」をランタイムに組み込まれた仕組みに置き換える作業を進めてきました。ここでは、その背景と仕組み、そして現状について説明します。

この内容はサーバーサイド JavaScript(Node.js、Bun、Deno、Cloudflare Workers)のみに適用されます。ブラウザには diagnostics_channel が存在せず、またそれをポリフィルするために必要な非同期コンテキスト伝播の仕組みも備わっていません。

 

モンキーパッチングはスケールしない

私のチームメイトである Sigrid は、なぜモンキーパッチングが破綻しつつあるのか、そして TracingChannel がそれをどう解決するのかについて詳しく解説しています。

要点をまとめると、JavaScript のすべての APM ツール(Sentry を含む)は、実行時に require() や import をフックし、import-in-the-middle(IITM)require-in-the-middle(RITM)を使ってライブラリをインストルメントしています。しかしこの仕組みは ECMAScript Modules(ESM)で壊れやすく、Node 以外のランタイムでは動作せず、バンドラとも衝突し、さらに制御できない内部実装に依存する構造になっています。また SDK は対象ライブラリより先に読み込まれていなければならず、そうでない場合は計測が静かに失敗します。

これは Sentry 固有の問題ではありません。JavaScript のインストルメンテーションを提供するすべての APM ベンダーが、同じ不安定さを抱えています。エコシステム全体が行き詰まっている状態です。

多くのライブラリメンテナーはオブザーバビリティを前提にしていません。何を公開すべきかも分からず、OpenTelemetry のような仕組みを導入することは標準化というより実装負担になります。これまで APM 側がパッチで吸収してきたため、ライブラリ側が対応する必要がありませんでした。

しかし、より良い方法があります。

 

TracingChannel:パッチ不要のオブザーバビリティ

2025年後半、私たちは Nitro、h3、そして unjs エコシステムの開発者である Pooya Parsa と、Nitro フレームワーク向けの Sentry SDK をどう設計するべきか議論していました。その中でチームメイトの Sigrid が、Node.js の diagnostics_channel モジュールに含まれる TracingChannel を検討することを提案しました。

Sigrid のブログではこの API について詳しく解説していますが、基本的な考え方は非常にシンプルです。ライブラリが TracingChannel 上に構造化されたイベントを公開すれば、APM ツールはそれをパッチなしで購読できます。ライブラリは「クエリが開始された」「クエリが終了した」といった情報を発行するだけで、リスナー側がそれをスパンとして構築します。

この追加されたコードのコストは非常に小さく、ライブラリメンテナーにとっても受け入れやすいものです。一方 APM 側では、そのトレーシングチャンネルを購読するだけでイベントを受け取ることができます。IITM も RITM もなく、ローダーフックもなく、初期化順序の制約もありません。誰もリスニングしていない場合はオーバーヘッドもゼロです。Node、Bun、Deno を横断して動作し、バンドラにも安全です。この API は Node 18 以降で利用可能で、dc-polyfill により未対応ランタイムもカバーできるため、既存のサポート範囲とも一致しています。

 

誰も同意しているが、誰も動いていない

TracingChannel API の調査と OpenTelemetry との統合方法について十分な知見が得られた段階で、2025年11月に Otel JS に issue を立て、TracingChannel サポートについて議論を始めました。

反応は前向きで、その後しばらくして、OpenTelemetry チームのメンバーの一人が TracingChannel を OTel SDK に統合するためのドラフト API 案を作成するところまで進みました。しかし、エコシステムとして実装を進める大きな動きはまだありません。ドラフトは存在しているものの、それを前に進める作業は進んでいません。

TracingChannel が JavaScript オブザーバビリティの未来であることには多くの人が同意していますが、それをライブラリに実装していく作業を担う人がいない状態です。データベース、Web フレームワーク、メッセージキュー、AI プロバイダーなど、多数のインストルメンテーションに対応が必要であり、それぞれにライブラリ内部の理解、メンテナーが受け入れる提案作成、実装、レビュー対応が必要になります。これは膨大な upstream PR の山です。

そこで私は「それなら、まず動かしてみればいいのではないか」と考えました。

最初のステップは、このパターンが実際に機能することを証明することでした。すでに h3srvxunstoragedb0、Nitro に TracingChannel サポートを手動で実装しており、これは前段階の SDK 開発の一環として行っていました。unjs エコシステムは反応が早く、実際に動く実装例を提供できたことで、イベントの設計方法、コンテキスト伝播の流れ、OTel との統合方法、セマンティックコンベンションの作り方まで、エンドツーエンドのモデルを示すことができました。

また早い段階で学んだのは、「TracingChannel を使うべきだ」と言うだけでは不十分だということです。それでは棚に置かれて終わるだけです。代わりに Nitro のときと同様、「こちらで実装し、維持も支援する」という形で進める必要があります。リポジトリにコードを取り込むことはメンテナンス負担になるため、その責任も含めて引き受ける提案をしています。

この方針のもと、pg、mysql2、redis に対してアプローチし、PR の完全実装とその後のサポートまで含めて支援する意向を伝えました。これらはエコシステム内でも主要なデータベースドライバであり、合計で週6,000万以上のダウンロードがあります。ここに TracingChannel を導入できれば、他のライブラリにも波及させることができます。3つすべてが前向きで、PR を受け入れる意思を示しています。

また Node.js コアの diagnostics_channel API の生みの親である Stephen Belanger にも連絡を取りました。現在彼はこの取り組みを後押ししており、提案へのフィードバックや、メンテナーを説得する際に必要な権威的な視点を提供しています。

このように、エコシステム全体で一つずつ進めています。

全体像として補足すると、私たちのチームは SDK をランタイム非依存にする取り組みを進めており、複数のアプローチを並行して動かしています。その中で TracingChannel の取り組みは長期的な戦略です。ユーザーがすぐにすべてのライブラリをアップグレードすることは現実的ではなく、同時にすべてを移行させることもできないため、移行は段階的に進むことになります。

 

AI でスケールさせる

現実的な話として、私一人で 44 個のライブラリに TracingChannel サポートを追加していくのは不可能です。どれも内部構造を深く理解しているわけではなく、Redis のプロトコル実装や mysql2 のクエリパイプラインをこれまで触ったこともありません。

そこで、Claude Code を使ったフィードバックループを構築しました。SKILLS を使い、ライブラリごとの重い作業を分解して処理します。

  1. リサーチと提案作成。ライブラリ名を入力すると、Claude がその非同期モデル、既存の OTel インストルメンテーション、メンテナンス状況、内部アーキテクチャを調査し、これまで確立したパターンに沿った提案をドラフトします。私はそれをレビューし、必要に応じて調整した上で次に進めます。

  2. 実装。承認された提案に基づき、Claude が実際に動作する実装とテストを生成します。tracePromise / traceCallback の選択、hasSubscribers ガード、Node 18 互換性、Docker を使った実サービスでの統合テストなども含みます。

  3. レビュー指摘の取り込み。上流の PR でレビューコメントが付いた場合、それを Claude が解析し、妥当性の判断、返信案の生成、今後の提案に反映すべきパターンの抽出を行います。最終的な対応判断とメンテナーとのコミュニケーションはすべて私が行います。

  4. トラッカー更新。各 PR の最新ステータスを Claude が取得し、移行状況のトラッカーを常に最新状態に保ちます。

 

このサイクルが次のサイクルに学習を渡していきます。あるライブラリで得られたレビューの学びが次の提案に反映され、知識として蓄積されて LEARNING.md に書き出されます。

人間と AI の役割分担を明確にすると、Claude はリサーチ、定型的な実装、パターン適用を担当し、私はアーキテクチャ判断、挿入ポイントの決定、メンテナーとのすべてのコミュニケーション、そして最終的なコードレビューを担当します。重要なのは、すべてのコミットが co-author 付きであり、AI の関与を透明にしている点です。ライブラリメンテナーがやり取りするのは AI ではなく人間です。これは非常に重要で、メンテナーの作業に対する敬意を示すことが採用の前提条件になります。

このアプローチによって、本来であれば数年規模の作業を、毎日提案と実装を並列で回しながら、学習を蓄積し続ける生産ラインへと変えることができました。

 

10件マージ済み、残り34件

現在、私たちは複数のインストルメンテーションを4つのカテゴリに分けて追跡しています。進捗は以下の通りです。

カテゴリ 合計 マージ済み PRオープン 議論中 未着手
OTel提供 24 4 2 6 12
Sentry提供 10 0 0 1 9
その他エコシステム 8 5 2 1 0
ロギング 2 1 0 0 1
合計 44 10 4 8 22

主な成果

  • mysql2 はマージ済みで、npm エコシステムでも特に利用率の高いデータベースドライバの一つです。
  • node-redisioredis もどちらもマージ済みで、Redis クライアントの主要2系統に TracingChannel サポートが入っています。
  • h3srvxunstorage もすべてマージ済みです。unjs エコシステムは初期から協力的で、これは Nitro にも影響し、さらに Nuxt など下流フレームワークにも波及します。

 

また、ライブラリ作者向けの標準化として e18e の統合イシューuntracing spec の策定にも関与し、TracingChannel の利用方法をエコシステム全体で揃える取り組みを進めています。

 

Sentryにとってこれが意味すること

これはインストルメンテーションのモデルを反転させます。ライブラリ側が契約(インターフェース)を所有し、私たちはそれにサブスクライブする形になります。これにより、これまで問題になっていたすべて(ESMの破綻、初期化順序の問題、ランタイムへのロックイン、バンドラーの競合)が解消されます。私たちのインストルメンテーションコードはよりシンプルになり、ランタイム固有のハックを維持する必要もなくなります。

これはSentryだけでなく、すべてのAPMツールにとってもメリットがあります。このアプローチを推進することは、ライブラリのメンテナーやコミュニティ全体との信頼構築にもつながりますし、実際に複数のメンテナーが「これは特定のAPMに偏らず、全体に利益があるので良い」と評価しています。

 

フライホイールが回り始めている

例として node-redis を見てみます。Redisチームとの共同作業の中で、彼らはすでに独自のOpenTelemetryインストルメンテーションを開発していました。彼らはSentryのTrancingChannel提案を自分たちの実装に整合させ、それを支える形にしたいと考えていました。その結果、すでにリリース済みだったメトリクス用プラグインをTracingChannelベースで再実装しましたが、テストを一切変更することなく動作しました。現在はトレースの対応も支援しています

その後すぐに mysql2 が TracingChannel サポートをリリースすると、誰かが独立して mysql2-otel-instrumentation を構築しました。これは diagnostics_channel のサブスクライバーとして動作し、OpenTelemetryのモンキーパッチ型 @opentelemetry/instrumentation-mysql2 を置き換えるものです。この動機もまさに、私たちが解決しようとしている問題そのものでした。RITM(実行時インストルメンテーションモデル)がうまく機能していなかったのです。ライブラリが TracingChannel を実装すると、その上にサブスクライバーが自然発生的に現れる、という流れが起きています。

 

次に何が起きるか

現在、Express、PostgreSQL(pg)、Knex、GraphQLといった主要ライブラリに対してオープンなPRがあります。これらのライブラリでTracingChannelがサポートされると、何百万ものアプリケーションが自分のコードを一行も変更せずに、より良いオブザーバビリティを得られるようになります。MongoDB、Mongoose、Prisma、Honoについても現在議論が進行中で、KoaやConsolaについてはすでに提案がドラフトされています。さらにNodeの組み込みHTTPモジュール、Kafkaクライアント、AIプロバイダーSDKなど、まだアプローチしていないライブラリが20以上残っています。

個別のライブラリ対応に加えて、次のレイヤーは「消費側の重複の削減」です。現状では、TracingChannelを購読する各APMツールが、それぞれ独自にライブラリのペイロードをOpenTelemetryのセマンティック規約へ変換しています。この重複をなくすために、共有マッパーレジストリを設計しています。これは、TracingChannelのイベントを標準化されたスパンや属性へ変換する共同管理モジュール群です。

まずはSentry内部でこれを構築・検証し、その後オープンソース化して、どのAPMベンダーでも利用できる形にする計画です。もしライブラリがTracingChannelを提供し、対応するマッパーが存在していれば、インストルメンテーションは自動的に成立するようになります。長期的な構想としては、ライブラリがイベントを第一級の責務として発信し、マッパーはコミュニティで維持され、APMツールは「どうデータを取得するか」ではなく「データをどう活用するか」で競争するエコシステムです。まだそこには到達していませんが、すでにフライホイールは回り始めています。

あなたもこの動きを後押しできます。TracingChannelについて議論し、利用しているライブラリでの採用を促すことが重要です。ライブラリをメンテしている場合は、untracing conventionsや公開されている提案を出発点として、TracingChannel対応を検討できます。

 

 

Original Page: Fixing JavaScript observability, one library at a time

 




IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談は「お問い合わせ」からお気軽にお問い合わせください。

 

シェアする

Recent Posts

;