こんにちは。AlphaDrive フロントエンドエンジニアの aku11i です。
Next.js で開発中のプロジェクトに New Relic APM / Browser を導入しました。
実は前にも対応したことがあったのでこれで2度目ですが、改めて調べ直したところ @newrelic/next というパッケージが新しく公開されていました。
Node.js エージェントをそのまま使う方法に比べて確認できる情報が増えるなどメリットがありましたので紹介したいと思います。
※ この記事で紹介する内容は 2022 年 8 月時点のものです。今後情報が古くなる可能性がありますのでご注意ください。
また、今回記事にするにあたって検証に使用したコードを公開しています。以降は導入手順と共にこのPRでの変更差分も紹介します。
- @newrelic/next パッケージについて
- 導入する
- トランザクション名を pages/ 下のファイル構造に合わせて命名する
- getServerSidePropsとmiddlewareをトレースする
- ブラウザー側のイベントを監視する
- 最後に
@newrelic/next
パッケージについて
https://github.com/newrelic/newrelic-node-nextgithub.com
New Relic が公式で提供する Next.js 向けの組み込み支援ツールで、初回リリースが 2022 年 3 月となっています。
最近公開されたばかりのようで破壊的変更などもままあるようです。依存関係の更新など柔軟に対応できないプロジェクトへの導入はもう少し様子を見た方が良いかも知れません。
主な機能は下記の通りです。
- トランザクション名を
pages/
下のファイル構造に合わせて命名する - getServerSidePropsとmiddlewareをトレースする
この後説明しますが、どちらも非常に助かる機能です。
この辺りのコードを見ると分かるように、Next.js の内部 API を上書きしてモニタリング用の処理を仕込むことで実現しているようです。
next
@newrelic/next
それぞれのバージョンを上げる際は API の互換性が崩れる可能性がありそうなので注意した方が良さそうです。
導入する
このパッケージは従来の Node.js の APM エージェントを拡張するものなので、先にエージェントのセットアップを済ませておく必要があります。
導入差分で見たい方はこちらをどうぞ。
https://github.com/aku11i/playground/pull/1/commits/6a054498a9913a6b9ff848928b60e781d635f820
次に @newrelic/next
を導入しますが、こちらは非常に簡単です。
npm からインストールします。
Next.js のバージョンが古い方は対応バージョンの範囲内かどうか、リリースページを見るなりして確認しておいた方が良いでしょう。
npm install @newrelic/next
次に package.json
の start
スクリプトで newrelic
が読み込まれるところを @newrelic/next
に置き換えます。
"start": "NODE_OPTIONS='--require @newrelic/next' next start",
これだけで APM を拡張することができました。変更差分はこちらです。
https://github.com/aku11i/playground/pull/1/commits/acfc693b47f3c24f828b868d39f1a35f8fcc4dd9
Browser の導入については後述します。
トランザクション名を pages/
下のファイル構造に合わせて命名する
@newrelic/next
はトランザクションの命名や振り分けを自動で行ってくれるためとても助かりました。
例えば getServerSideProps
で実装されたユーザーページ /users/[userId]
で考えてみます。
/users/1
にブラウザーからアクセスされると、初回(SSR)はそのままの URL パスでリクエストを受けますが、ページ内遷移でアクセスされた場合は /_next/data/{buildId}/ja/users/1.json
といった URL にページの props が入った JSON がリクエストされます。
従来の New Relic エージェントをインストールしただけではリクエストされたパス情報がそのままトランザクション名となり出力されるだけでした。(ユーザー ID などパス内の変動要素は自動で識別され *
に置き換えられるようです)
トランザクション名を分かりやすい任意の名前に変えるにはリクエストを受けた時に setTransactionName
を実行する必要がありましたが、@newrelic/next
ではそういった対応をせずとも Next.js のファイルベースルーティングに基づいたトランザクション名に整理してくれます。
個別のトランザクション詳細ページではアクセスされた正確な URL を確認することができるので、遅いトランザクションが発生しているユーザーを特定することも可能です。
URL パスが /_next/
で始まっているかどうかを見ることでページ内遷移か SSR かも分かります。
getServerSidePropsとmiddlewareをトレースする
getServerSideProps と Next.js 12.2 から安定版になった next/middleware のトレースにも対応しています。
今回の検証コードではユーザーページにアクセスした時に middleware で 500ms の待ち時間が発生し、 getServerSideProps で最大2秒の待ち時間がランダムに発生するようになっています。
New Relic のトランザクションの個別詳細ページにアクセスすると、それぞれの処理時間やリクエスト全体に占める割合を確認することができます。
ブラウザー側のイベントを監視する
ブラウザー側の監視も行いたい場合は New Relic Browser を導入します。
https://github.com/newrelic/newrelic-node-nextjs/blob/main/docs/inject-browser-agent.mdgithub.com
上記ドキュメントを見ると getServerSideProps の中でスクリプトを発行し、ブラウザー側に渡して読み込ませています。
getTimingHeader
は newrelic
からインポートされているので、これは @newrelic/next
の機能というわけではなさそうです。
今回は上記の手順を少し変更し、 next/script を使用するようにしました。
オプションで hasToRemoveScriptWrapper
を渡せば <script>
タグを抜いた状態で取得できるのがポイントです。
変更差分:https://github.com/aku11i/playground/pull/1/commits/4d5f563a5a1c069b90be51ae6aa01026099f53ff
正常にスクリプトが読み込まれていれば、Webページ上の window.newrelic
にオブジェクトがあるはずです。
newrelic の型定義について
newrelic
パッケージは型定義ファイルが同梱されておらず、 @types/newrelic
パッケージは一応存在しますが更新が追いついていないみたいです。
getTimingHeader
の引数の型が定義されていなかったため、ひとまず自分で定義しました。
declare module "newrelic" { // https://newrelic.github.io/node-newrelic/docs/API.html#getBrowserTimingHeader export function getBrowserTimingHeader(params?: { /** Nonce to inject into <script> header. */ nonce?: string; /** Used to import agent script without <script> tag wrapper. */ hasToRemoveScriptWrapper?: boolean; }): string; }
静的ページに導入したい場合
SSR ではない静的ページの場合は、ドキュメントページの最後に記載されている通り従来通りの Copy & Paste の方法で導入するのが良いようです。
For static compiled pages, you can use the copy-paste method for enabling the New Relic browser agent.
ただこの方法は環境別に読み込ませるのが面倒なので、将来的に簡単な方法が追加されることに期待しています。(getStaticProps で生成できるなど)
最後に
AlphaDrive/NewsPicks の Observebility をもっと知りたい方はこちらの記事をご覧ください 💁♂️
AlphaDrive では一緒に開発してくれるエンジニアを募集しています。
カジュアル面談もやっています。フロントエンド・サーバーサイド問わず気軽にご連絡ください!