T-CREATOR

Svelte 可観測性の実装:Sentry/OpenTelemetry/Web Vitals 連携ガイド

Svelte 可観測性の実装:Sentry/OpenTelemetry/Web Vitals 連携ガイド

Web アプリケーションを本番環境で運用する際、ユーザー体験を最適化し、問題を迅速に発見・解決するために可観測性(Observability)の実装が欠かせません。Svelte アプリケーションでも同様に、エラー追跡、パフォーマンス監視、分散トレーシングを統合的に実装することで、アプリケーションの健全性を維持できます。

本記事では、Sentry によるエラートラッキング ON、OpenTelemetry による分散トレーシング、そして Web Vitals によるパフォーマンス計測を、Svelte アプリケーションに統合する実践的な方法をご紹介します。これらのツールを組み合わせることで、フロントエンドからバックエンドまで一貫した監視体制を構築できるでしょう。

背景

可観測性とは

可観測性とは、システムの内部状態を外部から観察・理解できる能力を指します。従来の監視(Monitoring)が「問題が発生したことを知る」ことに焦点を当てるのに対し、可観測性は「なぜ問題が発生したのか」を理解することを目的としています。

可観測性は、以下の 3 つの柱(Three Pillars of Observability)で構成されますね。

#説明具体例
1メトリクス数値データの時系列変化CPU 使用率、リクエスト数、エラー率
2ログイベントの詳細記録エラーメッセージ、デバッグ情報
3トレースリクエストの処理経路分散システムでの処理フロー

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

mermaidflowchart TB
  app["Svelte<br/>アプリケーション"]
  metrics["メトリクス<br/>Web Vitals"]
  logs["ログ<br/>エラー情報"]
  traces["トレース<br/>処理経路"]

  app --> metrics
  app --> logs
  app --> traces

  metrics --> observe["可観測性<br/>プラットフォーム"]
  logs --> observe
  traces --> observe

  observe --> insight["インサイト<br/>問題の発見と解決"]

図で理解できる要点:

  • アプリケーションから 3 種類のデータを収集
  • すべてのデータが統合されて分析可能
  • 問題の原因特定が迅速化

Svelte における可観測性の重要性

Svelte は、コンパイル時に最適化された JavaScript を生成するフレームワークです。そのため、ランタイムオーバーヘッドが少なく、高速なアプリケーションを構築できます。しかし、本番環境では予期せぬエラーやパフォーマンス問題が発生する可能性があります。

特に Svelte アプリケーションでは、以下の点で可観測性が重要になりますね。

リアクティブな状態管理により、複雑な状態遷移が発生しやすく、デバッグが困難になることがあります。コンパイル後のコードは最適化されているため、エラーの発生箇所を特定しにくいことがあるでしょう。

SSR(Server-Side Rendering)や SPA(Single Page Application)の両方に対応できるため、監視対象が多岐にわたります。

課題

Svelte アプリケーションの監視における課題

Svelte アプリケーションを本番環境で運用する際、いくつかの課題に直面します。

エラーの可視性不足

開発環境では気づかないエラーが、本番環境で発生することがあります。特に以下のような問題が顕在化しにくいですね。

ユーザー環境固有のエラー(特定のブラウザやデバイスでのみ発生)、非同期処理でのエラー(Promise 拒否、タイムアウト)、サードパーティライブラリとの統合エラーなどが挙げられます。

パフォーマンス問題の特定困難

ユーザー体験に直結するパフォーマンス問題を、開発環境で再現することは困難です。以下のような指標を本番環境で計測する必要があります。

#指標説明重要性
1LCPLargest Contentful Paint - 最大コンテンツの描画時間★★★
2FIDFirst Input Delay - 最初の入力遅延★★★
3CLSCumulative Layout Shift - 累積レイアウトシフト★★★
4TTFBTime to First Byte - 最初のバイトまでの時間★★
5FCPFirst Contentful Paint - 最初のコンテンツ描画時間★★

分散システムでのトレーシング不足

