T-CREATOR

Next.js の 観測可能性入門:OpenTelemetry/Sentry/Vercel Analytics 連携

Next.js の 観測可能性入門:OpenTelemetry/Sentry/Vercel Analytics 連携

Next.js アプリケーションを本番環境で運用する際、「今どんな問題が起きているのか」「ユーザーはどのようにアプリを使っているのか」を可視化することは非常に重要です。そこで注目されるのが「観測可能性(Observability)」という概念でしょう。

本記事では、Next.js プロジェクトに OpenTelemetry、Sentry、Vercel Analytics を組み込み、アプリケーションの動作を多角的に監視・分析する方法を、初心者の方にもわかりやすく解説します。実際の設定手順からデータの見方まで、段階的に学んでいきましょう。

観測可能性とは

観測可能性(Observability)とは、システムの内部状態を外部からの出力(ログ、メトリクス、トレース)によって理解できる能力を指します。従来の「監視(Monitoring)」が「何が壊れたか」を知ることに重点を置くのに対し、観測可能性は「なぜ壊れたか」を深く追求できる点が特徴です。

Next.js のような Web アプリケーションでは、以下の 3 つの要素を組み合わせて観測可能性を実現します。

#要素説明代表的なツール
1ログ(Logs)イベントやエラーの記録Sentry、Datadog
2メトリクス(Metrics)数値データの時系列変化Vercel Analytics、Prometheus
3トレース(Traces)リクエストの流れと処理時間OpenTelemetry、Jaeger

以下の図は、観測可能性を構成する 3 つの柱とその関係性を示しています。

mermaidflowchart TB
  app["Next.js<br/>アプリケーション"]
  logs["ログ<br/>(Logs)"]
  metrics["メトリクス<br/>(Metrics)"]
  traces["トレース<br/>(Traces)"]

  app -->|エラー・イベント記録| logs
  app -->|パフォーマンス指標| metrics
  app -->|リクエスト追跡| traces

  logs --> obs["観測可能性<br/>(Observability)"]
  metrics --> obs
  traces --> obs

この 3 つの柱を統合することで、アプリケーションの「健康状態」を総合的に把握できます。

観測可能性がもたらすメリット

Next.js に観測可能性を導入することで、以下のメリットが得られます。

  • 障害の早期発見: エラーが発生した瞬間にアラートを受け取れます
  • パフォーマンス最適化: ボトルネックとなっている処理を特定できます
  • ユーザー行動の理解: どのページがよく見られているか、離脱率はどうかを把握できます
  • デバッグの効率化: 本番環境で起きた問題を再現しやすくなります

課題

Next.js アプリケーションを運用する際、開発者が直面する主な課題は以下の通りです。

エラーの見落とし

開発環境では問題なく動作していても、本番環境で特定の条件下でのみエラーが発生するケースがあります。ユーザーからの報告がない限り、これらのエラーに気づけません。

パフォーマンス劣化の検知遅れ

「なんとなく遅い気がする」という感覚的な問題を、データに基づいて判断できないため、改善の優先順位をつけられません。

複雑な処理の追跡困難

API Route から外部 API、データベースへと連鎖する処理において、どこでボトルネックが発生しているのか特定が難しい状況です。

以下の図は、観測可能性が不足している場合に起こる問題の連鎖を示しています。

mermaidflowchart LR
  user["ユーザー"] -->|リクエスト| frontend["Next.js<br/>フロントエンド"]
  frontend -->|API 呼び出し| api["API Route"]
  api -->|クエリ| db[("データベース")]

  frontend -.->|エラー?| unknown1["不明"]
  api -.->|遅延?| unknown2["不明"]
  db -.->|負荷?| unknown3["不明"]

  style unknown1 fill:#ffcccc
  style unknown2 fill:#ffcccc
  style unknown3 fill:#ffcccc

各レイヤーで何が起きているのか見えないため、問題の切り分けに時間がかかってしまいます。

データの分散

ログは CloudWatch に、エラーは Sentry に、アクセス解析は Google Analytics に…といったように、情報が分散していると、全体像を把握するのに手間がかかります。

