OpenTelemetryによる計装方法
オブザーバビリティは、Next.jsアプリの動作とパフォーマンスを理解し最適化するために非常に重要です。
アプリケーションが複雑になるにつれて、発生する可能性のある問題を特定して診断することがますます難しくなります。ロギングやメトリクスなどのオブザーバビリティツールを活用することで、開発者はアプリケーションの動作についての洞察を得て、最適化が必要な領域を特定できます。オブザーバビリティにより、開発者は問題が大きな問題になる前に積極的に対処し、より良いユーザーエクスペリエンスを提供できます。したがって、パフォーマンスの向上、リソースの最適化、ユーザーエクスペリエンスの向上のために、Next.jsアプリケーションでオブザーバビリティを使用することを強くお勧めします。
アプリの計装にはOpenTelemetryを使用することをお勧めします。 これはプラットフォームに依存しない方法でアプリを計装できるため、コードを変更せずにオブザーバビリティプロバイダーを変更できます。 OpenTelemetryとその仕組みについての詳細は、公式OpenTelemetryドキュメントをご覧ください。
このドキュメントでは、Span、Trace、_Exporter_などの用語を使用していますが、これらはすべてOpenTelemetryオブザーバビリティプライマーで説明されています。
Next.jsはOpenTelemetry計装を標準でサポートしており、Next.js自体が既に計装されています。
はじめに
OpenTelemetryは拡張可能ですが、適切に設定するのはかなり冗長になる場合があります。
そのため、迅速に開始できるよう@vercel/otel
パッケージを用意しました。
@vercel/otel
の使用
まず、以下のパッケージをインストールします:
npm install @vercel/otel @opentelemetry/sdk-logs @opentelemetry/api-logs @opentelemetry/instrumentation
次に、プロジェクトのルートディレクトリ(またはsrc
フォルダを使用している場合はその中)にカスタムinstrumentation.ts
(または.js
)ファイルを作成します:
import { registerOTel } from '@vercel/otel'
export function register() {
registerOTel({ serviceName: 'next-app' })
}
追加の設定オプションについては、@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.ts
でNodeSDK
を初期化できます。
@vercel/otel
とは異なり、NodeSDK
はedgeランタイムと互換性がないため、process.env.NEXT_RUNTIME === 'nodejs'
の場合にのみインポートするようにする必要があります。nodeを使用する場合にのみ条件付きでインポートする新しいファイルinstrumentation.node.ts
を作成することをお勧めします:
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
await import('./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ランタイムのサポートが必要な場合は、@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を使ってカスタムスパンを追加できます。
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.ts
、layout.ts
、loading.ts
など)へのルートとして考えることができます - これは
next.route
とペアにした場合にのみ一意の識別子として使用できます。なぜなら、/layout
は/(groupA)/layout.ts
と/(groupB)/layout.ts
の両方を識別するために使用できるからです
[http.method] [next.route]
next.span_type
:BaseServer.handleRequest
このスパンは、Next.jsアプリケーションへの各着信リクエストのルートスパンを表します。リクエストのHTTPメソッド、ルート、ターゲット、ステータスコードを追跡します。
属性:
- 共通HTTP属性
http.method
http.status_code
- サーバーHTTP属性
http.route
http.target
next.span_name
next.span_type
next.route
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
このスパンは、コード内で実行されるフェッチリクエストを表します。
属性:
- 共通HTTP属性
http.method
- クライアントHTTP属性
http.url
net.peer.name
net.peer.port
(指定されている場合のみ)
next.span_name
next.span_type
このスパンは環境で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
この長さゼロのスパンは、レスポンスで最初のバイトが送信された時間を表します。