モダンな Web アプリケーションは、フロントエンド、API、データベース、サードパーティサービスなど、複数のコンポーネントで構成されています。問題が発生した際、どのコンポーネントがボトルネックになっているのかを特定するのは容易ではありません。

以下の図は、分散システムにおけるトレーシングの課題を示しています。

mermaidflowchart LR
  user["ユーザー"] -->|リクエスト| svelte["Svelte<br/>フロントエンド"]
  svelte -->|API呼び出し| api["REST API"]
  api -->|クエリ| db[("データベース")]
  api -->|認証| auth["認証<br/>サービス"]
  api -->|外部API| external["サードパーティ"]

  svelte -.->|どこが遅い?| question["?"]
  api -.->|どこが遅い?| question
  db -.->|どこが遅い?| question

図で理解できる要点:

  • リクエストが複数のサービスを経由
  • ボトルネックの特定が困難
  • エンドツーエンドの可視性が必要

解決策

統合可観測性アーキテクチャ

これらの課題を解決するために、Sentry、OpenTelemetry、Web Vitals を組み合わせた統合可観測性アーキテクチャを構築します。それぞれのツールが異なる役割を担当し、補完し合うことで、包括的な監視体制を実現できますね。

以下の表は、各ツールの役割と特徴をまとめたものです。

#ツール主な役割対象データ強み
1Sentryエラー追跡ログ、エラーリアルタイム通知、スタックトレース
2OpenTelemetry分散トレーシングトレース、スパン標準化された計装、ベンダー中立
3Web Vitalsパフォーマンス計測メトリクスGoogle の推奨指標、UX 重視

アーキテクチャ全体像

以下の図は、3 つのツールがどのように連携するかを示しています。

mermaidflowchart TB
  browser["ブラウザ<br/>Svelteアプリ"]

  subgraph monitoring["監視レイヤー"]
    sentry["Sentry SDK"]
    otel["OpenTelemetry<br/>SDK"]
    vitals["web-vitals<br/>ライブラリ"]
  end

  browser --> sentry
  browser --> otel
  browser --> vitals

  sentry -->|エラー| sentrycloud["Sentry<br/>クラウド"]
  otel -->|トレース| collector["OpenTelemetry<br/>Collector"]
  vitals -->|メトリクス| analytics["分析<br/>プラットフォーム"]

  collector --> backend["バックエンド<br/>トレーシング"]

  sentrycloud --> dashboard["統合<br/>ダッシュボード"]
  collector --> dashboard
  analytics --> dashboard

図で理解できる要点:

  • フロントエンドで 3 種類の SDK を統合
  • それぞれが専用のバックエンドに送信
  • 統合ダッシュボードで一元管理

ツール選定の理由

Sentry を選ぶ理由

Sentry は、エラートラッキングのデファクトスタンダードです。以下の特徴により、Svelte アプリケーションとの相性が良いですね。

公式の Svelte SDK が提供されており、簡単に統合できます。ソースマップに対応しており、ミニファイされたコードのエラーも正確に特定できるでしょう。リリース追跡機能により、どのバージョンでエラーが発生したかを把握できます。

OpenTelemetry を選ぶ理由

OpenTelemetry は、CNCF(Cloud Native Computing Foundation)のプロジェクトであり、可観測性の標準となっています。ベンダーロックインを避けられ、複数のバックエンド(Jaeger、Zipkin、Datadog など)に対応できます。

フロントエンドとバックエンドで同じ標準を使用でき、エンドツーエンドのトレーシングが可能です。自動計装と手動計装の両方をサポートし、柔軟な実装ができますね。

Web Vitals を選ぶ理由

Web Vitals は、Google が推奨するユーザー体験指標です。SEO にも影響するため、測定と改善が重要になります。

軽量なライブラリで、パフォーマンスへの影響が最小限です。リアルユーザーモニタリング(RUM)により、実際のユーザー体験を把握できるでしょう。

具体例

環境構築とセットアップ

まず、必要なパッケージをインストールします。Yarn を使用してプロジェクトに依存関係を追加しましょう。

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

以下のコマンドで、可観測性に必要なすべてのパッケージをインストールします。

bash# Sentry SDK
yarn add @sentry/svelte @sentry/vite-plugin