解決策

これらの課題を解決するために、以下の 3 つのツールを組み合わせて観測可能性を実現します。

#ツール主な役割対象データ
1OpenTelemetryトレーシングの標準化トレース、スパン
2Sentryエラー監視・追跡エラー、例外
3Vercel Analyticsパフォーマンス分析Core Web Vitals、UX 指標

以下の図は、各ツールがどのように連携して観測可能性を実現するかを示しています。

mermaidflowchart TB
  nextjs["Next.js<br/>アプリケーション"]

  otel["OpenTelemetry<br/>(トレーシング)"]
  sentry["Sentry<br/>(エラー監視)"]
  vercel["Vercel Analytics<br/>(UX メトリクス)"]

  nextjs -->|リクエストフロー追跡| otel
  nextjs -->|エラー・例外送信| sentry
  nextjs -->|Web Vitals 計測| vercel

  otel --> dashboard["統合<br/>ダッシュボード"]
  sentry --> dashboard
  vercel --> dashboard

  dashboard --> insight["インサイト<br/>獲得"]

この 3 つを組み合わせることで、異なる視点からアプリケーションを観測できます。

OpenTelemetry:トレーシングの標準規格

OpenTelemetry(略称:OTel)は、分散トレーシングとメトリクス収集のためのオープンソース標準です。特定のベンダーに依存せず、さまざまなバックエンドにデータを送信できる柔軟性が魅力でしょう。

主な特徴

  • ベンダーニュートラル:特定のツールに依存しない
  • 自動計装:コードを大きく変更せずにトレースを取得
  • 詳細な追跡:関数レベルでの処理時間を可視化

Sentry:エラー監視のデファクトスタンダード

Sentry は、JavaScript エラーをリアルタイムで検知し、スタックトレースやユーザーコンテキストと共に記録するサービスです。Next.js 向けの SDK が充実しており、導入が容易な点が評価されています。

主な特徴

  • エラーの自動キャプチャ:未処理のエラーも逃さない
  • リリース追跡:どのバージョンでエラーが増えたか把握
  • ソースマップ対応:本番の圧縮されたコードも読みやすく表示

Vercel Analytics:Next.js に最適化された分析ツール

Vercel Analytics は、Core Web Vitals などの UX メトリクスを簡単に計測できるサービスです。Vercel にデプロイした Next.js アプリであれば、ほぼ設定不要で利用できます。

主な特徴

  • Real User Monitoring(RUM):実際のユーザーの体験を計測
  • Core Web Vitals の追跡:LCP、FID、CLS を自動計測
  • ページ別分析:どのページが遅いか一目瞭然

具体例

それでは、実際に Next.js プロジェクトへこれらのツールを導入する手順を見ていきましょう。

前提条件

以下の環境を前提とします。

  • Next.js 14.x 以上(App Router 使用)
  • Node.js 18.x 以上
  • Yarn パッケージマネージャー
  • Vercel アカウント(Vercel Analytics 用)
  • Sentry アカウント

プロジェクトのセットアップ

新規プロジェクトを作成する場合は、以下のコマンドを実行します。

bashyarn create next-app my-observable-app --typescript
cd my-observable-app

既存のプロジェクトに導入する場合は、そのディレクトリに移動してください。

OpenTelemetry の導入

必要なパッケージのインストール

OpenTelemetry の関連パッケージをインストールします。

bashyarn add @opentelemetry/api \
  @opentelemetry/sdk-node \
  @opentelemetry/auto-instrumentations-node \
  @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/resources \
  @opentelemetry/semantic-conventions

これらのパッケージは、トレーシングの基盤となる SDK と、自動計装のためのライブラリです。

OpenTelemetry 設定ファイルの作成

プロジェクトルートに instrumentation.ts ファイルを作成します。

typescript// instrumentation.ts
import { NodeSDK } from '@opentelemetry/sdk-node';
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';

// リソース情報とセマンティック規約をインポート
import { Resource } from '@opentelemetry/resources';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

Next に、トレースデータをエクスポートするための設定を追加します。

