SPAのURLパラメータ遷移をInstana EUMで正確に監視する方法

## はじめに

### 💡 よくある疑問
- 「モダンWebアプリケーション（SPA: Single Page Application）でURLパラメータ（`?id=123`や`?page=products`など）によるページ切り替えを行う場合、Instana は別ページとして認識してくれるのか？」

本記事では、URLパラメータで画面を切り替えるSPAを例に、この疑問を解説します。

## 1. なぜ Web パフォーマンス監視が重要なのか？

### 1-1. モダンWebアプリケーション（SPA）の特徴
- **モダンWebアプリケーション**とは、従来のWebサイトと異なり、**サーバーからページを再読み込みせず**、JavaScript で画面を動的に差し替える仕組みです（SPA: Single Page Applicationとも呼ばれます）
- このようなアプリケーションでは、**URL にクエリパラメータ（`?id=123`や`?page=products`など）を付与して画面を切り替える** 手法がよく使われています

```plaintext
https://example.com/app?menuId=fms-111  → 商品リスト
https://example.com/app?menuId=fms-222  → 注文履歴
https://example.com/app?menuId=fms-333  → サポートページ
```

しかし、技術的には **「同じ HTML」** を使い続けており、**ツールから見ると「同じページ」** と扱われがちです。

### 1-2. ユーザー視点のパフォーマンス計測が不可欠
- **ユーザーは「商品リスト画面」「注文履歴画面」を明確に区別** して利用している  
- でも、単に同じ URL とみなされると **どの画面が遅いか分からない**  
- 実際に画面が切り替わる瞬間のロード時間やエラーを可視化できなければ、UX の問題を発見できない

```mermaid
graph TD
    User[ユーザー] --> SPA[SPA Webアプリケーション]
    SPA -- "?menuId=fms-111" --> Page1[商品リスト画面]
    SPA -- "?menuId=fms-222" --> Page2[注文履歴画面]
    SPA -- "?menuId=fms-333" --> Page3[サポートページ]
    
    subgraph "従来の監視ツール"
    Page1 -.-> SinglePage["「同じページ」として認識"]
    Page2 -.-> SinglePage
    Page3 -.-> SinglePage
    end
    
    subgraph "Instana EUMの適切な設定"
    Page1 --> Instana1["Menu-fms-111として記録"]
    Page2 --> Instana2["Menu-fms-222として記録"]
    Page3 --> Instana3["Menu-fms-333として記録"]
    end
    
    style SinglePage fill:#f99,stroke:#f66,stroke-width:2px
    style Instana1 fill:#9f9,stroke:#6f6,stroke-width:2px
    style Instana2 fill:#9f9,stroke:#6f6,stroke-width:2px
    style Instana3 fill:#9f9,stroke:#6f6,stroke-width:2px
```

**💡 Instana EUM（End-User Monitoring）** なら、**SPA での画面遷移を正しくトラッキングし、ページごとにパフォーマンスを測定できます。**

## 2. Instana EUM を使って URLパラメータごとのページを区別する

### 2-1. Instana の基本セットアップ
1. Instana 管理画面で新規サイトを作成 → 監視キー（`key`）と `reportingUrl` を取得  
2. 発行されたトラッキングスクリプトを **HTML の `<head>`** に挿入

```html
<script>
  (function(s,t,a,n){s[t]||(s[t]=a,n=s[a]=function(){n.q.push(arguments)},
  n.q=[],n.v=2,n.l=1*new Date)})(window,"InstanaEumObject","ineum");

  ineum('reportingUrl', 'INSTANA_AGENT_ENDPOINT');
  ineum('key', 'YOUR_INSTANA_KEY');
  ineum('trackSessions'); // セッション追跡を有効化
</script>
<script defer crossorigin="anonymous" src="https://eum.instana.io/1.7.3/eum.min.js"></script>
```

### 2-2. `menuId`ごとに異なるページとして集計する方法

```mermaid
sequenceDiagram
    participant User as ユーザー
    participant SPA as SPAアプリ
    participant Instana as Instana EUM
    
    User->>SPA: アクセス（初期表示）
    SPA->>Instana: ページ読み込み情報を送信
    
    User->>SPA: 商品リストをクリック<br>(?menuId=fms-111)
    
    alt 方法A：autoPageDetection
        SPA->>Instana: 通常のビーコン送信
        Note over SPA: 正規表現で<br>menuIdを抽出
        Instana-->>Instana: "Page: fms-111"<br>として記録
    else 方法B：手動で設定
        SPA->>SPA: getMenuId()で<br>menuIdを取得
        SPA->>Instana: ineum('page', 'Menu-' + menuId)
        SPA->>Instana: ineum('meta', 'menuId', menuId)
        Instana-->>Instana: "Menu-fms-111"<br>として記録
    end
    
    User->>SPA: 注文履歴をクリック<br>(?menuId=fms-222)
    SPA->>Instana: 同様の処理で別ページとして記録
```