# OpenTelemetry SDK
yarn add @opentelemetry/api \
  @opentelemetry/sdk-trace-web \
  @opentelemetry/instrumentation-fetch \
  @opentelemetry/instrumentation-document-load \
  @opentelemetry/exporter-trace-otlp-http

# Web Vitals
yarn add web-vitals

それぞれのパッケージの役割を理解しておきましょう。@sentry​/​svelteは Svelte 専用の Sentry SDK、@opentelemetry​/​apiは OpenTelemetry のコア API です。

Sentry の統合

Sentry の初期化

Svelte アプリケーションのエントリーポイントで、Sentry を初期化します。src​/​main.tsまたはsrc​/​main.jsで以下のように設定しましょう。

typescript// src/main.ts
import { init } from '@sentry/svelte';
import App from './App.svelte';

// Sentry初期化 - エラー追跡の開始
init({
  dsn: 'YOUR_SENTRY_DSN', // SentryプロジェクトのDSN
  environment: import.meta.env.MODE, // 環境識別(development/production)
  release: `my-svelte-app@${
    import.meta.env.VITE_APP_VERSION
  }`, // リリースバージョン
});

この設定により、アプリケーション全体でエラーが自動的にキャプチャされます。

Sentry の詳細設定

本番環境では、より詳細な設定を行うことで、有用な情報を収集できますね。

typescript// src/lib/sentry.ts
import {
  init,
  BrowserTracing,
  Replay,
} from '@sentry/svelte';

// エラー追跡とパフォーマンス監視の統合設定
init({
  dsn: 'YOUR_SENTRY_DSN',
  environment: import.meta.env.MODE,
  release: `my-svelte-app@${
    import.meta.env.VITE_APP_VERSION
  }`,

  // トレーシング設定
  integrations: [
    new BrowserTracing({
      // ルーティングトランザクションの自動作成
      tracingOrigins: [
        'localhost',
        'your-production-domain.com',
      ],
      // XMLHttpRequestとFetch APIの自動計装
      traceFetch: true,
      traceXHR: true,
    }),
  ],

  // サンプリング設定
  tracesSampleRate: 1.0, // 本番環境では0.1〜0.3程度に調整
});

tracesSampleRateは、パフォーマンストランザクションの送信率を制御します。本番環境ではコストを抑えるため、10〜30%程度に設定するのが一般的でしょう。

Vite プラグインの設定

ソースマップを Sentry にアップロードすることで、ミニファイされたコードのエラーも正確に特定できます。

typescript// vite.config.ts
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import { sentryVitePlugin } from '@sentry/vite-plugin';

export default defineConfig({
  plugins: [
    svelte(),

    // ビルド時のソースマップアップロード
    sentryVitePlugin({
      org: 'your-org-slug',
      project: 'your-project-slug',
      authToken: process.env.SENTRY_AUTH_TOKEN,
    }),
  ],

  build: {
    sourcemap: true, // ソースマップ生成を有効化
  },
});

環境変数SENTRY_AUTH_TOKENは、Sentry の設定画面から取得できます。

エラー境界の実装

Svelte コンポーネントでエラーが発生した際に、適切にキャプチャするためのエラー境界を実装しましょう。

svelte<!-- src/lib/components/ErrorBoundary.svelte -->
<script lang="ts">
  import { captureException } from '@sentry/svelte';
  import { onMount, onDestroy } from 'svelte';

  // エラーハンドリング用の状態
  let error: Error | null = null;
  let errorInfo: any = null;

  // グローバルエラーハンドラーの登録
  function handleError(event: ErrorEvent) {
    error = event.error;
    errorInfo = { message: event.message };
    captureException(event.error); // Sentryにエラー送信
  }

  onMount(() => {
    window.addEventListener('error', handleError);
  });

  onDestroy(() => {
    window.removeEventListener('error', handleError);
  });
</script>

