Menu

OpenTelemetryでインストルメンテーションをセットアップする方法

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

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

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

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

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'の場合にのみそれらをインポートしていることを確認する必要があります。新しいinstrumentation.node.tsファイルを作成し、nodeを使用する場合にのみ条件付きでインポートすることをお勧めします。

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 { 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ランタイムサポートが必要な場合は、@vercel/otelを使用する必要があります。

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

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

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

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

デプロイ

OpenTelemetry Collectorを使用する

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

Vercelへのデプロイ

OpenTelemetryがVercelで標準で動作することを確認しました。

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

セルフホスティング

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

これを行うには、OpenTelemetry Collector Getting Started guideに従ってください。このガイドでは、コレクターをセットアップし、Next.jsアプリからデータを受け取るように設定する手順を説明します。

コレクターが起動して実行されたら、各プラットフォームのデプロイガイドに従い、選択したプラットフォームにNext.jsアプリをデプロイできます。

カスタムエクスポーター

OpenTelemetry Collectorは必須ではありません。@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
    • これはアプリRouterで使用される内部値です。
    • これは特別なファイルへのルート(page.tslayout.tsloading.tsなど)と考えることができます
    • /(groupA)/layout.ts/(groupB)/layout.tsの両方を識別するために/layoutを使用できるため、next.routeと組み合わせた場合にのみ一意の識別子として使用できます

[http.method] [next.route]

  • next.span_type: BaseServer.handleRequest

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

属性:

render route (app) [next.route]

  • next.span_type: AppRender.getBodyResult

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

属性:

  • 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

このスパンはアプリ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

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

このスパンは特定のページのメタデータ生成プロセスを表します(1つのルートには複数のこれらのスパンを含めることができます)。

属性:

  • 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

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