T-CREATOR

Vue.js 可観測性:Sentry/OpenTelemetry/Web Vitals で UX を数値化

Vue.js 可観測性:Sentry/OpenTelemetry/Web Vitals で UX を数値化

現代の Web アプリケーション開発において、ユーザー体験の質を把握することは非常に重要です。Vue.js で構築したアプリケーションがどれほど快適に動作しているのか、どこでエラーが発生しているのか、パフォーマンスのボトルネックはどこにあるのか。これらを「見える化」することで、ユーザーに最高の体験を提供できるようになります。

本記事では、Sentry、OpenTelemetry、Web Vitals という 3 つの強力なツールを組み合わせて、Vue.js アプリケーションの可観測性を高め、UX(ユーザーエクスペリエンス)を数値化する方法をご紹介しますね。これらのツールを導入すれば、開発チームは問題を素早く発見し、データドリブンな改善を実現できるでしょう。

背景

アプリケーション監視の重要性

Vue.js でフロントエンドアプリケーションを開発する際、開発環境では問題なく動作していても、本番環境では予期しないエラーが発生することがあります。ユーザーの利用環境は多様で、ブラウザの種類、ネットワーク速度、デバイスのスペックなど、さまざまな条件下で動作することになりますね。

このような複雑な環境下で、アプリケーションが正常に動作し続けているかを確認するには、可観測性(Observability) が欠かせません。

可観測性の 3 本柱

可観測性は、以下の 3 つの要素で構成されています。

#要素説明
1メトリクスCPU やメモリ使用率、リクエスト数などの数値データ
2ログアプリケーションが出力するイベントの記録
3トレースリクエストがシステムを通過する際の経路と処理時間

これら 3 つを組み合わせることで、システムの内部状態を外部から理解できるようになります。

以下の図は、可観測性の 3 本柱がどのように連携してシステムの健全性を監視するかを示しています。

mermaidflowchart TB
    app["Vue.js<br/>アプリケーション"]
    metrics["メトリクス<br/>(数値データ)"]
    logs["ログ<br/>(イベント記録)"]
    traces["トレース<br/>(処理経路)"]
    platform["監視プラットフォーム"]

    app --> metrics
    app --> logs
    app --> traces
    metrics --> platform
    logs --> platform
    traces --> platform
    platform --> insight["インサイト<br/>(問題発見と改善)"]

この図からわかるように、Vue.js アプリケーションから収集した 3 種類のデータを統合的に分析することで、より深いインサイトが得られるのです。

UX 数値化の必要性

ユーザー体験は主観的な概念ですが、これを客観的に評価するには数値化が必要です。「なんとなく遅い」「たまにエラーが出る」といった曖昧な表現ではなく、「ページ読み込みに 3.5 秒かかっている」「エラー発生率が 0.8%」といった具体的な数値で把握できれば、改善の優先順位を決めやすくなりますね。

課題

Vue.js アプリケーションにおける監視の難しさ

Vue.js はクライアントサイドで動作するため、従来のサーバーサイドアプリケーションとは異なる課題があります。

#課題詳細
1エラーの把握困難ユーザーのブラウザで発生したエラーは開発者に届きにくい
2パフォーマンス測定デバイスやネットワーク環境によって性能が大きく変動する
3ユーザー行動の追跡どの機能がどの程度使われているか把握しづらい
4非同期処理の複雑さAPI 呼び出しやコンポーネントライフサイクルの追跡が複雑

既存のツールだけでは不十分

ブラウザの開発者ツールや Vue Devtools は開発時には便利ですが、本番環境での監視には使えません。また、単一のツールだけでは可観測性の全体をカバーできないケースが多いのです。

以下の図は、Vue.js アプリケーションで発生する典型的な問題フローを示しています。

mermaidsequenceDiagram
    participant user as ユーザー
    participant vue as Vue.js App
    participant api as Backend API
    participant db as データベース

    user->>vue: ページアクセス
    Note over vue: コンポーネント<br/>マウント処理
    vue->>api: データ取得リクエスト
    Note over api: 処理遅延発生
    api->>db: クエリ実行
    db-->>api: データ返却
    Note over api: タイムアウト発生
    api-->>vue: エラーレスポンス
    Note over vue: エラー表示失敗
    vue-->>user: 白い画面
    Note over user: 何が起きたか<br/>わからない

この図が示すように、エラーがどこで発生したのか、何が原因なのかを特定するのは容易ではありません。ユーザーは問題を報告してくれないことも多く、開発者は問題の存在すら気づけないこともあります。

