Menu

OpenTelemetryによる計装方法

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

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

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

このドキュメントでは、SpanTrace、_Exporter_などの用語を使用していますが、これらはすべてOpenTelemetryオブザーバビリティプライマーで説明されています。

Next.jsはOpenTelemetry計装を標準でサポートしており、Next.js自体が既に計装されています。

はじめに

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

@vercel/otelの使用

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

Terminal
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation

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

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

追加の設定オプションについては、@vercel/otelのドキュメントをご覧ください。

補足:

  • instrumentationファイルはプロジェクトのルートに配置し、appまたはpagesディレクトリの中に入れないでください。srcフォルダを使用している場合は、pagesappと並んでsrc内に配置してください。
  • pageExtensions設定オプションを使用して接尾辞を追加する場合は、それに合わせてinstrumentationファイル名も更新する必要があります。
  • 基本的なwith-opentelemetryのサンプルを使用できます。

OpenTelemetryの手動設定

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

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

Terminal
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ランタイムと互換性がないため、process.env.NEXT_RUNTIME === 'nodejs'の場合にのみインポートするようにする必要があります。nodeを使用する場合にのみ条件付きでインポートする新しいファイルinstrumentation.node.tsを作成することをお勧めします:

instrumentation.ts
TypeScript
export async function register() {
  if (process.env.NEXT_RUNTIME === 'nodejs') {
    await import('./instrumentation.node.ts')
  }
}
instrumentation.node.ts
TypeScript
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ランタイムのサポートが必要な場合は、@vercel/otelを使用する必要があります。

計装のテスト

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

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

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

デプロイメント

OpenTelemetryコレクターの使用

OpenTelemetryコレクターを使用してデプロイする場合は、@vercel/otelを使用できます。 これはVercelでもセルフホスティングでも機能します。

Vercelへのデプロイ

VercelではOpenTelemetryが標準で動作するようになっています。

プロジェクトをオブザーバビリティプロバイダーに接続するには、Vercelのドキュメントに従ってください。

セルフホスティング

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

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

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

カスタムエクスポーター

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

カスタムスパン

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

Terminal
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
    • これはアプリルーターによって内部的に使用される値です
    • これは特別なファイル(page.tslayout.tsloading.tsなど)へのルートとして考えることができます
    • これはnext.routeとペアにした場合にのみ一意の識別子として使用できます。なぜなら、/layout/(groupA)/layout.ts/(groupB)/layout.tsの両方を識別するために使用できるからです

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

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

属性:

render route (app) [next.route]

  • next.span_type: AppRender.getBodyResult

このスパンは、アププリケーションルーターでのルートのレンダリングプロセスを表します。

属性:

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

fetch [http.method] [http.url]

  • next.span_type: AppRender.fetch

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

属性:

このスパンは環境でNEXT_OTEL_FETCH_DISABLED=1を設定することでオフにできます。これはカスタムフェッチ計装ライブラリを使用したい場合に便利です。

executing api route (app) [next.route]

  • next.span_type: AppRouteRouteHandlers.runHandler

このスパンは、アプリケーションルーターでの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

render 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

resolve page components

  • next.span_type: NextNodeServer.findPageComponents

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

属性:

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

resolve segment modules

  • next.span_type: NextNodeServer.getLayoutOrPageModule

このスパンは、レイアウトまたはページのコードモジュールの読み込みを表します。

属性:

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

start response

  • next.span_type: NextNodeServer.startResponse

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