typescript// instrumentation.ts(続き)
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

// トレースエクスポーターの設定
const traceExporter = new OTLPTraceExporter({
  url:
    process.env.OTEL_EXPORTER_OTLP_ENDPOINT ||
    'http://localhost:4318/v1/traces',
  headers: {
    // 必要に応じて認証ヘッダーを追加
    'api-key': process.env.OTEL_API_KEY || '',
  },
});

この設定により、収集したトレースデータを指定したエンドポイントに送信できます。

SDK の初期化

OpenTelemetry SDK を初期化し、自動計装を有効にします。

typescript// instrumentation.ts(続き)
const sdk = new NodeSDK({
  // サービス名などのリソース情報を設定
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAME]:
      'my-nextjs-app',
    [SemanticResourceAttributes.SERVICE_VERSION]: '1.0.0',
  }),
  // トレースエクスポーターを指定
  traceExporter,
  // 自動計装を有効化(HTTP、データベースなど)
  instrumentations: [
    getNodeAutoInstrumentations({
      '@opentelemetry/instrumentation-fs': {
        enabled: false, // ファイルシステム操作は除外
      },
    }),
  ],
});

// SDK を起動
sdk.start();

自動計装により、HTTP リクエストやデータベースクエリなどが自動的にトレースされます。

Next.js での有効化

Next.js 14 以降では、instrumentation.ts が自動的に読み込まれます。next.config.js で明示的に有効化しましょう。

javascript// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
  // Experimental な instrumentation 機能を有効化
  experimental: {
    instrumentationHook: true,
  },
};

module.exports = nextConfig;

この設定により、サーバー起動時に OpenTelemetry が初期化されます。

カスタムスパンの作成

API Route で独自のスパン(トレースの最小単位)を作成してみましょう。

typescript// app/api/users/route.ts
import { trace } from '@opentelemetry/api';

// 現在のトレーサーを取得
const tracer = trace.getTracer('my-nextjs-app');

次に、特定の処理をスパンでラップします。

typescript// app/api/users/route.ts(続き)
export async function GET() {
  // カスタムスパンを開始
  return await tracer.startActiveSpan(
    'fetch-users',
    async (span) => {
      try {
        // データベースからユーザー情報を取得
        const users = await fetchUsersFromDB();

        // スパンに属性を追加(ユーザー数など)
        span.setAttribute('user.count', users.length);

        return Response.json(users);
      } catch (error) {
        // エラーをスパンに記録
        span.recordException(error as Error);
        span.setStatus({ code: 2 }); // ERROR
        throw error;
      } finally {
        // スパンを終了
        span.end();
      }
    }
  );
}

// ダミーのデータベース取得関数
async function fetchUsersFromDB() {
  // 実際のDB処理をここに記述
  return [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
  ];
}

このようにカスタムスパンを作成することで、任意の処理の実行時間やエラーを詳細に追跡できます。

以下の図は、トレーシングによるリクエストフローの可視化イメージです。

mermaidsequenceDiagram
  participant Client as クライアント
  participant Next as Next.js
  participant API as API Route
  participant DB as データベース

  Client->>Next: GET /api/users
  activate Next
  Note over Next: スパン開始<br/>(http.request)

  Next->>API: fetch-users
  activate API
  Note over API: スパン開始<br/>(fetch-users)

  API->>DB: SELECT * FROM users
  activate DB
  Note over DB: スパン開始<br/>(db.query)
  DB-->>API: 結果返却
  deactivate DB

  API-->>Next: JSON レスポンス
  deactivate API

  Next-->>Client: 200 OK
  deactivate Next

  Note over Client,DB: 各スパンの時間が<br/>トレースとして記録される

各処理がどれだけの時間を要したかが、階層的に記録されていることが分かります。

Sentry の導入

Sentry Wizard によるセットアップ

Sentry は公式の CLI ツールを使うと、自動的に設定ファイルを生成してくれます。

bashyarn add @sentry/nextjs
yarn sentry-wizard@latest -i nextjs