データの統合と分析の困難さ

パフォーマンスデータ、エラーログ、ユーザー行動データがそれぞれ別のツールに散在していると、全体像を把握するのが難しくなります。データを統合し、相関関係を見つけることが課題となるでしょう。

解決策

Sentry、OpenTelemetry、Web Vitals の組み合わせ

これらの課題を解決するため、3 つのツールを組み合わせたアプローチをご紹介します。

#ツール主な役割収集データ
1Sentryエラー監視・トラッキングエラースタック、ユーザーコンテキスト
2OpenTelemetry分散トレーシング・メトリクス収集トレース、スパン、カスタムメトリクス
3Web VitalsUX パフォーマンス測定LCP、FID、CLS などの Core Web Vitals

各ツールの役割と特徴

Sentryは、JavaScript エラーを自動的にキャプチャし、スタックトレース、ブラウザ情報、ユーザー操作履歴などの詳細な情報を提供します。エラーが発生した際の状況を再現しやすくなるため、バグ修正が格段に早くなりますね。

OpenTelemetryは、ベンダーニュートラルな計測フレームワークで、トレースやメトリクスを標準化された方法で収集できます。Vue.js アプリケーションから API サーバー、データベースまで、リクエストの全経路を追跡できるのが強みでしょう。

Web Vitalsは、Google が定義したユーザー体験の品質指標です。ページの読み込み速度、インタラクティブ性、視覚的安定性を数値化し、SEO にも影響を与えます。

以下の図は、これら 3 つのツールがどのように連携して Vue.js アプリケーションの可観測性を実現するかを示しています。

mermaidflowchart LR
    vue["Vue.js App"]

    subgraph monitoring["監視システム"]
        sentry["Sentry<br/>(エラー監視)"]
        otel["OpenTelemetry<br/>(トレーシング)"]
        vitals["Web Vitals<br/>(UX指標)"]
    end

    subgraph data["収集データ"]
        errors["エラー情報"]
        traces["トレースデータ"]
        metrics["パフォーマンス<br/>メトリクス"]
    end

    vue --> sentry
    vue --> otel
    vue --> vitals

    sentry --> errors
    otel --> traces
    vitals --> metrics

    errors --> dashboard["統合<br/>ダッシュボード"]
    traces --> dashboard
    metrics --> dashboard

    dashboard --> action["改善<br/>アクション"]

図で理解できる要点:

  • 各ツールが異なる側面から Vue.js アプリを監視
  • 収集したデータを統合ダッシュボードで一元管理
  • データに基づいた改善アクションを実行可能

統合による相乗効果

これら 3 つのツールを組み合わせることで、以下のような相乗効果が得られます。

エラーが発生した際、Sentry でエラー内容を確認しつつ、OpenTelemetry のトレースでどの API 呼び出しが失敗したかを特定し、Web Vitals でそのエラーが UX にどの程度影響を与えたかを数値で把握できるのです。

具体例

環境構築とパッケージインストール

まず、必要なパッケージをインストールしましょう。Yarn を使って以下のコマンドを実行します。

bash# Sentryの公式Vue.jsプラグイン
yarn add @sentry/vue @sentry/tracing

# OpenTelemetryのコアパッケージ
yarn add @opentelemetry/api @opentelemetry/sdk-trace-web

# Web Vitals測定ライブラリ
yarn add web-vitals

このコマンドで、3 つの監視ツールの基礎となるパッケージがインストールされます。

Sentry の初期設定

Vue.js アプリケーションに Sentry を統合する際は、アプリケーションの初期化時に設定を行います。以下はmain.tsでの設定例です。

typescript// main.ts
import { createApp } from 'vue';
import * as Sentry from '@sentry/vue';
import App from './App.vue';

const app = createApp(App);

次に、Sentry の初期化設定を行います。DSN(Data Source Name)は Sentry プロジェクトで取得したものを使用してください。

typescript// Sentry初期化設定
Sentry.init({
  app,
  dsn: 'YOUR_SENTRY_DSN', // Sentryダッシュボードから取得
  environment: process.env.NODE_ENV, // 開発・本番環境の識別
  integrations: [
    // ブラウザトレーシングを有効化
    new Sentry.BrowserTracing({
      tracePropagationTargets: [
        'localhost',
        /^https:\/\/api\.yourapp\.com/,
      ],
    }),
  ],
});