{#if error}
  <!-- エラー表示UI -->
  <div class="error-boundary">
    <h2>エラーが発生しました</h2>
    <p>問題が報告されました。しばらくしてから再度お試しください。</p>
  </div>
{:else}
  <!-- 通常のコンテンツ -->
  <slot />
{/if}

この実装により、予期せぬエラーが発生してもアプリケーションがクラッシュせず、ユーザーに適切なメッセージを表示できますね。

OpenTelemetry の統合

OpenTelemetry の初期化

OpenTelemetry の初期化は、Sentry よりも複雑ですが、柔軟な設定が可能です。

typescript// src/lib/telemetry.ts
import { WebTracerProvider } from '@opentelemetry/sdk-trace-web';
import { registerInstrumentations } from '@opentelemetry/instrumentation';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';

// トレーサープロバイダーの作成
const provider = new WebTracerProvider({
  resource: {
    attributes: {
      'service.name': 'svelte-frontend', // サービス名の識別
      'service.version': import.meta.env.VITE_APP_VERSION,
    },
  },
});

WebTracerProviderは、ブラウザ環境でのトレーシングを管理する中核的なクラスです。

プロセッサーとエクスポーターの設定

収集したトレースデータを適切に処理し、バックエンドに送信する設定を行います。

typescript// src/lib/telemetry.ts(続き)
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';

// トレースデータのエクスポーター設定
const exporter = new OTLPTraceExporter({
  url: 'https://your-otel-collector.com/v1/traces', // OpenTelemetry Collectorのエンドポイント
  headers: {
    Authorization: `Bearer ${
      import.meta.env.VITE_OTEL_TOKEN
    }`,
  },
});

// バッチ処理でパフォーマンス向上
provider.addSpanProcessor(new BatchSpanProcessor(exporter));

// グローバルプロバイダーとして登録
provider.register();

BatchSpanProcessorは、スパンをまとめて送信することで、ネットワークリクエストを削減し、パフォーマンスへの影響を最小化します。

自動計装の設定

Fetch API やドキュメント読み込みを自動的に計装することで、手動でコードを追加する必要がなくなりますね。

typescript// src/lib/telemetry.ts(続き)
// 自動計装の登録
registerInstrumentations({
  instrumentations: [
    // Fetch APIの自動トレーシング
    new FetchInstrumentation({
      // トレース対象のURLを指定
      propagateTraceHeaderCorsUrls: [
        /^https:\/\/api\.your-domain\.com\/.*/,
      ],
      // リクエスト/レスポンスフックで追加情報を記録
      applyCustomAttributesOnSpan: (
        span,
        request,
        result
      ) => {
        span.setAttribute(
          'http.request_content_length',
          request.headers.get('content-length') || 0
        );
      },
    }),

    // ページ読み込みの自動トレーシング
    new DocumentLoadInstrumentation(),
  ],
});

この設定により、すべての Fetch リクエストとページ読み込みが自動的にトレースされます。

手動スパンの作成

特定の処理を詳細にトレースしたい場合は、手動でスパンを作成できます。

typescript// src/lib/api/user.ts
import { trace } from '@opentelemetry/api';

// トレーサーの取得
const tracer = trace.getTracer('user-api');

// ユーザーデータ取得関数
export async function fetchUserData(userId: string) {
  // スパンの開始 - 処理の開始を記録
  const span = tracer.startSpan('fetchUserData');

  try {
    // カスタム属性の追加
    span.setAttribute('user.id', userId);
    span.setAttribute('operation.type', 'read');

    // API呼び出し
    const response = await fetch(`/api/users/${userId}`);
    const data = await response.json();

    // 成功を記録
    span.setStatus({ code: 1 }); // SpanStatusCode.OK

    return data;
  } catch (error) {
    // エラーを記録
    span.setStatus({
      code: 2, // SpanStatusCode.ERROR
      message: error.message,
    });
    throw error;
  } finally {
    // スパンの終了 - 処理時間が自動計算される
    span.end();
  }
}

手動スパンを使用することで、ビジネスロジックの詳細なパフォーマンス分析が可能になるでしょう。

Web Vitals の統合

Web Vitals の計測

Web Vitals ライブラリを使用して、主要なパフォーマンス指標を計測します。

typescript// src/lib/vitals.ts
import {
  onCLS,
  onFID,
  onLCP,
  onFCP,
  onTTFB,
} from 'web-vitals';

// Web Vitalsメトリクスの送信関数
function sendToAnalytics(metric: any) {
  // メトリクスデータの構造化
  const body = JSON.stringify({
    name: metric.name, // メトリクス名(LCP, FID, CLSなど)
    value: metric.value, // 計測値
    rating: metric.rating, // 評価(good, needs-improvement, poor)
    delta: metric.delta, // 前回からの変化量
    id: metric.id, // ページ読み込みごとの一意ID
    navigationType: metric.navigationType, // ナビゲーションタイプ
  });

  // 分析エンドポイントに送信
  navigator.sendBeacon('/api/analytics/web-vitals', body);
}

sendBeaconを使用することで、ページ遷移時でも確実にデータを送信できます。

メトリクスの監視開始

各メトリクスの監視を開始し、値が利用可能になったら送信します。

typescript// src/lib/vitals.ts(続き)
// メトリクス監視の初期化
export function initWebVitals() {
  // LCP(Largest Contentful Paint)- 最大コンテンツ描画時間
  // 2.5秒以下が良好、4秒以上は改善が必要
  onLCP(sendToAnalytics);

  // FID(First Input Delay)- 最初の入力遅延
  // 100ms以下が良好、300ms以上は改善が必要
  onFID(sendToAnalytics);

  // CLS(Cumulative Layout Shift)- 累積レイアウトシフト
  // 0.1以下が良好、0.25以上は改善が必要
  onCLS(sendToAnalytics);

  // FCP(First Contentful Paint)- 最初のコンテンツ描画
  onFCP(sendToAnalytics);

  // TTFB(Time to First Byte)- 最初のバイト受信時間
  onTTFB(sendToAnalytics);
}

これらの指標を継続的に監視することで、パフォーマンスの劣化を早期に発見できますね。

Svelte コンポーネントでの利用

アプリケーションのエントリーポイントで、Web Vitals の監視を開始します。

svelte<!-- src/App.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  import { initWebVitals } from './lib/vitals';

  // コンポーネントマウント時にWeb Vitals監視を開始
  onMount(() => {
    initWebVitals();
  });
</script>

<main>
  <!-- アプリケーションのコンテンツ -->
  <slot />
</main>

この実装により、すべてのページで Web Vitals が自動的に計測されます。

バックエンドとの統合

API エンドポイントの実装

フロントエンドから送信された Web Vitals データを受け取る API エンドポイントを実装しましょう。

typescript// src/routes/api/analytics/web-vitals/+server.ts
import type { RequestHandler } from './$types';

// Web Vitalsデータの受信エンドポイント
export const POST: RequestHandler = async ({ request }) => {
  // リクエストボディの解析
  const metric = await request.json();

  // データベースまたは分析プラットフォームに保存
  await saveMetric({
    timestamp: new Date(),
    metric_name: metric.name,
    value: metric.value,
    rating: metric.rating,
    page_url: request.headers.get('referer'),
    user_agent: request.headers.get('user-agent'),
  });

  return new Response(null, { status: 204 });
};

収集したデータは、時系列データベース(InfluxDB、TimescaleDB など)に保存すると分析しやすいでしょう。

OpenTelemetry コンテキスト伝播

フロントエンドからバックエンドへのトレースコンテキスト伝播を設定し、エンドツーエンドのトレーシングを実現します。

typescript// src/lib/api/client.ts
import { context, propagation } from '@opentelemetry/api';

// トレースコンテキストを含むFetchラッパー
export async function tracedFetch(
  url: string,
  options: RequestInit = {}
) {
  // 現在のコンテキストを取得
  const currentContext = context.active();

  // トレースヘッダーの注入
  const headers = new Headers(options.headers);
  propagation.inject(currentContext, headers, {
    set: (carrier, key, value) => {
      carrier.set(key, value);
    },
  });

  // トレース情報を含むリクエスト送信
  return fetch(url, {
    ...options,
    headers,
  });
}

この実装により、フロントエンドのトレース ID がバックエンドに伝播され、一連のリクエストを追跡できます。

統合ダッシュボードの構築

Grafana での可視化

OpenTelemetry データを Grafana で可視化する設定例です。

yaml# docker-compose.yml
version: '3.8'

services:
  # OpenTelemetry Collector
  otel-collector:
    image: otel/opentelemetry-collector-contrib:latest
    command: ['--config=/etc/otel-collector-config.yaml']
    volumes:
      - ./otel-collector-config.yaml:/etc/otel-collector-config.yaml
    ports:
      - '4318:4318' # OTLP HTTP receiver
      - '8889:8889' # Prometheus metrics exporter

  # Grafana
  grafana:
    image: grafana/grafana:latest
    ports:
      - '3000:3000'
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    volumes:
      - grafana-storage:/var/lib/grafana

この Docker Compose 設定により、ローカル環境で簡単に可観測性スタックを構築できますね。

OpenTelemetry Collector の設定

収集したテレメトリーデータを適切に処理し、複数のバックエンドに送信する設定です。

yaml# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318 # フロントエンドからのデータ受信

processors:
  batch:
    timeout: 10s # 10秒ごとにバッチ送信
    send_batch_size: 1024 # 1024スパンごとに送信

exporters:
  prometheus:
    endpoint: '0.0.0.0:8889' # Prometheusメトリクス公開

  otlp:
    endpoint: jaeger:4317 # Jaegerへトレース送信
    tls:
      insecure: true

service:
  pipelines:
    traces:
      receivers: [otlp]
      processors: [batch]
      exporters: [otlp]

    metrics:
      receivers: [otlp]
      processors: [batch]
      exporters: [prometheus]

この設定により、フロントエンドからのデータを効率的に処理し、適切なバックエンドに振り分けられます。

エラー処理とデバッグ

Sentry 統合のデバッグ

Sentry が正しく動作しているかを確認するためのテストコードです。

typescript// src/lib/debug/sentry-test.ts
import * as Sentry from '@sentry/svelte';

// Sentry動作確認用の関数
export function testSentryIntegration() {
  try {
    // テストエラーの送出
    throw new Error('Test error from Svelte app');
  } catch (error) {
    // Sentryにエラーを手動送信
    Sentry.captureException(error);
    console.log('Test error sent to Sentry');
  }

  // ブレッドクラムのテスト(ユーザーアクション記録)
  Sentry.addBreadcrumb({
    category: 'test',
    message: 'Test breadcrumb added',
    level: 'info',
  });
}

開発環境でこの関数を実行し、Sentry ダッシュボードにエラーが表示されることを確認しましょう。

よくあるエラーと解決方法

統合時に遭遇する可能性のあるエラーと、その解決方法をまとめます。

エラー 1: CORS(Cross-Origin Resource Sharing)エラー

plaintextError: Access to fetch at 'https://otel-collector.com/v1/traces'
from origin 'http://localhost:5173' has been blocked by CORS policy

発生条件: OpenTelemetry Collector が CORS ヘッダーを適切に返していない場合に発生します。

解決方法:

  1. OpenTelemetry Collector の設定に CORS ヘッダーを追加します
yaml# otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      http:
        endpoint: 0.0.0.0:4318
        cors:
          allowed_origins:
            - 'http://localhost:5173'
            - 'https://your-production-domain.com'
          allowed_headers:
            - '*'
  1. または、プロキシを使用して CORS 問題を回避します
typescript// vite.config.ts
export default defineConfig({
  server: {
    proxy: {
      '/otel': {
        target: 'https://otel-collector.com',
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/otel/, ''),
      },
    },
  },
});

