コンテンツをスキップ

OpenTelemetry

可観測性は、Next.js アプリの動作とパフォーマンスを理解し、最適化するために不可欠です。

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

アプリをインストゥルメント化するには、OpenTelemetry を使用することをお勧めします。これはプラットフォームに依存しないアプリのインストゥルメント化方法であり、コードを変更することなく可観測性プロバイダーを変更できます。OpenTelemetry の詳細とその動作については、OpenTelemetry 公式ドキュメントを参照してください。

このドキュメントでは、SpanTraceExporterといった用語が使用されており、これらはすべてOpenTelemetry Observability Primerで見つけることができます。

Next.js は OpenTelemetry のインストゥルメンテーションをすぐにサポートしています。つまり、Next.js 自体はすでにインストゥルメント化されています。OpenTelemetry を有効にすると、`getStaticProps` などのすべてのコードを役立つ属性を持つ*スパン*で自動的にラップします。

はじめに

OpenTelemetry は拡張可能ですが、適切に設定するには非常に詳細な記述が必要になる場合があります。そのため、迅速に開始できるパッケージ `@vercel/otel` を用意しました。

@vercel/otel の使用

まず、以下のパッケージをインストールします

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

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

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

その他の設定オプションについては、`@vercel/otel` ドキュメントを参照してください。

知っておくと良いこと:

  • `instrumentation` ファイルは、プロジェクトのルートに配置し、`app` または `pages` ディレクトリ内には配置しないでください。`src` フォルダーを使用している場合は、`src` フォルダー内の `pages` および `app` と同じ場所にファイルを配置してください。
  • サフィックスを追加するために`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.ts` で `NodeSDK` を初期化できます。`@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 { Resource } 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: new Resource({
    [ATTR_SERVICE_NAME]: 'next-app',
  }),
  spanProcessor: new SimpleSpanProcessor(new OTLPTraceExporter()),
})
sdk.start()

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

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

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

すべてが正常に動作していれば、`GET /requested/pathname` というラベルの付いたルートサーバーのスパンが表示されるはずです。その特定のトレースからの他のすべてのスパンは、その下にネストされます。

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

デプロイ

OpenTelemetry Collector の使用

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

Vercel へのデプロイ

Vercel では OpenTelemetry がすぐに使えるようにしました。

プロジェクトを可観測性プロバイダーに接続するには、Vercel ドキュメントに従ってください。

セルフホスティング

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

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

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

カスタムエクスポート

OpenTelemetry Collector は必須ではありません。`@vercel/otel` または OpenTelemetry の手動設定を使用して、カスタムの OpenTelemetry エクスポーターを使用できます。

カスタムスパン

OpenTelemetry APIでカスタムスパンを追加できます。

ターミナル
npm install @opentelemetry/api

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

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` 関数は、コードが新しい環境で実行される前に実行されます。新しいスパンの作成を開始でき、それらはエクスポートされたトレースに正しく追加されるはずです。

Next.js のデフォルトスパン

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

スパンの属性はOpenTelemetry のセマンティック規約に従っています。また、`next` 名前空間の下にいくつかのカスタム属性を追加しています。

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

[http.method] [next.route]

  • `next.span_type`: `BaseServer.handleRequest`

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

属性

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

  • `next.span_type`: `AppRender.getBodyResult`。

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

属性

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

フェッチ [http.method] [http.url]

  • `next.span_type`: `AppRender.fetch`

このスパンは、コードで実行されたフェッチリクエストを表します。

属性

このスパンは、環境で `NEXT_OTEL_FETCH_DISABLED=1` を設定することでオフにできます。これは、カスタムのフェッチインストゥルメンテーションライブラリを使用したい場合に役立ちます。

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

  • `next.span_type`: `AppRouteRouteHandlers.runHandler`。

このスパンは、App Router での API ルートハンドラーの実行を表します。

属性

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

getServerSideProps [next.route]

  • `next.span_type`: `Render.getServerSideProps`。

このスパンは、特定のルートに対する `getServerSideProps` の実行を表します。

属性

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

getStaticProps [next.route]

  • `next.span_type`: `Render.getStaticProps`。

このスパンは、特定のルートに対する `getStaticProps` の実行を表します。

属性

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

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

  • `next.span_type`: `Render.renderDocument`。

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

属性

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

generateMetadata [next.page]

  • `next.span_type`: `ResolveMetadata.generateMetadata`。

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

属性

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

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

  • `next.span_type`: `NextNodeServer.findPageComponents`。

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

属性

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

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

  • `next.span_type`: `NextNodeServer.getLayoutOrPageModule`。

このスパンは、レイアウトまたはページのコードモジュールをロードすることを示します。

属性

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

応答の開始

  • `next.span_type`: `NextNodeServer.startResponse`。

このゼロ長スパンは、応答で最初のバイトが送信された時刻を表します。