対話形式で以下の項目を入力します。

  • Sentry プロジェクトの DSN(Data Source Name)
  • 組織名とプロジェクト名
  • ソースマップのアップロード設定

これにより、sentry.client.config.tssentry.server.config.tssentry.edge.config.ts が自動生成されます。

クライアント側の設定

クライアント側でのエラーキャプチャを設定します。

typescript// sentry.client.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  // Sentry プロジェクトの DSN
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,

  // トレーシングのサンプリングレート(10%をトレース)
  tracesSampleRate: 0.1,

  // デバッグモード(開発時のみ有効化推奨)
  debug: false,
});

サンプリングレートを調整することで、トラフィックが多い場合でもコストを抑えられます。

サーバー側の設定

サーバー側(API Route など)のエラーもキャプチャするための設定です。

typescript// sentry.server.config.ts
import * as Sentry from '@sentry/nextjs';

Sentry.init({
  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,

  // サーバー側のトレーシング設定
  tracesSampleRate: 0.1,

  // 環境名を設定(production、staging など)
  environment: process.env.NODE_ENV,
});

環境名を設定しておくと、本番とステージングのエラーを区別しやすくなります。

エラーのカスタムキャプチャ

意図的にエラー情報を Sentry に送信する例です。

typescript// app/api/process/route.ts
import * as Sentry from '@sentry/nextjs';

export async function POST(request: Request) {
  try {
    const body = await request.json();

    // ビジネスロジックの実行
    await processData(body);

    return Response.json({ success: true });
  } catch (error) {
    // エラーをSentryに送信
    Sentry.captureException(error);

    return Response.json(
      { error: 'Processing failed' },
      { status: 500 }
    );
  }
}

async function processData(data: unknown) {
  // 処理ロジック
  throw new Error('Something went wrong');
}

captureException を使うことで、例外情報がスタックトレースと共に Sentry に記録されます。

ユーザーコンテキストの設定

エラーが発生したユーザーの情報を付与すると、デバッグがしやすくなります。

typescript// app/components/UserProvider.tsx
'use client';

import { useEffect } from 'react';
import * as Sentry from '@sentry/nextjs';

export function UserProvider({
  userId,
  email,
}: {
  userId: string;
  email: string;
}) {
  useEffect(() => {
    // ユーザー情報をSentryに設定
    Sentry.setUser({
      id: userId,
      email: email,
    });

    return () => {
      // クリーンアップ時にユーザー情報をクリア
      Sentry.setUser(null);
    };
  }, [userId, email]);

  return null;
}

この設定により、Sentry のダッシュボードでエラーごとに「どのユーザーに影響があったか」を確認できます。

パフォーマンストランザクションの計測

Sentry では、トランザクション(一連の処理)のパフォーマンスも計測できます。

typescript// app/lib/analytics.ts
import * as Sentry from '@sentry/nextjs';

export async function trackPerformance<T>(
  name: string,
  operation: () => Promise<T>
): Promise<T> {
  // トランザクションを開始
  const transaction = Sentry.startTransaction({
    name,
    op: 'function',
  });

  try {
    // 処理を実行
    const result = await operation();

    // 成功ステータスを設定
    transaction.setStatus('ok');
    return result;
  } catch (error) {
    // エラーステータスを設定
    transaction.setStatus('internal_error');
    throw error;
  } finally {
    // トランザクションを終了
    transaction.finish();
  }
}

使用例を見てみましょう。

typescript// app/api/analytics/route.ts
import { trackPerformance } from '@/lib/analytics';

export async function GET() {
  const data = await trackPerformance(
    'fetch-analytics-data',
    async () => {
      // 重い処理をここに記述
      const result = await fetchAnalyticsFromDatabase();
      return result;
    }
  );

  return Response.json(data);
}

async function fetchAnalyticsFromDatabase() {
  // データベースアクセスの実装
  return { views: 1000, clicks: 150 };
}

これにより、特定の処理にかかった時間を Sentry のパフォーマンスダッシュボードで確認できます。

Vercel Analytics の導入

Vercel ダッシュボードでの有効化