エラー 2: ソースマップアップロード失敗

plaintextError: Unable to upload source maps to Sentry
Error 401: Invalid authentication token

発生条件: Sentry の認証トークンが無効、または権限が不足している場合です。

解決方法:

  1. 有効な Auth トークンを生成します(Sentry → Settings → Auth Tokens)
  2. 環境変数に正しく設定します
bash# .env.local
SENTRY_AUTH_TOKEN=your_valid_token_here
  1. トークンに必要な権限(project:releasesorg:read)が付与されていることを確認します

エラー 3: Web Vitals データが送信されない

plaintextTypeError: Cannot read property 'sendBeacon' of undefined

発生条件: 古いブラウザや特定の環境でnavigator.sendBeaconが利用できない場合です。

解決方法: フォールバック実装を追加します

typescript// src/lib/vitals.ts(修正版)
function sendToAnalytics(metric: any) {
  const body = JSON.stringify(metric);

  // sendBeaconが利用可能か確認
  if (navigator.sendBeacon) {
    navigator.sendBeacon('/api/analytics/web-vitals', body);
  } else {
    // フォールバック: 通常のfetchを使用
    fetch('/api/analytics/web-vitals', {
      method: 'POST',
      body,
      headers: { 'Content-Type': 'application/json' },
      keepalive: true, // ページ遷移後も送信継続
    }).catch(console.error);
  }
}

