Next.js は最初からリクエストをトレースしている ー OpenTelemetry でエクスポートする方法

Article by: (読了時間:7分)

 

トレースは情報の宝庫であり、あなた自身、あるいは AI が遅いページを見つけて修正する助けになります。

Next.js は標準で トレーシング をサポートしています。受信リクエスト、fetch() 呼び出し、ミドルウェア、サーバーサイドレンダリングはすべて配線済みで、OpenTelemetry 互換のバックエンドにトレースを送信する準備が整っています。

ただし落とし穴があります。エクスポーターを設定しない限り、それらのトレースを目にすることはありません。

数行のコードと @vercel/otel ライブラリの助けを借りれば、アプリケーションは OpenTelemetry データを受け付けるあらゆるプラットフォーム(Sentry を含む)にトレースをエクスポートできます。(また OTLP と Sentry SDK のどちらを選ぶべきか迷っている方のために、その点についても解説します。)

 

なぜ Next.js のトレースが重要なのか

Next.js アプリのページが遅いとき、難しいのはリクエストのどの部分が原因なのかを突き止めることです。原因はミドルウェアかもしれませんし、サーバーサイドレンダリング、API ルート、データベースクエリ、上流への fetch() 呼び出し、あるいは自分で書いたカスタム関数かもしれません。

リクエストが実際にどう実行されたかのトレースがなければ、症状から逆算して作業を進めることになります。ローカルで再現し、ログを追加し、ボトルネックになりそうな箇所を推測し、本番環境が自分のマシンと同じ挙動をすることを願う、という具合です。

トレーシングを使えば、各 API 呼び出し、ページロード、データベースクエリなどが、実行タイムライン上の スパン として記録されます。トレースは 1 つのリクエストから生まれたすべてのスパンをウォーターフォールとしてまとめるため、どこで時間が費やされたかを正確に確認できます。

このトレースでは、最上位の GET /api/auth/[...nextauth] スパンが受信リクエストを表しています。その下に、Next.js がリクエストの各ステップごとに追加のスパンを作成します。ページコンポーネントの解決、API ルートの実行、レスポンスの開始といった具合です。この完全な階層構造があれば、そのリクエスト中にどこで時間が費やされたかを正確に把握できます。

Node ランタイム では、このタイムラインを独自のカスタムスパンで拡充し、各トレースにアプリ固有のコンテキストを追加していくことになります(具体例は後ほど紹介します)。

Edge ランタイム のトレースも、@vercel/otel を通じて Next.js からの自動スパンを含めることはできますが、@vercel/otel は Edge でのカスタムスパンをサポートしていません。

OpenTelemetry はトレースをはじめとするさまざまなテレメトリデータを記録し、それをサポートする任意のバックエンドへ転送するためのオープン標準です。このデータをアプリから取り出すには、OpenTelemetry に送信先を伝えるだけで済みます。

 

Next.js で OpenTelemetry を設定する

Next.js ではサーバーサイドのトレーシングがすでに有効になっています。OpenTelemetry に必要なのは、送信先を知ることだけです。

Vercel は @vercel/otel というライブラリを提供しており、これが OpenTelemetry SDK の設定を代行し、Node ランタイム と Edge ランタイム の違いを自動的に吸収してくれます。ここではこれを使って SDK を設定し、トレースを Sentry にエクスポートします。すでに OpenTelemetry スタックを運用していて、Next.js をセルフホストしている場合に特に有用です。Vercel でホストしている場合は、他の選択肢もあります。

Sentry は OpenTelemetry データの送信先として利用できる OTLP 直接エンドポイントを提供していますが、OTLP 互換であればどのバックエンドでも構いません。

Next.js は起動時にプロジェクトルートから instrumentation.ts を自動的に読み込みます。register 関数はどのリクエストが処理されるよりも前に一度だけ実行されるため、OpenTelemetry SDK を設定するのに適したフックとなります。基本的に、アプリに追加すべきものはこれだけです。Sentry へトレースをエクスポートするには、あと 2 つの環境変数が必要になります。これについてはすぐ後で説明します。

@vercel/otel の完全なセットアップガイドは Vercel のドキュメントでご覧いただけます。

 

独自のスパンを追加する

Next.js にはトレーシングがあらかじめインストルメントされていますが、そこから発行されるスパンは汎用的なもので、あなた固有のビジネスロジックに関する属性は含まれていません。 出発点としては良く、有用ですが、トレーシングを本当に活かすには、アプリケーションの重要な部分に独自のスパンを追加する必要があります。

インシデント発生時に名前を挙げるような重要なビジネス操作の周辺に、独自のスパンを追加してください。請求書の生成、カードの決済、アップロードされた画像のリサイズ、AI モデルの呼び出し、レコメンドのスコアリング、サードパーティ API への顧客情報の同期などです。

