コンテンツにスキップ

OpenTelemetry で Next.js アプリをインストラメントする方法

オブザーバビリティは、Next.js アプリの動作とパフォーマンスを理解し最適化するために不可欠です。

アプリケーションが複雑になるにつれて、発生する可能性のある問題を特定し診断することがますます困難になります。ログやメトリクスなどのオブザーバビリティツールを活用することで、開発者はアプリケーションの動作に関する洞察を得て、最適化の領域を特定できます。オブザーバビリティを使用すると、開発者は問題が重大になる前にプロアクティブに対処し、より良いユーザーエクスペリエンスを提供できます。したがって、パフォーマンスを向上させ、リソースを最適化し、ユーザーエクスペリエンスを向上させるために、Next.js アプリケーションでオブザーバビリティを使用することを強くお勧めします。

アプリのインストラメンテーションには OpenTelemetry の使用をお勧めします。これは、アプリをインストラメントするためのプラットフォームに依存しない方法であり、コードを変更せずにオブザーバビリティプロバイダーを変更できます。OpenTelemetry およびその仕組みの詳細については、公式 OpenTelemetry ドキュメント をお読みください。

このドキュメントでは、ドキュメント全体で SpanTraceExporter などの用語を使用していますが、これらはすべて OpenTelemetry Observability Primer に記載されています。

Next.js は OpenTelemetry のインストラメンテーションを標準でサポートしています。つまり、Next.js 自体がすでにインストラメントされています。

OpenTelemetry を有効にすると、getStaticProps のようなすべてのコードが、役立つ属性を持つ span で自動的にラップされます。

はじめに

OpenTelemetry は拡張可能ですが、適切にセットアップするにはかなり冗長になる場合があります。そのため、@vercel/otel というパッケージを用意し、迅速に開始できるようにしました。

@vercel/otel の使用

開始するには、以下のパッケージをインストールします

ターミナル
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation

次に、プロジェクトのルートディレクトリ(または、使用している場合は src フォルダ内)にカスタムの instrumentation.ts (または .js)ファイルを作成します。

your-project/instrumentation.ts
import { registerOTel } from '@vercel/otel'
 
export function register() {
  registerOTel({ serviceName: 'next-app' })
}

@vercel/otel のドキュメント@vercel/otel で、追加の構成オプションを確認してください。

知っておくと良いこと:

  • instrumentationファイルは、プロジェクトのルートに配置する必要があり、appまたはpagesディレクトリ内には配置できません。srcフォルダを使用している場合は、pagesおよびappと同じレベルのsrc内にファイルを配置してください。
  • pageExtensions 設定オプションを使用してサフィックスを追加する場合、instrumentation ファイル名も一致するように更新する必要があります。
  • 基本的な with-opentelemetry の例を作成しました。これを使用できます。

OpenTelemetry の手動設定

@vercel/otel パッケージは多くの設定オプションを提供しており、一般的なユースケースのほとんどに対応できるはずです。しかし、ニーズに合わない場合は、OpenTelemetry を手動で設定できます。

まず、OpenTelemetry パッケージをインストールする必要があります

ターミナル
npm install @opentelemetry/sdk-node @opentelemetry/resources @opentelemetry/semantic-conventions @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-http

次に、instrumentation.tsNodeSDK を初期化できます。@vercel/otel とは異なり、NodeSDK は edge runtime と互換性がありません。そのため、process.env.NEXT_RUNTIME === 'nodejs' の場合のみインポートするようにしてください。Node を使用する場合のみ条件付きでインポートする新しいファイル instrumentation.node.ts を作成することをお勧めします。

instrumentation.ts
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.ts')
  }
}
instrumentation.node.ts
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
import { resourceFromAttributes } from '@opentelemetry/resources'
import { NodeSDK } from '@opentelemetry/sdk-node'
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node'
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions'
 