#### 🛠️ 方法A：autoPageDetection + mappingRule（正規表現）
```javascript
ineum('autoPageDetection', {
  mappingRule: [[/.*menuId=([A-Za-z0-9-]+)/, 'Page: $1']]
});
```
- **Instana のページ自動検出機能**（`autoPageDetection`）に **`mappingRule`** を設定  
- 正規表現で `menuId=xxx` の部分を抽出し、Instana に「Page: xxx」として記録  
- **メリット**：コーディング不要（Instana UI で設定可能）  
- **デメリット**：正規表現の知識が必要、URL が変わると修正が必要、下記注意点の環境に限る

> **注意**: `autoPageDetection` + mappingRule(正規表現)は Apache HTTP Server の HTTPd センサー 1.2.12 以降でのみ利用可能です。オプション `autoPageDetection` は HTTPd センサー 1.2.19 と EUM センサー 1.0.5 から利用可能です。詳細は[こちらのQiita記事](https://qiita.com/iwashinat/items/d2a05186bca7cc627128)をご参照ください。本記事では手動設定方法に焦点を当てています。

#### 🛠️ 方法B：`ineum('page', ...)` で手動設定
```javascript
function getMenuId() {
  const params = new URLSearchParams(window.location.search);
  return params.get('menuId') || 'default';
}

// menuIdが変更されたら実行
const menuId = getMenuId();
ineum('page', 'Menu-' + menuId);

// 重要: menuIdをメタデータとして送信すると分析が便利
ineum('meta', 'menuId', menuId);
```
- **JavaScript で `menuId` を取得し、ページ名を手動で Instana に通知**  
- **メリット**：細かい制御ができる、正規表現不要、設定が簡単  
- **重要ポイント**: `ineum('meta', 'menuId', menuId)` でメタデータとしても送信すると、後述のAnalyze画面での分析が非常に便利になります

## 3. 実際に動作するサンプルコード

以下は最小限の要素だけを含んだシンプルなサンプルコードです。これだけで十分に動作確認ができます。

### 3-1. 基本の例（Instanaなし）

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>URLパラメータ Test</title>
</head>
<body>
    <h1>URLパラメータによるページ切り替えテスト</h1>
    <ul>
        <li><a href="index.html?menuId=fms-111">商品リスト (menuId=fms-111)</a></li>
        <li><a href="index.html?menuId=fms-222">注文履歴 (menuId=fms-222)</a></li>
        <li><a href="index.html?menuId=fms-333">サポートページ (menuId=fms-333)</a></li>
    </ul>

    <h2>現在のページ情報</h2>
    <p id="current-menu">Loading...</p>

    <script>
        function getMenuId() {
            const params = new URLSearchParams(window.location.search);
            return params.get('menuId') || 'default';
        }
        
        document.getElementById('current-menu').textContent = "現在のmenuId: " + getMenuId();
    </script>
</body>
</html>
```

### 3-2. Instana EUMで正しくページを区別するシンプルな実装

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>URLパラメータ Test (Instana対応)</title>
    
    <!-- Instana EUMトラッキングスクリプト -->
    <script>
        (function(s,t,a,n){s[t]||(s[t]=a,n=s[a]=function(){n.q.push(arguments)},
        n.q=[],n.v=2,n.l=1*new Date)})(window,"InstanaEumObject","ineum");

        ineum('reportingUrl', 'INSTANA_AGENT_ENDPOINT');
        ineum('key', 'YOUR_INSTANA_KEY');
        ineum('trackSessions');
    </script>
    <script defer crossorigin="anonymous" src="https://eum.instana.io/1.7.3/eum.min.js"></script>
</head>
<body>
    <h1>URLパラメータによるページ切り替えテスト</h1>
    <ul>
        <li><a href="index.html?menuId=fms-111">商品リスト (menuId=fms-111)</a></li>
        <li><a href="index.html?menuId=fms-222">注文履歴 (menuId=fms-222)</a></li>
        <li><a href="index.html?menuId=fms-333">サポートページ (menuId=fms-333)</a></li>
    </ul>

    <h2>現在のページ情報</h2>
    <p id="current-menu">Loading...</p>

    <script>
        function getMenuId() {
            const params = new URLSearchParams(window.location.search);
            return params.get('menuId') || 'default';
        }
        
        // menuIdを取得
        const menuId = getMenuId();
        
        // Instanaにページ情報を送信
        if (typeof ineum === 'function') {
            ineum('page', 'Menu-' + menuId);
            
            // メタデータとしても送信（分析時に便利）
            ineum('meta', 'menuId', menuId);
        }
        
        // ページ上にmenuIdを表示（4-1と同じように）
        document.getElementById('current-menu').textContent = "現在のmenuId: " + menuId;
    </script>
</body>
</html>
```

**このコードのポイント**:
* 3-1の例に、Instana EUMのコードを追加しただけのシンプルな実装です
* HTML部分はほぼ同一で、追加された主要な部分は：
  1. Instana EUMの初期化コード（head内）
  2. menuIdを取得した後、以下の2つの処理を追加：
     - `ineum('page', 'Menu-' + menuId)` でページ名を設定
     - `ineum('meta', 'menuId', menuId)` でメタデータも送信
* 元の機能（menuIdの表示）はそのままに、Instanaへの通知機能だけを追加しています

### 4-1. ローカルでの動作確認

1. 上記のいずれかのHTMLコードを `index.html` としてローカルに保存
2. Python の SimpleHTTPServer などでローカル起動
   ```sh
   python3 -m http.server 8080
   ```
3. ブラウザで `http://localhost:8080` にアクセス
4. メニューボタンをクリックしてSPAの動作を確認
![instana-spa-sample-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/99b01778-cd3a-487a-a896-7f6a29c34404.png)

5. InstanaのWebサイトおよびモバイル・アプリ画面でアクセス結果の確認


### 4-2. Instana Analyzeでの確認と詳細分析

1. Instanaの管理画面にログイン
2. Websites > あなたのサイト を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/d376a9be-e653-467c-a655-d10cb12479c8.png)
3. ページ・ロードの分析 を開く
4. 以下のようなフィルターで確認：
   - グループ: `Location -> URL ` でURLでフィルタリング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/282b01a0-9cb9-4f92-9778-5024b3013b05.png)

   - menuId別:  `Location -> Page Name ` などでフィルタリング
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/e20e4e3b-d213-4378-a89a-3052d21e364a.png)

   - **メタデータ: `Data Collection -> Meta -> menuId` でフィルタリング**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/8d0a5892-b835-47e7-99e1-6310e6d0c76c.png)