すべてのヘルパー関数をラップする必要はありません。ユーザー体験やビジネス指標に影響する、高レベルな操作から始めましょう。

スパン名には、invoice.generate-pdfai.summarize-ticket のように、一貫したアクション指向の名前を使ってください。 ドット記法による命名は一般的な慣習です。関連するスパンをドメインごとにグループ化するのに便利で、後で集計する際に検索範囲を絞り込むのにも使えます。 たとえば、まず invoice のスパンをすべて調査し、そこから invoice.generate-pdf のような特定の操作へと絞り込んでいく、といった使い方ができます。

属性については、パフォーマンス調査の際にトレースのフィルタリングやグループ化に役立つコンテキストを追加するようにしてください。フィーチャーフラグ、プランの階層、バッチサイズ、アイテム数、ペイロードサイズ、生成されたファイルサイズなどです。 生の user ID、メールアドレス、シークレット、プロンプト、その他カーディナリティが非常に高いものは、バックエンドが明示的にそのユースケースをサポートしていない限り避けてください。

たとえば、請求書ジェネレーターに追加しうるスパンを見てみましょう。

これで、請求書の生成が遅くなった場合に invoice.generate-pdf を検索し、それらのスパンを template、locale、あるいは顧客プランごとにグループ化することで、大きな請求書が原因なのか、特定の PDF テンプレートが原因なのか、それとも特定の顧客セグメントが原因なのかを素早く判断できます。

@vercel/otel でのカスタムスパンに関する重要な注意点は、Node ランタイムでのみサポートされているということです。@vercel/otel ライブラリは Edge ランタイムでも自動スパンをインストルメントしますが、独自のスパンを追加することはできません。 Edge ランタイムでカスタムスパンを追加したい場合は、代わりに @sentry/nextjs SDK の利用をご検討ください。

 

OTLP バックエンドにトレースを送信する

OTLP は、OpenTelemetry のトレースを転送するための標準プロトコルです。OTLP バックエンドとは、それらのトレースを受け取って保存し、トレースのウォーターフォールを検索・フィルタリング・表示するための UI を提供する送信先を指します。Sentry のような オブザーバビリティプラットフォームでもよいですし、他の OpenTelemetry 互換サービスでも構いません。

@vercel/otel は、エクスポーターの設定に標準の OTEL_EXPORTER_OTLP_* 環境変数を想定し、それを尊重します。したがって Sentry の OTLP 直接エンドポイントへエクスポートするには、エンドポイント URL と認証ヘッダーという 2 つの環境変数を設定する必要があります。エンドポイント URL と public key は Project Settings → Client Keys (DSN) で確認できます。

次の 2 つの環境変数を設定します。

アプリをデプロイしている場所であればどこでも構いませんので、実行時に存在するようこれらを設定してください。設定が済めば、すぐに Sentry の Trace View にトレースが入り始めるはずです。 Sentry SDK も不要、コレクターも不要、コード変更も最小限です。

 

OTLP と Sentry SDK のどちらを選ぶか

OpenTelemetry はベンダーニュートラルであり、すでにそのエコシステムに深く入り込んでいる場合には優れた選択肢です。しかしトレーシングを初めて設定するのであれば、特に Next.js においては、私は Sentry SDK をおすすめします。

@vercel/otel は設定が簡単ですが、全体像を捉えられるわけではありません。Node ランタイムと Edge ランタイム から有用なサーバーサイドのスパンをエクスポートできますが、トレースのブラウザ側、Sentry の Issues、その他 SDK がもたらすデバッグコンテキストは得られません。

機能 @vercel/otel から Sentry OTLP へ直接送信 @sentry/nextjs SDK
Node サーバーのトレーシング あり あり
Edge ランタイムのトレーシング 自動サーバースパンのみ あり
カスタムスパン Node ランタイムのみ Node、Edge、ブラウザ
ブラウザのトレーシング なし あり
エラーモニタリング なし あり
ソースマップ適用済みスタックトレース なし あり
Session Replay なし あり
Logs なし あり

実際の選択は、インテグレーションの数から想像するよりもずっとシンプルです。チームが本番環境の問題を発見・グループ化・デバッグするために Sentry に依存しているなら、SDK を使ってください。すでに OpenTelemetry のパイプラインがある、段階的に移行したい、あるいは標準ベースのサーバートレースだけを Sentry に送れれば十分だという場合は、OTLP 直接送信のほうが軽量な選択肢になります。

互換性に関する注意点が 1 つあります。@vercel/otel と Sentry SDK の OpenTelemetry セットアップを同じアプリ内で初期化しないでください。どちらもトレーサープロバイダーを登録するためです。すでに独自の NodeTracerProvider を管理していて、そこに Sentry を併用したい場合は、Sentry.init()skipOpenTelemetrySetup: true を設定し、カスタムセットアップガイドに従ってください。

