Next.js に Datadog APM を導入する際の注意点
この記事は AoTo Advent Calendar 2023 6日目の記事です。
TL;DR
Next.js のうち、サーバーサイドで動いているモジュール群で Node.js APM を利用する方法をまとめています。
前提として、Datadog Node.js APM には互換性要件が定められており、Next.js は Standalone や AppRouter を利用していている場合に、サーバーサイドのモジュール群をトレース可能です。[1]
ただし、愚直に/app/page.jsに対してimport dd-traceのように実装すれば良いのではなく、Node.js APM ライブラリであるdd-traceが Next.js のサーバーサイドモジュール群にインポートされる前にロードされる必要があります。
そのため、最初にdd-traceをロードする方法として、このような方法が考えられます。
- カスタムサーバーによるエントリポイントの指定
 - 
next start時に Node.js CLI の--require moduleオプションの指定 
今回は、Node.js への APM の実装方法から簡単に解説をしていきます。
前提条件
Datadog Node.js APM はdd-trace(GitHub, npm) というトレーシングライブラリを提供しています。このライブラリにより、npm installによるインストールとdd-trace.init()による初期化を行うことで、Node.js アプリケーションの自動計装を開始できます。
初期化の方法としては公式ドキュメントでは、大きく2通りの方法を案内しています。
- パッケージのインポートとトレーサーの初期化を初めに行う
 
// `dd-trace`パッケージを要求(require)し、その`init`メソッドを呼び出してトレーサーを初期化
const tracer = require('dd-trace').init();
- 初めに
dd-traceを初期化できるよう、tracer.jsのように別ファイルを作成する 
import tracer from 'dd-trace';
tracer.init();
export default tracer;
コードの保守性の観点からも、本番環境では後者を採用することが多いように思います。
以下では、こちらの前提で Next.js におけるプリロード方法を解説します。
プリロードの実装と注意点
カスタムサーバー
Next.js 通常next startにより独自のサーバーを起動することができます。しかし、next startを行う場合、計装対象のアプリケーションより先に起動している必要のある dd-traceが先に読み込まれず、適切にアプリケーションを計装できません。
ここで、カスタムサーバー を利用することで、エントリポイントにdd-traceを初期化するコードを指定することができます。
{
  "scripts": {
    "dev": "node tracer.js",
    "build": "next build",
    "start": "NODE_ENV=production node tracer.js"
  }
}
しかし、カスタムサーバーは Next.js のメリットでもあるパフォーマンス最適化の機能が利用できなくなる注意点があります。
カスタムサーバーの使用を決定する前に、Next.js の統合ルーターがアプリの要件を満たせない場合にのみ使用すべきことを気に留めてください。カスタムサーバーでは、サーバーレス関数や Automatic Static Optimization などの重要なパフォーマンス最適化が削除されます。
これらの最適化による恩恵を受けていて、Datadog APM を利用するためだけにこれらのメリットを諦めるのは難しいという場合は、この方法が向いていないということがわかります。
Node.js CLI の --require-module オプション
--require moduleオプションは、起動時に指定されたモジュールをプリロードするための Node.js CLI のオプションです。node --require モジュール名またはnode -r モジュール名の形式で指定できます。
{
  "scripts": {
    "dev": "node tracer.js",
    "build": "next build",
    "start": "node --require ./tracer.js ./node_modules/.bin/next start"
  }
}
これにより、next startによるパフォーマンス最適化のメリットを享受しつつ、dd-traceをプリロードすることができます。
補足
tracer.jsのように別ファイルを作成することで、Node.js トレーシングライブラリの構成管理しやすくなります。使用されることが多い構成は、runtimeMetrics: trueやlogInjection: trueなどではないでしょうか。
これらを SetUpDatadogTracing()のように名前付き関数でまとめることで、Datadog Node.js APM の処理をカプセル化して可読性を高めることができます。
function setUpDatadogTracing() {
  const { tracer: Tracer } = require('dd-trace')
  const tracer = Tracer.init({
    runtimeMetrics: true,
    logInjection: true,
  })
}
setUpDatadogTracing()
- 
クライアントサイドの動作は Datadog Browser RUM により取得できます。 Datadog Browser RUM は Core Web Vitals をはじめ各コンポーネントの表示パフォーマンスなどを測定できます。 ↩︎
 
Discussion