#### メタデータを使った詳細分析（重要）

「分析 > Webサイト / ページ・ロード」画面では、メタデータを使った強力な分析が可能です：

```mermaid
graph TB
    A[Instana Analyze画面] --> B[Filter byを選択]
    B --> C[Data Collection -> Meta menuidを選択]
    C --> D[equals fms-111と入力]
    D --> E[fms-111に関するデータのみを表示]
    
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/209170/42100982-b4b5-4160-acb4-dd539f61119e.png)

これにより、**特定の menuId に限定したパフォーマンスやエラーの発生状況**を簡単に把握できます。例えば：

- 「商品リスト画面（fms-111）だけロード時間が長い」
- 「注文履歴画面（fms-222）で特定のJavaScriptエラーが発生している」

といった問題をピンポイントで発見できます。

## 5. まとめ＆次のステップ

```mermaid
graph LR
    A[SPAアプリケーション] --> B[Instana EUMの導入]
    B --> C[menuIdごとの区別設定]
    C --> D["メタデータの活用"]
    D --> E[詳細な分析が可能に]

    B -.-> F[次のステップ]

    F --> G[ユーザーの特定]
    G --> D["メタデータの活用"]
    
    F --> J[Synthetic Monitoring]
    F --> K[Smart Alerts]
    
    style D fill:#fc9,stroke:#f96,stroke-width:2px
    style E fill:#9f9,stroke:#6f6,stroke-width:2px
```

1. **SPA でも URLパラメータごとにページを区別可能**  
   - `autoPageDetection + mappingRule` か `ineum('page', ...)`  
2. **`ineum('meta', 'menuId', menuId)` でメタデータを送信すると分析が格段に便利に**
   - 分析画面で `Data Collection -> Meta ` を使った詳細な絞り込みが可能

**💡 今回紹介した内容で、SPA の画面切り替えを正確にモニタリングできます。特にメタデータを活用することで、分析の精度と効率が大幅に向上します。**

### 次のステップ：さらに知識を深めたい方へ
- **ユーザー特定方法**：IP アドレスがマスクされる SaaS 環境でも、`ineum('user', ...)` や `ineum('meta', ...)` でユーザー特定＆グルーピングが可能。これによりユーザーごとの問題をスピーディに発見できます。
- **Synthetic Monitoring**：リアルユーザーが問題に遭遇する前に自動テストで検知する機能。定期的にテストを実行し、失敗時に通知を受けることで、ユーザーへの影響前に対応可能です。
- **Smart Alerts**：特定のURLパラメータページでエラー率が高まった時だけに通知する設定も可能です。

### 参考ドキュメント
- Web サイトのモニター - Instanaの製品ドキュメント
https://www.ibm.com/docs/ja/instana-observability/current?topic=instana-monitoring-websites

- JavaScript エージェント API  - Instanaの製品ドキュメント
https://www.ibm.com/docs/ja/instana-observability/current?topic=websites-javascript-agent-api