アプリを Vercel 上で実行していて、アプリレベルのエクスポーター設定ではなくプラットフォームレベルの転送を利用したい場合は、Vercel Drains でも Sentry にログとトレースを送信できます。ただし、ドレインも OTLP 直接送信も、SDK ベースのエラーモニタリングを置き換えるものではない点にご注意ください。

 

次のステップ

Next.js のトレースが Sentry に送信されるようになったら、それをチームが行動に移せるものへと変えていきましょう。Monitors を使って、最も重要なルートや操作を監視します。認証フロー、チェックアウト、ダッシュボード、API ルート、そしてユーザーから遅いと苦情のあるページなどです。Monitors はスパンやその他のアプリケーションシグナルに対してしきい値を評価し、条件を超えたときにそれを Issue へと変換できます。

それらのモニターを Alerts と組み合わせれば、新規の問題や悪化している問題を、Slack、メール、PagerDutywebhooks など、チームが使っているワークフローを通じて適切な人に届けられます。これにより、トレースデータからトリアージ可能な Issue へ、そしてチームが取るべきアクションへとつながる道筋ができあがります。

そのうえで、毎週答えを知りたい問いを軸にダッシュボードを構築しましょう。どのルートが最も遅いのか、どのカスタムスパンが退行しているのか、デプロイ前後でパフォーマンスは変わったのか、といった問いです。

トレースが最も役立つのは、それが運用のループの一部になったときです。重要な経路を監視し、逸脱があればアラートを出し、ダッシュボードで修正が実際に効いたかを確認する。そして Seer Agent を使えば、トレースを自動的に調査し、遅いリクエストや失敗したリクエストの原因を説明させることもできます。

 

 

FAQ


 
■ Sentry にトレースを送るには OpenTelemetry Collector が必要ですか?

いいえ。Sentry は現在、取り込みエンドポイントで OTLP トレースを直接受け付けています。単純なケースであれば、2 つの環境変数で @vercel/otel を Sentry に直接向けるだけです。コレクター をパイプラインに戻すのは、取り込み前にトレースをサンプリング、変換、あるいは複数の宛先にルーティングする必要がある場合だけで構いません。

■ OpenTelemetry SDK を直接使わずに @vercel/otel を使うのはなぜですか?

Edge ランタイムのためです。手動セットアップは @opentelemetry/sdk-node に依存しますが、これは Edge では動作しません。つまり ミドルウェア と Edge のルートハンドラーは自動スパンを生成せず、process.env.NEXT_RUNTIME === 'nodejs' で import をガードする必要があります。@vercel/otel は、Next.js の自動サーバースパンについて Node ランタイムと Edge ランタイムの両方で動作します。ただしカスタムスパンは依然として Node ランタイムのみです。

■ @vercel/otel はブラウザをトレースしますか?

いいえ。@vercel/otel は Node ランタイムと Edge ランタイムでのみ動作します。インストルメントする対象はサーバーであり、クライアントではありません。ブラウザで始まりサーバーへと続くエンドツーエンドのトレースには、別途クライアントサイドのインストルメンテーションが必要です。@sentry/nextjs SDKinstrumentation-client.ts エントリーポイントを通じてブラウザをカバーします。

■ @vercel/otel と Sentry SDK を同時に使えますか?

直接には使えません。どちらも OTel のプロバイダーを登録するため、競合してしまいます。@vercel/otel から Sentry SDK へ移行したい場合は、registerOTel()Sentry.init() に置き換えてください。(@vercel/otel ではなく)独自の NodeTracerProvider セットアップを別途持っている場合は、skipOpenTelemetrySetup: true を使って Sentry SDK を併用できます。

■ 期待していたスパンがすべて表示されないのはなぜですか?

Next.js はデフォルトスパンのうち有用な一部を発行します。低レベルの内部処理を含めてすべてを見るには、環境変数に NEXT_OTEL_VERBOSE=1 を設定してください。独自の fetch インストルメンテーションを実行していて、Next.js の自動 fetch スパンを抑制したい場合は、NEXT_OTEL_FETCH_DISABLED=1 を設定します。

■ OTLP 直接取り込みの制限は何ですか?

現在 open beta です。スパンイベントは完全に破棄されます。一方、スパンリンクと配列属性は取り込まれて Trace View に表示されますが、検索、フィルタリング、集計はできません。完全な忠実度と、エラーやログとの緊密な相関が必要な場合は、@sentry/nextjs SDK のほうが豊かな選択肢となります。

 


 

 

 

Original Page: Next.js already traces your requests. Here’s how to export them with OpenTelemetry.

 




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

 

シェアする

Recent Posts

;