Next.jsでApplication Insightsを使ってみる
はじめに
Azure において APM(アプリケーション監視)が行えるサービスとして Application Insights というサービスが提供されています。
Application Insights は様々な機能が提供されており何かと便利なのですが、イマイチ使い方が分かりにくいことがあります。特に Next.js のようなフロントエンドのフレームワークにおいては実行される場所によってライブラリを使い分ける必要があります。
今回は Next.js において Application Insights にテレメトリーを送信し、APM を開始する方法を共有します。
使用するライブラリ
現在、Application Insights へのテレメトリー送信において公式から提供されている JavaScript 用ライブラリとしては@microsoft/applicationinsights-web
と@azure/monitor-opentelemetry
の 2 つです。
各ライブラリの使い分けとしては@microsoft/applicationinsights-web
はクライアントサイド、@azure/monitor-opentelemetry
はサーバーサイドで使用するライブラリとなります。
Next.js においてサーバーサイドやクライアントサイドという言葉は混乱しやすいのですが、あくまで実行場所の話のため、サーバーコンポーネントのようなサーバーサイドで実行されるフロントエンドにおいても@azure/monitor-opentelemetry
を使用する形になります。
サーバーサイド側の計測
前述の通り、@azure/monitor-opentelemetry
を使用します。
このライブラリは Microsoft がサポートする Application Insights(Azure Monitor)用の OpenTelemetry のディストリビューションです。
OpenTelemetry の説明については世の中に出回っているので割愛します。
@azure/monitor-opentelemetry
においてはuseAzureMonitor
関数を実行することで OpenTelemetry を使用した自動計測を行うことができます。
useAzureMonitor
関数は実行時に一回のみ実行されることが期待されており、ここで使用されるのが instrumentation.ts です。これは middleware.ts 同様 src ディレクトリ配下に置くことでサーバー起動時に一回のみ実行される処理を記述することができます。
公式ドキュメントには@vercel/otel
の使用例が記載されていますが、同様に@azure/monitor-opentelemetry
を使用することができます。
export async function register() {
if (process.env.NEXT_RUNTIME === "nodejs") {
console.log("start instrumain");
await import("./instrumentation.node");
}
}
import { useAzureMonitor } from "@azure/monitor-opentelemetry";
// eslint-disable-next-line react-hooks/rules-of-hooks
useAzureMonitor({
azureMonitorExporterOptions: {
connectionString: process.env.APPLICATIONINSIGHTS_CONNECTION_STRING,
},
});
クライアント側の計測
@microsoft/applicationinsights-web
を使用します。
サーバー側と異なり、クライアント側はブラウザ内で初期化されるため、セッション毎で初期化させることが期待されます。そのため、ルートページの layout.tsx 内で初期化する等で対応することで自動計測を開始させることができます。
また、手動計測においてはサーバー側と異なり api 経由での取得ができないため、シングルトンクラスのインスタンスを使用して行うことになります。
そのため、以下のようにインスタンスを取得する関数を定義することで二重で初期化されることを防ぎながら手動収集を行うことができます。
import { ReactPlugin } from "@microsoft/applicationinsights-react-js";
import { ApplicationInsights } from "@microsoft/applicationinsights-web";
let appInsights: ApplicationInsights | null = null;
export function getAppInsights() {
if (appInsights === null) {
const reactPlugin = new ReactPlugin();
appInsights = new ApplicationInsights({
config: {
connectionString:
process.env.NEXT_PUBLIC_APPLICATION_INSIGHTS_CONNECTION_STRING,
enableAutoRouteTracking: true,
extensionConfig: [reactPlugin],
},
});
console.log("Start load Application Insights");
appInsights.loadAppInsights();
}
return appInsights;
}
サンプルアプリによる計測例
さて、以上の説明を含めてサンプルアプリケーションを実装してみます。
コードは以下に格納してあります。
サーバーサイドの手動計測については Node.js の OpenTelemetry SDK と同様に@opentelemetry/api
を使用することで行うことができます。
以下は Server Actions でトレーシングを行う例です。
"use server"
import { trace } from '@opentelemetry/api';
export async function getTodos() {
const tracer = trace.getTracer("todo tracer");
...
getSpan.addEvent("5秒待ちます");
await new Promise((resolve) => setTimeout(resolve, 5000));
getSpan.addEvent("処理を完了しました");
getSpan.end();
const getSpan.end();
...
}
すると、Application Insights に以下のように送信されます。
きちんとトレーシングが行われることがわかります。
次に、コンポーネント内でのクリックをカスタムイベントとして収集してみます。
"use client";
import { getAppInsights } from "@/utils/app-insights";
import type { Todo } from "@/types/todo";
type Props = {
todos: Todo[];
};
export const TodoList = ({ todos }: Props) => {
const onClickTodo = (todo: Todo) => {
const appInsights = getAppInsights();
appInsights.trackEvent({
name: `${todo.name}がクリックされました`,
});
};
return (
<ul>
{todos.map((todo) => {
<li key={todo.id} onClick={() => onClickTodo(todo)}>
{todo.title}
</li>;
})}
</ul>
);
};
すると以下のように Application Insights に送信されます。
これにより Application Insights で RUM(リアルタイムユーザーモニタリング)を行うことができます。
Discussion