Next.jsの稼働状況をNew Relicで監視してみる

2024/12/21に公開

はじめに

現代のウェブアプリケーションでは、パフォーマンスの最適化と継続的な監視がますます重要になっています。特にNext.jsのようなモダンなフレームワークを使ったアプリケーションでは、ユーザー体験を損なうことなくスムーズに動作させることが求められます。しかし、問題の原因を特定するのは簡単ではありません。

そこで、今回ご紹介するのが「New Relic」です。New Relicはアプリケーションのパフォーマンスをリアルタイムで監視し、ボトルネックやエラーの発生箇所をすばやく特定することができる強力なツールです。本記事では、Next.jsアプリケーションにNew Relicを導入し、活用する方法について分かりやすく解説します。

このガイドを通して、監視ツールの導入がどのようにアプリケーション運用を効率化し、開発者の負担を軽減するかを体感していただけるはずです。

公式のチュートリアルをベースにして進めながら補足情報も付け足して進めていきます。

New Relic公式のチュートリアルはこちら

https://newrelic.com/blog/how-to-relic/how-to-monitor-app-based-router-nextjs-application

New Relicについて

New Relicは、アプリケーションやインフラストラクチャのパフォーマンスを監視し、問題を可視化・解決するためのオールインワン型の監視ツールです。クラウドベースのサービスで、開発者や運用チームがシステムの健全性を維持し、パフォーマンスを最適化するために使用されます。

主な特徴

1.アプリケーションパフォーマンスモニタリング (APM)

  • アプリケーションの内部動作を詳細に追跡し、リクエストの遅延やエラーの原因を特定します。
  • 特に、トランザクションのトレースやデータベースクエリのボトルネックを検出する機能が優れています。

2.インフラストラクチャモニタリング

  • サーバー、コンテナ、クラウドインフラストラクチャなどのリソースの使用状況を追跡します。
  • メトリクスの収集や、異常検出のためのアラート設定が可能です。

3.分散トレーシング

  • マイクロサービス間の通信を可視化し、システム全体のデータフローと遅延箇所を特定します。
  • 特に複雑な分散システムで問題解決に役立ちます。

4.ログ管理と分析

  • アプリケーションやサーバーのログを集約し、リアルタイムで検索・分析が可能です。
  • 他の監視データと統合することで、迅速なトラブルシューティングを実現します。

5.ユーザーエクスペリエンスモニタリング

  • Webアプリケーションやモバイルアプリのユーザーが実際にどのような体験をしているかを測定します。
  • ページロード時間やエラー率を追跡し、UXの改善ポイントを明確化します。

6.アラートとダッシュボード

  • メトリクスに基づいたアラートを設定可能で、異常が発生した際に通知を受け取れます。
  • カスタマイズ可能なダッシュボードで、リアルタイムのデータを視覚的に確認できます。

メリット

  • 統合性の高さ: アプリケーション、インフラ、ログなどを一元的に監視できるため、問題発見が効率的。
  • マルチプラットフォーム対応: クラウド(AWS、GCP、Azure)やオンプレミス環境で利用可能。
  • 豊富なサポート言語: Java、Node.js、Python、Ruby、PHPなど、多くのプログラミング言語をサポート。

使用例

  • Webアプリケーションのレスポンスが遅いと感じた際に、データベースや外部APIとの接続のどこでボトルネックが発生しているかを分析。
  • サーバーのCPUやメモリ使用率が急激に増加した場合、アラートを受け取って迅速に対応。
  • マイクロサービス間の通信遅延を特定し、サービス間の依存関係を再構築。

競合ツールとの比較

New Relicは、DatadogやDynatraceといった他のモニタリングツールと競合しますが、特にAPM分野では長い歴史があり、使いやすさと機能のバランスが高いと言われています。

New Relicの設定

アカウント登録

それではNew Relicにアカウント登録をして使える状態にします。

下記のURLからお進みください。

https://newrelic.com/signup

無料で作成可能となっております。

Image from Gyazo

名前とリージョンを設定したらアカウント登録は完了です。

Image from Gyazo

アプリケーションの設定

New Relicで監視の単位としてアプリケーションというものがあり設定していきます。

どの環境を監視するか選択します。今回は「Browser」にある「Next.js」を選択します。

Image from Gyazo

ホスト側は今回監視しないので「Don't have access to host」を選択。

Image from Gyazo

こちらも選択せずに次へ

Image from Gyazo

「Next.js」を選択して次へ

Image from Gyazo

「Place a JavaScript snippet in frontend code」を選択

Image from Gyazo

アプリケーション名を決めます。今回は「nextjs-tutorial」とします。

Image from Gyazo

今回は検証のため「Capture session replays」と「Turn on distributed tracing」にチェックを入れて次へ進みます。

Browser agentを動かすためのコードが表示されますが今回は別で設定します。

以上でアプリケーションの設定は終了となります。

Image from Gyazo

ライセンスキーの発行

左下のアカウント名から「API Keys」を選択

Image from Gyazo

「Create a key」を選択

