Nuxt3 に CloudWatch RUM を導入する

# 実際のユーザー体験モニタリングが必要な理由
「[Nearly 70% of consumers admit that page speed influences their likeliness to buy.（消費者の約 70% が、ページ速度が購買意欲に影響すると答えている)
](https://unbounce.com/page-speed-report)」と言われるようにエンドユーザー体験はWEBサービスにおいて最重要な要素の一つです。そもそも、サービスとはユーザーに提供するものであり、サービスの監視をする目的は、ユーザーの体験を損なわないようにすることです。
そして、ユーザーの体験を直接測定することで、クリティカルな問題に正確、早くに気がつくことができます。
加えて、この情報をもとに実際のユーザ行動を分析し、改善することでユーザーからの信頼の維持や、ユーザー体験を向上させることが可能となります。
そして、計測できていなければ改善に取り組むことも難しいため、ユーザーの視点に立った監視についても非常に重要となってきます。

# Amazon CloudWatch RUM（Real User Monitoring)
Amazon CloudWatch RUM は、このユーザー体験の特定、理解、および改善に役立つインサイトを提供するメトリクスの収集をサポートします。必要なのは、アプリケーションを登録し、各ページのヘッダーに JavaScript のスニペットを追加して、デプロイすることだけです。このスニペットは、ユーザーがアプリケーションの各ページにアクセスする際に実行され、統合と分析のためにデータを RUM に送信します。このツールは単独で、あるいは AWS X-Ray と組み合わせて使用できます。


# Nuxt3 への導入
CloudWatch RUM はアプリケーションモニターを作成するとコードスニペットを提供します。 
この CloudWatch RUM のコードスニペットをアプリケーションに挿入する事でRUMが自動的に情報を収集します。
Nuxt3 へ導入する場合、plugins を利用すると便利です。
例えば、plugins 以下に rum.ts を作成します。
```
import { AwsRum, type AwsRumConfig } from 'aws-rum-web';

export default defineNuxtPlugin((_nuxtApp) => {
  const runtimeConfig = useRuntimeConfig();
  const config: AwsRumConfig = {
    sessionSampleRate: 1,
    identityPoolId: runtimeConfig.public.rumIdentityPoolId,
    endpoint: runtimeConfig.public.rumEndpoint,
    telemetries: ["performance", "errors", "http"],
    allowCookies: true,
    enableXRay: true,
    sessionEventLimit: -1,
  };

  const APPLICATION_ID: string = runtimeConfig.public.rumApplicationId;
  const APPLICATION_VERSION: string = '1.0.0';
  const APPLICATION_REGION: string = runtimeConfig.public.rumApplicationRegion;

  const awsRum: AwsRum = new AwsRum(
    APPLICATION_ID,
    APPLICATION_VERSION,
    APPLICATION_REGION,
    config
  );

  return {
    provide: {
      awsRum: awsRum
    }
  };
});
```
このコードでは環境変数を利用して設定値を挿入しています。
そのため、nuxt.config.ts に下記を追加しました。
それぞれの値はコードスニペットからペーストしてください。
```
  runtimeConfig: {
    public: {
      rumIdentityPoolId: '',
      rumEndpoint: '',
      rumApplicationId: '',
      rumApplicationRegion: '',
    }
  },
```
また、Nuxt3 は独自のエラーハンドラーを行っています。そのため、エラー処理を追加しなければなりません。
plugins に error-handler.ts を追加します。
```
import Vue from 'vue'
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.config.errorHandler = (error, instance, info) => {
    console.error(error)
    useNuxtApp().$awsRum.recordError(error)
  }

  // Also possible
  nuxtApp.hook('vue:error', (error, instance, info) => {
    console.error(error)
    useNuxtApp().$awsRum.recordError(error)
  })
})
```

これで、CloudWatch RUM にユーザーの操作情報が連携されました。

# ユーザーIDを CloudWatch RUM に連携する
CloudWatch RUM はランダム文字列を使い、特定のセッション、ユーザーを識別します。
これは、クッキー「cwr_s」と「cwr_u」を利用します。しかし、この値の変更は推奨されていません。もしアプリケーション側のユーザーと情報を連携したい場合、属性値として設定することができます。
例えば、サインインしたタイミングで下記のようにuid(ユーザーID)を登録します。これにより、CloudWatch RUM へ送られた情報に UID が連携されます。
```
useNuxtApp().$awsRum.addSessionAttributes({ uid: data.user.uid});
```
例えば Javascript エラーイベントの場合、このようにUIDも確認できます。
![スクリーンショット 2025-03-16 16.40.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005574/d7eccb32-03e1-4e93-81f2-b7495f9f7217.png)
これにより、フロントで問題が起きたユーザーを特定できるようになり、迅速に対応が可能になります。

# X-Ray のトレースマップ で CloudWatch RUM のトレースとバックエンドのトレースを紐付ける
CloudWatch RUM は X-Rayと簡単に連携できます。
連携方法はコードスニペットの「enableXRay」を「true」にするだけです。
```
enableXRay: true,
```

正しく連携されるとこのようにX-Ray画面に「Client: Real User Monitoring」セグメントが表示されます。
![スクリーンショット 2025-03-16 16.54.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005574/a2b23ad6-7dd0-4b2d-bff2-c89c572dcb7b.png)

# バックエンドとトレースを連携する
CloudWatch RUM で X-Ray を有効にする場合、デフォルトではバックエンドとトレースマップが繋がりません。
もし、バックエンドと繋げたい場合、Http Request 時のヘッダーに トレースIDを指定する必要があります。
CloudWatch RUM でこれを行う場合、 `addXRayTraceIdHeader: true` を指定することで実現だけです。


```
telemetries: ["performance", "errors", ['http', { addXRayTraceIdHeader: true, recordAllRequests: true, }]],
```

https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/monitoring/CloudWatch-RUM-modify-snippet.html

# Nuxt3 で `addXRayTraceIdHeader: true` が動作しない？
Nuxt3 で `addXRayTraceIdHeader: true` を指定しても、実際のHttp Request のヘッダーに反映されない問題が起きていました。
調査した結果、実際にヘッダーにトレースを付与するロジックに問題がありました。
そのため、GitHub Issue と PR を作成しています。

https://github.com/aws-observability/aws-rum-web/issues/615

https://github.com/aws-observability/aws-rum-web/pull/616

現状、修正は行われていないため、私の場合は `yarn patch` を利用し、PRの修正内容のパッチを適用しました。

https://yarnpkg.com/cli/patch

もし、Nuxt3 で CloudWatch RUM の X-Ray トレースとバックエンドのトレースを紐付けたい場合はご注意ください。

![スクリーンショット 2025-03-21 15.45.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1005574/375dc4c3-d2fe-4e62-b261-b15c4418f2dd.png)



# まとめ
この記事では Nuxt3 で CloudWatch RUM を導入する方法を説明しました。フロントエンドの監視は忘れがちですが、サーバーサイドと同等に重要です。CloudWatch RUMは簡単にフロントエンドの監視ができるため、ぜひ試してみてください