パフォーマンス最適化

可観測性ツールの導入により、アプリケーションのパフォーマンスに影響を与える可能性があります。以下の最適化を実施しましょう。

遅延初期化

必須でない計装は、アプリケーション起動後に遅延して初期化します。

typescript// src/lib/observability.ts
// 遅延初期化による初期ロード時間の短縮
export async function initObservabilityLazy() {
  // ページ読み込み完了を待機
  if (document.readyState === 'complete') {
    await initializeTools();
  } else {
    window.addEventListener('load', initializeTools);
  }
}

async function initializeTools() {
  // 非同期でツールを初期化(並列実行)
  await Promise.all([
    import('./telemetry').then((m) =>
      m.initOpenTelemetry()
    ),
    import('./vitals').then((m) => m.initWebVitals()),
  ]);
}

この方法により、初期ページ読み込み時間への影響を最小化できますね。

サンプリングの調整

すべてのイベントを送信するのではなく、適切なサンプリング率を設定します。

typescript// src/lib/sentry.ts(最適化版)
import { init } from '@sentry/svelte';

init({
  dsn: 'YOUR_SENTRY_DSN',

  // 本番環境でのサンプリング設定
  tracesSampleRate: import.meta.env.PROD ? 0.1 : 1.0, // 本番10%、開発100%

  // エラーサンプリング(特定エラーを除外)
  beforeSend(event, hint) {
    // 開発環境のエラーは送信しない
    if (import.meta.env.DEV) {
      return null;
    }

    // 特定のエラーを除外(例:ネットワークエラー)
    if (
      event.exception?.values?.[0]?.value?.includes(
        'Network Error'
      )
    ) {
      return null;
    }

    return event;
  },
});