const sdk = new NodeSDK({
  resource: resourceFromAttributes({
    [ATTR_SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()

このようにすると @vercel/otel を使用するのと同等ですが、@vercel/otel で公開されていない一部の機能を変更および拡張することが可能です。edge runtime のサポートが必要な場合は、@vercel/otel を使用する必要があります。

インストルメンテーションのテスト

ローカルで OpenTelemetry trace をテストするには、互換性のあるバックエンドを持つ OpenTelemetry Collector が必要です。当社の OpenTelemetry 開発環境 の使用をお勧めします。

すべてがうまく機能していれば、GET /requested/pathname とラベル付けされたルートサーバー span を確認できるはずです。その特定の trace の他のすべての span は、その下にネストされます。

Next.js は、デフォルトで発行されるよりも多くの span をトレースします。より多くの span を表示するには、NEXT_OTEL_VERBOSE=1 を設定する必要があります。

デプロイメント

OpenTelemetry Collector の使用

OpenTelemetry Collector でデプロイする場合、@vercel/otel を使用できます。これは Vercel 上でも、セルフホストでも動作します。

Vercel へのデプロイ

Vercel では OpenTelemetry がすぐに動作するようにしました。

Vercel のドキュメント を参照して、プロジェクトをお使いのオブザーバビリティプロバイダーに接続してください。

セルフホスティング

他のプラットフォームへのデプロイも簡単です。Next.js アプリからテレメトリデータを受信・処理するには、独自の OpenTelemetry Collector を起動する必要があります。

これを行うには、OpenTelemetry Collector Getting Started ガイド に従ってください。これにより、コレクターのセットアップと Next.js アプリからのデータ受信設定について案内されます。

コレクターが稼働したら、各プラットフォームのデプロイメントガイドに従って、選択したプラットフォームに Next.js アプリをデプロイできます。

カスタム Exporter

OpenTelemetry Collector は必須ではありません。@vercel/otel または OpenTelemetry の手動設定 でカスタム OpenTelemetry exporter を使用できます。

カスタム Span

OpenTelemetry API を使用してカスタム span を追加できます。

ターミナル
npm install @opentelemetry/api

以下の例は、GitHub のスター数を取得し、フェッチリクエストの結果を追跡するためにカスタム fetchGithubStars span を追加する関数を示しています。

import { trace } from '@opentelemetry/api'
 
export async function fetchGithubStars() {
  return await trace
    .getTracer('nextjs-example')
    .startActiveSpan('fetchGithubStars', async (span) => {
      try {
        return await getValue()
      } finally {
        span.end()
      }
    })
}

register 関数は、新しい環境でコードが実行される前に実行されます。新しい span を作成でき、それらはエクスポートされた trace に正しく追加されます。

Next.js のデフォルト Span

Next.js は、アプリケーションのパフォーマンスに関する有用な洞察を提供するために、いくつかの span を自動的にインストラメントします。

span の属性は OpenTelemetry semantic conventions に従います。また、next 名前空間の下にいくつかのカスタム属性を追加します。

  • next.span_name - span 名の複製
  • next.span_type - 各 span タイプには一意の識別子があります。
  • next.route - リクエストのルートパターン(例: /[param]/user)。
  • next.rsc (true/false) - リクエストが RSC リクエスト(例: prefetch)であるかどうか。
  • next.page
    • これは、app router によって使用される内部値です。
    • page.tslayout.tsloading.ts などの特別なファイルへのルートと考えることができます。
    • /layout/(groupA)/layout.ts/(groupB)/layout.ts の両方を識別するために使用できるため、next.route と組み合わせてのみ一意の識別子として使用できます。

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

この span は、Next.js アプリケーションへの各受信リクエストのルート span を表します。リクエストの HTTP メソッド、ルート、ターゲット、およびステータスコードを追跡します。

属性

ルートのレンダリング (app) [next.route]

  • next.span_type: AppRender.getBodyResult

この span は、app router でルートをレンダリングするプロセスを表します。

属性

  • next.span_name
  • next.span_type
  • next.route

fetch [http.method] [http.url]

  • next.span_type: AppRender.fetch

この span は、コードで実行された fetch リクエストを表します。

属性

この span は、環境変数 NEXT_OTEL_FETCH_DISABLED=1 を設定することで無効にできます。これは、カスタム fetch インストルメンテーションライブラリを使用したい場合に便利です。

API ルートの実行 (app) [next.route]

  • next.span_type: AppRouteRouteHandlers.runHandler

この span は、app router で API Route Handler を実行するプロセスを表します。

属性

  • next.span_name
  • next.span_type
  • next.route

getServerSideProps [next.route]

  • next.span_type: Render.getServerSideProps

この span は、特定のルートの getServerSideProps の実行を表します。

属性

  • next.span_name
  • next.span_type
  • next.route

getStaticProps [next.route]

  • next.span_type: Render.getStaticProps

この span は、特定のルートの getStaticProps の実行を表します。

属性

  • next.span_name
  • next.span_type
  • next.route

ルートのレンダリング (pages) [next.route]

  • next.span_type: Render.renderDocument

この span は、特定のルートのドキュメントをレンダリングするプロセスを表します。

属性

  • next.span_name
  • next.span_type
  • next.route

generateMetadata [next.page]

  • next.span_type: ResolveMetadata.generateMetadata

この span は、特定のページのメタデータを生成するプロセスを表します(1 つのルートに複数の span がある場合があります)。

属性

  • next.span_name
  • next.span_type
  • next.page

ページコンポーネントの解決

  • next.span_type: NextNodeServer.findPageComponents

この span は、特定のページのページコンポーネントを解決するプロセスを表します。

属性

  • next.span_name
  • next.span_type
  • next.route

セグメントモジュールの解決

  • next.span_type: NextNodeServer.getLayoutOrPageModule

この span は、レイアウトまたはページのコードモジュールをロードするプロセスを表します。

属性

  • next.span_name
  • next.span_type
  • next.segment

レスポンスの開始

  • next.span_type: NextNodeServer.startResponse

このゼロ長 span は、レスポンスの最初のバイトが送信された時点を表します。