Image from Gyazo

Key typeを「Ingest - License」にしてから「Create a key」を選択するとライセンスキーが発行されますので控えておいてください。

Image from Gyazo

Next.jsの設定

Next.jsのプロジェクト作成

まずはNext.jsのプロジェクトを作成します。

作成手順についてはこちらにも記載がありますのでこちらをご参考頂けたらと思います。

こちらの手順通りに進めて頂いて、ローカル環境で起動できるところまで確認できましたら終了です。

https://shinagawa-web.com/blogs/nextjs-microcms-blog-tutorial#next.jsプロジェクトの作成

NPMパッケージの導入

New Relicのパッケージを下記コマンドでインストールします。

npm install newrelic

あとは.envファイルを読み込むために下記のパッケージをインストールします。

npm install dotenv

next.config.jsの編集

Next.jsアプリケーションをNew Relicで効果的に計測するには、next.config.jsファイルを変更する必要があります。

この設定により、New Relic がサポートするモジュールが webpack によって変更されないようにし、それらのモジュールを外部化します。

next.config.js ファイルを下記の通りに変更します。

next.config.js
'use strict'

const nrExternals = require('newrelic/load-externals')

module.exports = {
  experimental: {
    serverComponentsExternalPackages: ['newrelic']
  },
  webpack: (config) => {
    nrExternals(config)
    return config
  }
}

newrelic.jsファイルの作成

プロジェクト直下にnewrelic.jsファイルを作成しAPMエージェントの設定を行います。

newrelic.js
'use strict'

require('dotenv').config();

/**
 * New Relic agent configuration.
 *
 * See lib/config/default.js in the agent distribution for a more complete
 * description of configuration variables and their potential values.
 */
exports.config = {
  app_name: [process.env.NEW_RELIC_APP_NAME],
  license_key: process.env.NEW_RELIC_LICENSE_KEY,
  /**
   * This application_logging block shows the default configuration. That is,
   * it is not technically necessary; if it were omitted completely, we'd still
   * get the same configuration applied.
   *
   * We are including it here for illustrative purposes. With log forwarding
   * enabled, the Pino instance returned by `lib/logger.js` will be instrumented
   * by the `newrelic` agent and ship logs to New Relic so that they can be
   * viewed in the dashboard.
   */
  application_logging: {
    forwarding: {
      enabled: true
    }
  },

  logging: {
    /**
     * Level at which to log. 'trace' is most useful to New Relic when diagnosing
     * issues with the agent, 'info' and higher will impose the least overhead on
     * production applications.
     */
    level: 'trace'
  },

  /**
   * When true, all request headers except for those listed in attributes.exclude
   * will be captured for all traces, unless otherwise specified in a destination's
   * attributes include/exclude lists.
   */
  allow_all_headers: true,
  attributes: {
    /**
     * Prefix of attributes to exclude from all destinations. Allows * as wildcard
     * at end.
     *
     * NOTE: If excluding headers, they must be in camelCase form to be filtered.
     *
     * @name NEW_RELIC_ATTRIBUTES_EXCLUDE
     */
    exclude: [
      'request.headers.cookie',
      'request.headers.authorization',
      'request.headers.proxyAuthorization',
      'request.headers.setCookie*',
      'request.headers.x*',
      'response.headers.cookie',
      'response.headers.authorization',
      'response.headers.proxyAuthorization',
      'response.headers.setCookie*',
      'response.headers.x*'
    ]
  }
}

コードに関する解説です。

require('dotenv').config();

プロジェクト直下にある.envファイルを読み込みます。
Next.jsではそれ自体が.env.env.localなどのファイルを自動で読み込むようになっていますが、Node.jsでnewrelic.jsを読み込むためdotenvのパッケージを使っています。

  app_name: [process.env.NEW_RELIC_APP_NAME],
  license_key: process.env.NEW_RELIC_LICENSE_KEY,

アプリケーション名とライセンスキーを設定します。
アプリケーションは複数に関連づけることができるため配列の形式となっています。

.envファイルの作成

環境変数を設定しnewrelic.jsで取得できるようにします。

NEW_RELIC_APP_NAME=xxxxxxx
NEW_RELIC_LICENSE_KEY=xxxxxxx

package.jsonの編集

次に、package.jsonファイルのscriptsセクションを修正します。

Nodeの-rオプションでアプリケーションを実行できるようにし、newrelicモジュールをプリロードします。

package.json
"scripts": {
  "dev": "NODE_OPTIONS='-r newrelic' next",
  "build": "next build",
  "start": "NODE_OPTIONS='-r newrelic' next start",
  "lint": "next lint"
}

動作確認(サーバーサイド)

サーバーサイドの監視設定が完了しましたので早速動作確認をしてみます。

Next.jsを起動しトップページにアクセスします。

すると、New RelicのAPMでログが出力されるようになります。

Image from Gyazo

次はサーバーサイドで動く処理でエラーが発生した時のログを確認します。

まずはエラーを発生させるコードを書いていきます。

/app/api/error/route.tsファイルを作成し以下のコードを書いていきます。