適切なサンプリング率により、コストとデータの有用性のバランスを取れるでしょう。

まとめ

Svelte アプリケーションにおける可観測性の実装は、ユーザー体験の向上と運用の効率化に不可欠です。本記事では、Sentry、OpenTelemetry、Web Vitals という 3 つの強力なツールを統合し、包括的な監視体制を構築する方法をご紹介しました。

Sentry によるエラートラッキングで、本番環境の問題を迅速に発見・解決できます。OpenTelemetry による分散トレーシングで、フロントエンドからバックエンドまでのリクエストフローを可視化できますね。Web Vitals によるパフォーマンス計測で、実際のユーザー体験を定量的に把握できます。

これらのツールを適切に組み合わせることで、以下の効果が期待できるでしょう。

#効果説明
1問題の早期発見ユーザーが気づく前にエラーを検知
2迅速なデバッグ詳細なトレース情報で原因特定が容易
3パフォーマンス改善データに基づいた最適化が可能
4ユーザー満足度向上安定した高速なアプリケーションを提供

可観測性の実装は一度で完了するものではなく、継続的な改善が重要です。収集したデータを定期的に分析し、アプリケーションの品質向上に活用していきましょう。

また、チーム全体で可観測性の重要性を共有し、新機能開発時にも計装を忘れずに実施することが成功の鍵となります。本記事で紹介した実装パターンを基盤として、プロジェクトの特性に合わせてカスタマイズし、最適な監視体制を構築してください。

関連リンク