トレースサンプリングレートとエラーのリプレイ機能を設定します。本番環境では全トラフィックを記録するとコストがかかるため、適切なサンプリングレートを設定しましょう。

typescript// サンプリングとリプレイの設定
Sentry.init({
  // ... 前述の設定に追加
  tracesSampleRate: 0.1, // 10%のトランザクションをトレース
  replaysSessionSampleRate: 0.1, // 10%のセッションを記録
  replaysOnErrorSampleRate: 1.0, // エラー発生時は100%記録
});

app.mount('#app');

この設定により、Vue.js アプリケーションで発生したエラーが自動的に Sentry に送信されるようになります。

カスタムエラーハンドリング

Vue.js コンポーネント内で意図的にエラー情報を送信したい場合は、以下のように実装します。

typescript// components/UserProfile.vue
import {
  captureException,
  captureMessage,
} from '@sentry/vue';

// 非同期処理のエラーハンドリング例
async function fetchUserData(userId: string) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP Error: ${response.status}`);
    }
    return await response.json();
  } catch (error) {
    // エラーをSentryに送信
    captureException(error);
    throw error;
  }
}

重要な処理の開始や完了をログとして記録する場合は、captureMessageを使用します。

typescript// 重要な処理のログ記録
function processPayment(amount: number) {
  captureMessage(`決済処理開始: ${amount}円`, 'info');

  // 決済処理の実装
  // ...

  captureMessage(`決済処理完了: ${amount}円`, 'info');
}

OpenTelemetry の設定

OpenTelemetry を使用して、Vue.js アプリケーションのトレーシングを実装します。まず、必要なモジュールをインポートします。

typescript// telemetry.ts
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { registerInstrumentations } from '@opentelemetry/instrumentation';

次に、トレーサープロバイダーを設定し、エクスポーターを構成します。OTLP エクスポーターは、収集したトレースデータをバックエンドに送信する役割を担います。

typescript// トレーサープロバイダーの初期化
const provider = new WebTracerProvider({
  resource: {
    attributes: {
      'service.name': 'vue-app', // サービス名
      'service.version': '1.0.0', // バージョン
    },
  },
});

エクスポーターとスパンプロセッサーを設定します。バッチ処理により、効率的にデータを送信できますね。

typescript// OTLPエクスポーターの設定
const exporter = new OTLPTraceExporter({
  url: 'https://your-collector-endpoint/v1/traces',
  headers: {
    Authorization: 'Bearer YOUR_API_TOKEN',
  },
});

// バッチスパンプロセッサーの追加
provider.addSpanProcessor(new BatchSpanProcessor(exporter));
provider.register();

自動計測機能を有効化することで、XMLHttpRequest や Fetch API の呼び出しを自動的にトレースできます。

typescript// 自動計測の登録
registerInstrumentations({
  instrumentations: [
    new XMLHttpRequestInstrumentation(),
    new FetchInstrumentation(),
  ],
});

カスタムスパンの作成

Vue.js コンポーネント内で特定の処理をトレースしたい場合は、カスタムスパンを作成します。

typescript// composables/useTracing.ts
import { trace } from '@opentelemetry/api';

export function useTracing() {
  const tracer = trace.getTracer('vue-app');

  // カスタムスパンを作成する関数
  function traceOperation<T>(
    name: string,
    operation: () => Promise<T>
  ): Promise<T> {
    return tracer.startActiveSpan(name, async (span) => {
      try {
        const result = await operation();
        span.setStatus({ code: SpanStatusCode.OK });
        return result;
      } catch (error) {
        span.setStatus({
          code: SpanStatusCode.ERROR,
          message: error.message,
        });
        throw error;
      } finally {
        span.end();
      }
    });
  }

  return { traceOperation };
}

この Composable を使用することで、任意の非同期処理をトレースできるようになります。

typescript// コンポーネントでの使用例
import { useTracing } from '@/composables/useTracing';

const { traceOperation } = useTracing();

async function loadUserData() {
  await traceOperation('load-user-data', async () => {
    const response = await fetch('/api/user');
    return await response.json();
  });
}

Web Vitals の測定と送信

Web Vitals ライブラリを使用して、Core Web Vitals を測定し、結果を監視システムに送信します。

typescript// vitals.ts
import {
  onCLS,
  onFID,
  onLCP,
  onFCP,
  onTTFB,
} from 'web-vitals';
import { captureMessage } from '@sentry/vue';

各指標の測定結果を受け取り、Sentry に送信する関数を定義します。メトリクス名、値、評価(good/needs-improvement/poor)を含めましょう。

typescript// Web Vitalsメトリクスの送信
function sendToAnalytics(metric: Metric) {
  // Sentryにカスタムメトリクスとして送信
  captureMessage(`Web Vitals: ${metric.name}`, {
    level: 'info',
    tags: {
      metric_name: metric.name,
      metric_value: metric.value.toString(),
      metric_rating: metric.rating, // good/needs-improvement/poor
    },
  });
}

各 Core Web Vitals 指標の測定を開始します。これらはユーザーのブラウザで自動的に計測されます。

typescript// Core Web Vitalsの測定開始
export function initWebVitals() {
  // Largest Contentful Paint(最大コンテンツの描画時間)
  onLCP(sendToAnalytics);

  // First Input Delay(初回入力遅延)
  onFID(sendToAnalytics);

  // Cumulative Layout Shift(累積レイアウトシフト)
  onCLS(sendToAnalytics);

  // First Contentful Paint(初回コンテンツの描画時間)
  onFCP(sendToAnalytics);

  // Time to First Byte(初回バイト受信時間)
  onTTFB(sendToAnalytics);
}

アプリケーションの初期化時にこの関数を呼び出すことで、自動的に Web Vitals の測定が開始されます。

typescript// main.ts で呼び出し
import { initWebVitals } from './vitals';

// アプリケーションマウント後に測定開始
app.mount('#app');
initWebVitals();

OpenTelemetry と Web Vitals の統合

Web Vitals のデータを OpenTelemetry のメトリクスとしても送信することで、より包括的な分析が可能になります。

typescript// vitals-otel.ts
import { metrics } from '@opentelemetry/api';
import { onLCP, onFID, onCLS } from 'web-vitals';

const meter = metrics.getMeter('web-vitals');

各指標用のヒストグラムメトリクスを作成します。ヒストグラムは値の分布を記録するのに適しています。

typescript// メトリクスの作成
const lcpHistogram = meter.createHistogram(
  'web.vitals.lcp',
  {
    description: 'Largest Contentful Paint',
    unit: 'ms',
  }
);

const fidHistogram = meter.createHistogram(
  'web.vitals.fid',
  {
    description: 'First Input Delay',
    unit: 'ms',
  }
);

const clsHistogram = meter.createHistogram(
  'web.vitals.cls',
  {
    description: 'Cumulative Layout Shift',
    unit: 'score',
  }
);

測定結果を OpenTelemetry のメトリクスとして記録します。

typescript// Web VitalsをOpenTelemetryメトリクスとして記録
export function initWebVitalsWithOTel() {
  onLCP((metric) => {
    lcpHistogram.record(metric.value, {
      rating: metric.rating,
      navigation_type: metric.navigationType,
    });
  });

  onFID((metric) => {
    fidHistogram.record(metric.value, {
      rating: metric.rating,
    });
  });

  onCLS((metric) => {
    clsHistogram.record(metric.value, {
      rating: metric.rating,
    });
  });
}

エラーとパフォーマンスの相関分析

Sentry のコンテキスト機能を使用して、エラー発生時のパフォーマンス情報を付加できます。

typescript// error-context.ts
import { setContext, captureException } from '@sentry/vue';
import { onLCP, onFID, onCLS } from 'web-vitals';

// パフォーマンスメトリクスを保持
let performanceMetrics = {
  lcp: 0,
  fid: 0,
  cls: 0,
};

// メトリクスの更新
onLCP((metric) => {
  performanceMetrics.lcp = metric.value;
});
onFID((metric) => {
  performanceMetrics.fid = metric.value;
});
onCLS((metric) => {
  performanceMetrics.cls = metric.value;
});

エラー発生時に、その時点でのパフォーマンスメトリクスをコンテキストとして添付します。これにより、パフォーマンスの悪化とエラーの関連性を分析できますね。

typescript// エラーキャプチャ時にパフォーマンス情報を添付
export function captureErrorWithMetrics(error: Error) {
  setContext('performance', {
    lcp: performanceMetrics.lcp,
    fid: performanceMetrics.fid,
    cls: performanceMetrics.cls,
  });

  captureException(error);
}

ルーターナビゲーションのトレーシング

Vue Router のナビゲーションをトレースすることで、ページ遷移のパフォーマンスを測定できます。

typescript// router/index.ts
import { createRouter, createWebHistory } from 'vue-router';
import { trace } from '@opentelemetry/api';

const router = createRouter({
  history: createWebHistory(),
  routes: [
    /* ルート定義 */
  ],
});

const tracer = trace.getTracer('vue-router');

ナビゲーションガードを使用して、ルート遷移のトレーシングを実装します。

typescript// ルート遷移のトレーシング
router.beforeEach((to, from, next) => {
  const span = tracer.startSpan('route-navigation', {
    attributes: {
      'route.from': from.path,
      'route.to': to.path,
      'route.name': to.name?.toString() || 'unknown',
    },
  });

  // スパンをルートメタに保存
  to.meta.span = span;
  next();
});

ナビゲーション完了時にスパンを終了します。これにより、ページ遷移にかかった時間を正確に測定できるでしょう。

typescriptrouter.afterEach((to) => {
  const span = to.meta.span;
  if (span) {
    span.end();
  }
});

export default router;

カスタムダッシュボードの構築

収集したデータを可視化するため、Vue.js コンポーネントでリアルタイムダッシュボードを構築することもできます。

typescript// components/MetricsDashboard.vue
<script setup lang="ts">
import { ref, onMounted } from 'vue'

interface Metrics {
  errorRate: number
  avgResponseTime: number
  lcp: number
  fid: number
  cls: number
}

const metrics = ref<Metrics>({
  errorRate: 0,
  avgResponseTime: 0,
  lcp: 0,
  fid: 0,
  cls: 0,
})

定期的にメトリクスを取得し、ダッシュボードに表示します。

typescript// メトリクスの取得
async function fetchMetrics() {
  try {
    const response = await fetch('/api/metrics')
    metrics.value = await response.json()
  } catch (error) {
    console.error('メトリクス取得失敗', error)
  }
}

// 30秒ごとに更新
onMounted(() => {
  fetchMetrics()
  setInterval(fetchMetrics, 30000)
})
</script>

パフォーマンス最適化のワークフロー

以下の図は、可観測性データを活用したパフォーマンス改善のワークフローを示しています。

mermaidflowchart TD
    start["監視開始"] --> collect["データ収集<br/>(Sentry/OTel/Vitals)"]
    collect --> analyze["データ分析"]

    analyze --> check{"パフォーマンス<br/>基準を満たす?"}
    check -->|Yes| monitor["継続監視"]
    check -->|No| identify["ボトルネック特定"]

    identify --> prioritize["優先度付け<br/>(UX影響度順)"]
    prioritize --> implement["改善実装"]
    implement --> test["効果測定"]
    test --> collect

    monitor --> alert{"アラート<br/>発生?"}
    alert -->|Yes| identify
    alert -->|No| monitor

図で理解できる要点:

  • データ収集から改善実装までの継続的なサイクル
  • パフォーマンス基準に基づいた自動判定
  • アラート機能による問題の早期発見

エラー率とパフォーマンスの関連性

実際の運用では、エラー率とパフォーマンス指標の相関関係を分析することが重要です。

#パフォーマンス状態エラー率LCP 平均改善施策
1良好0.1%以下2.5s 以下現状維持
2要注意0.1-0.5%2.5-4.0s部分最適化
3問題あり0.5-1.0%4.0-5.0s優先的改善
4深刻1.0%以上5.0s 以上緊急対応必要

このような基準を設けることで、チーム全体で改善の優先度を共有できますね。

まとめ

Vue.js アプリケーションの可観測性を高めることは、ユーザーに最高の体験を提供するための必須条件です。Sentry、OpenTelemetry、Web Vitals という 3 つのツールを組み合わせることで、エラー監視、分散トレーシング、UX パフォーマンス測定を統合的に実現できます。

本記事でご紹介した実装方法を活用すれば、以下のような効果が期待できるでしょう。

まず、エラーが発生した際に即座に通知を受け取り、詳細なコンテキスト情報から原因を特定できるようになります。次に、API レスポンス時間やページ読み込み速度などのパフォーマンスメトリクスを継続的に監視し、劣化を早期に発見できますね。さらに、Core Web Vitals を数値化することで、SEO 対策と UX 改善を同時に進められます。

可観測性は一度導入して終わりではなく、継続的にデータを収集・分析し、改善を重ねていくことが大切です。ユーザーの行動データとパフォーマンス指標を組み合わせることで、より深いインサイトが得られ、データドリブンな意思決定が可能になるのです。

これらのツールを活用して、ユーザーにとって快適で信頼性の高い Vue.js アプリケーションを構築していきましょう。

関連リンク