route.ts
export function GET() {
  throw new Error("API throw error test");
};

作成できましたらAPIにアクセスしてエラーを発生させます。

ブラウザで下記のURLを入力するとアクセス可能です。

http://localhost:3000/api/error

サーバーサイドですのでローカルで起動したコンソール上にエラーメッセージが表示されます。

想定通りエラーを発生させることができました。

Image from Gyazo

New Relicのダッシュボードでエラーのログを取得できているか確認します。

Errorの情報を取得できていることがわかります。

Image from Gyazo

さらに「Error」のリンクをクリックすると詳細が表示されます。

URLや発生時間などが確認できます。

Image from Gyazo

クライアントサイドの監視設定

サーバーサイドについてはこれまでの設定で監視できていることが確認できました。

クライアントサイドについては追加で設定が必要となります。

型定義のインストール

.tsxファイルのため事前にnewrelicの型定義をインストールします。

npm i --save-dev @types/newrelic

/app/layout.tsxファイルを/app/layout.jsに変更し、下記のように編集します。

import Script from 'next/script'
import Link from 'next/link'
import newrelic from 'newrelic'

export default async function RootLayout({ children }) {
  if (newrelic.agent.collector.isConnected() === false) {
    await new Promise((resolve) => {
      newrelic.agent.on("connected", resolve)
    })
  }

  const browserTimingHeader = newrelic.getBrowserTimingHeader({
    hasToRemoveScriptWrapper: true,
    allowTransactionlessInjection: true,
  })

  return (
    <html>
	<Script
        id="nr-browser-agent"
        dangerouslySetInnerHTML={{ __html: browserTimingHeader }}
      />
      <body>
        <ul className="navbar">
          <li><a href="/">Home</a></li>
          <li><Link href="/users" key={"users"}>Users</Link></li>
          <li><Link href="/about" key={"about"}>About</Link></li>
        </ul>
        {children}
      </body>
    </html>
  )
}

https://forum.newrelic.com/s/hubtopic/aAXPh0000001rgbOAA/new-relic-types-not-working

https://github.com/DefinitelyTyped/DefinitelyTyped/discussions/69606

layout.jsファイルを修正したらNext.jsを起動しクライアントサイドのログが出力されていることを確認できます。

Image from Gyazo

またセッションごとのブラウザでの処理内容も細かく確認できるページも用意されています。

Image from Gyazo

次にクライアントサイドで発生したエラーログの取得できるか確認します。

まずはエラーを発生させるためのボタンコンポーネントを作成します。

/components/button.tsxファイルで以下の内容を書いていきます。

button.tsx
'use client'
export const Button = () => {
  return (
    <button
      className="bg-blue-500 text-white font-bold py-2 px-4 rounded"
      type="button"
      onClick={() => {
        throw new Error("New Relic Frontend Error");
      }}
    >
      Throw error
    </button>
  );
}

このボタンをクリックするとエラーが発生し「New Relic Frontend Error」というメッセージが出力されます。

そしてそのコンポーネントを使うページも新たに用意します。

/app/test/page.tsx

page.tsx
import { Button } from "@/components/button"

const TestPage = () => {
  return (
    <div>
      <h1>
        Test Page
      </h1>
      <Button />
    </div>
  )
}

export default TestPage

作成できましたら早速ページにアクセスしボタンをクリックします。

下記のようにエラーが発生しましたらOKです。

Image from Gyazo

するとNew Relicでエラーを検知していることが確認できます。

Image from Gyazo

こちらはエラー専用のダッシュボード。

New Relic側でエラーをグループ化して表示しています。

Image from Gyazo

さらに詳細な情報を見ることも可能でアクセスしているURLや発生日時、ブラウザやOSなども確認でき開発者としては調査がスムーズに進むのかと思います。

Image from Gyazo

さいごに

今回は、Next.jsアプリケーションにNew Relicを導入し、パフォーマンス監視を行う手順をご紹介しました。New Relicは、アプリケーションやインフラのパフォーマンスを可視化し、問題の原因を迅速に特定するための非常に強力なツールです。特に、複雑化するモダンアプリケーションの運用において、その多機能性と使いやすさは大きな魅力と言えるでしょう。

この記事で紹介した内容を参考に、ぜひ皆さんのプロジェクトでもNew Relicを活用してみてください。導入は比較的簡単で、得られる効果は大きいです。パフォーマンスに課題を感じている方や、運用の効率化を目指している方には特におすすめです。

最後までお読みいただきありがとうございました!この記事が皆さんのアプリケーションの品質向上に役立つことを願っています。もし疑問点や気になる点があれば、ぜひお気軽にコメントやフィードバックをお寄せください。

記事に関するお問い合わせ📝

記事の内容に関するご質問、ご意見などは、下記よりお気軽にお問い合わせください。
ご質問フォームへ

技術支援などお仕事に関するお問い合わせ📄

技術支援やお仕事のご依頼に関するお問い合わせは、下記よりお気軽にお問い合わせください。
お問い合わせフォームへ