Vercel にデプロイしているプロジェクトの場合、ダッシュボードから Analytics を有効化するだけで基本的な計測が始まります。

  1. Vercel ダッシュボードにログイン
  2. 対象プロジェクトを選択
  3. 「Analytics」タブを開く
  4. 「Enable Analytics」をクリック

これだけで Core Web Vitals の計測が開始されます。

パッケージのインストール

より詳細な分析を行うには、Next.js 用の SDK をインストールします。

bashyarn add @vercel/analytics

このパッケージにより、カスタムイベントの送信などが可能になります。

Analytics コンポーネントの追加

ルートレイアウトに Analytics コンポーネントを追加します。

typescript// app/layout.tsx
import { Analytics } from '@vercel/analytics/react';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='ja'>
      <body>
        {children}
        {/* Vercel Analytics を有効化 */}
        <Analytics />
      </body>
    </html>
  );
}

これにより、ページビューやパフォーマンスデータが自動的に収集されます。

カスタムイベントの送信

ユーザーの特定のアクション(ボタンクリック、フォーム送信など)を追跡したい場合は、カスタムイベントを送信します。

typescript// app/components/SubscribeButton.tsx
'use client';

import { track } from '@vercel/analytics';

export function SubscribeButton() {
  const handleClick = () => {
    // カスタムイベントを送信
    track('subscribe_clicked', {
      location: 'hero_section',
      plan: 'premium',
    });

    // 購読処理
    handleSubscription();
  };

  return <button onClick={handleClick}>今すぐ購読</button>;
}

function handleSubscription() {
  // 購読ロジックの実装
  console.log('Subscription started');
}

送信したイベントは、Vercel Analytics のダッシュボードで確認できます。

Web Vitals のカスタム処理

Next.js が計測した Web Vitals を取得し、独自の処理を行うこともできます。

typescript// app/components/WebVitalsReporter.tsx
'use client';

import { useReportWebVitals } from 'next/web-vitals';

export function WebVitalsReporter() {
  useReportWebVitals((metric) => {
    // メトリクス名に応じて処理を分岐
    switch (metric.name) {
      case 'LCP':
        console.log(
          'Largest Contentful Paint:',
          metric.value
        );
        break;
      case 'FID':
        console.log('First Input Delay:', metric.value);
        break;
      case 'CLS':
        console.log(
          'Cumulative Layout Shift:',
          metric.value
        );
        break;
      case 'FCP':
        console.log(
          'First Contentful Paint:',
          metric.value
        );
        break;
      case 'TTFB':
        console.log('Time to First Byte:', metric.value);
        break;
    }

    // 独自のアナリティクスサービスに送信することも可能
    // sendToAnalytics(metric);
  });

  return null;
}

このコンポーネントをレイアウトに追加します。

typescript// app/layout.tsx(Web Vitals レポーター追加版)
import { Analytics } from '@vercel/analytics/react';
import { WebVitalsReporter } from './components/WebVitalsReporter';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang='ja'>
      <body>
        {children}
        <Analytics />
        <WebVitalsReporter />
      </body>
    </html>
  );
}

これにより、パフォーマンスデータを他のサービスにも送信できるようになります。

3 つのツールの統合運用

OpenTelemetry、Sentry、Vercel Analytics を組み合わせることで、以下のような統合的な観測が可能になります。

#シナリオ使用ツール確認内容
1ページ表示が遅いVercel AnalyticsLCP が悪化しているページを特定
2特定ページでエラー多発Sentryエラーの詳細とスタックトレース確認
3API の応答が遅いOpenTelemetryどの処理で時間がかかっているか特定
4ユーザー離脱率が高いVercel Analyticsどのページで離脱しているか分析

以下の図は、3 つのツールが連携して問題を発見・解決するフローです。

mermaidflowchart TD
  issue["パフォーマンス<br/>問題発生"]

  vercel_check["Vercel Analytics<br/>でページ特定"]
  otel_check["OpenTelemetry<br/>でボトルネック特定"]
  sentry_check["Sentry<br/>でエラー確認"]

  fix["コード修正"]
  deploy["デプロイ"]
  verify["改善確認"]

  issue --> vercel_check
  vercel_check -->|遅いページ判明| otel_check
  otel_check -->|処理時間計測| sentry_check
  sentry_check -->|エラー詳細取得| fix
  fix --> deploy
  deploy --> verify
  verify -->|メトリクス改善| issue

  style issue fill:#ffcccc
  style verify fill:#ccffcc

このように、各ツールの強みを活かしながら、問題の発見から解決まで一気通貫で対応できます。

環境変数の設定

これらのツールを動作させるには、環境変数の設定が必要です。.env.local ファイルを作成しましょう。

bash# .env.local

# OpenTelemetry の設定
OTEL_EXPORTER_OTLP_ENDPOINT=https://your-collector.example.com/v1/traces
OTEL_API_KEY=your-otel-api-key

# Sentry の設定
NEXT_PUBLIC_SENTRY_DSN=https://your-dsn@sentry.io/project-id
SENTRY_AUTH_TOKEN=your-sentry-auth-token
SENTRY_ORG=your-org-name
SENTRY_PROJECT=your-project-name

本番環境では、Vercel のダッシュボードから環境変数を設定してください。

動作確認

すべての設定が完了したら、開発サーバーを起動して動作を確認します。

bashyarn dev

ブラウザで http:​/​​/​localhost:3000 にアクセスし、以下の点を確認しましょう。

  1. OpenTelemetry: ターミナルにトレース情報が出力されるか
  2. Sentry: エラーが Sentry ダッシュボードに表示されるか
  3. Vercel Analytics: ページビューがカウントされているか

意図的にエラーを発生させて、Sentry に記録されることを確認するのも良いでしょう。

typescript// app/test-error/page.tsx
export default function TestErrorPage() {
  // クライアント側でエラーを発生させる
  const throwError = () => {
    throw new Error('Test error for Sentry');
  };

  return (
    <div>
      <h1>エラーテストページ</h1>
      <button onClick={throwError}>
        エラーを発生させる
      </button>
    </div>
  );
}

このページでボタンをクリックすると、Sentry にエラーが送信されます。

ダッシュボードの活用

各ツールのダッシュボードを定期的にチェックすることで、問題の早期発見につながります。

Sentry ダッシュボードで確認すべきポイント

  • 新しいエラーの発生件数
  • エラー率の推移(リリース前後で比較)
  • 影響を受けたユーザー数

Vercel Analytics で確認すべきポイント

  • Core Web Vitals のスコア(特に LCP、FID、CLS)
  • ページごとのパフォーマンス比較
  • デバイス・地域別の表示速度

OpenTelemetry(バックエンド)で確認すべきポイント

  • API エンドポイントごとのレイテンシ
  • データベースクエリの実行時間
  • 外部 API 呼び出しの成功率

まとめ

本記事では、Next.js アプリケーションに観測可能性を導入する方法として、OpenTelemetry、Sentry、Vercel Analytics の 3 つのツールを組み合わせた実装を解説しました。

重要なポイント

  • 観測可能性は 3 つの柱で構成: ログ、メトリクス、トレースを組み合わせることで、システムの内部状態を深く理解できます
  • OpenTelemetry でトレーシング: リクエストの流れを追跡し、ボトルネックを特定できます
  • Sentry でエラー監視: リアルタイムでエラーを検知し、スタックトレースから原因を追求できます
  • Vercel Analytics で UX 分析: 実際のユーザー体験を数値化し、改善すべきページを見つけられます

これらのツールを導入することで、「何となく遅い」「たまにエラーが出る」といった曖昧な状況から脱却し、データに基づいた改善活動が可能になります。最初はすべての機能を使いこなせなくても、まずは基本的なエラー監視とパフォーマンス計測から始めてみてください。

段階的に観測の範囲を広げていくことで、アプリケーションの品質向上とユーザー満足度の向上につながるでしょう。ぜひ、あなたの Next.js プロジェクトにも観測可能性を取り入れてみてはいかがでしょうか。

関連リンク