Pick up

Amplify + Reactでフロントエンドのモニタリング設定

kohachan

はじめに

今回はReactで作成したSPAアプリからCloudWatch RUM, CloudWatch Logsへデータやログを送信し、フロントエンドのモニタリングをする方法を紹介したいと思います。

環境にはAmplifyを利用するので、その想定で参考にしていただければと思います。

Reactプロジェクトの作成

今回はViteを使ってReactのプロジェクトを作成します。
以下のコマンドでプロジェクトを作成しておきましょう。

npm create vite@latest amplify-frontend-monitoring --template react-ts

Amplifyのセットアップ

次に、Amplify周りの設定をしていきます。

Amplify CLIの設定

まずはAmplify CLIの設定を行います。

まだAmplify CLIのインストールが完了していない場合は、npmでグローバルインストールしておきましょう。

npm i -g amplify

以下のコマンドを実行し、コマンドラインに表示されるインストラクションに沿って、Amplify CLIに使用するIAMユーザーの作成などの設定を進めていきます。

amplify configure

必要な設定は以下のドキュメントからも参照できるので、これらを参考に設定してみてください。
AdministratorAccess-Amplifyのマネージドポリシーを持ったユーザーが作成できていればOKです。
https://docs.amplify.aws/gen1/javascript/tools/cli/start/set-up-cli/#configure-the-amplify-cli

以上でAmplify CLIの設定は完了です。

プロジェクトにAmplifyの初期設定をする

次にプロジェクトに対してAmplifyの初期設定を行います。
プロジェクトのディレクトリに移動し、amplify initを実行します。

cd amplify-frontend-monitoring
amplify init

注意点としては、Initialize the project with the above configuration?で自動的に設定を作ってくれようとしますが、こちらはnを選択して、手動で設定するようにしてください。

以下の記事にも記載されていますが、デフォルトのビルドコマンドとスタートコマンドはViteで作られるコマンドとは異なっているので、合わせてあげる必要があります。
https://dev.to/ethanlloyd21/react-typescript-vite-amplify-setup-052023-25ge

そのため、Viteの設定に合うよう、手動でそれぞれ以下のように入力します。

? Build Command:  npm run build
? Start Command: npm run dev

最後まで入力が終わると自動的にデプロイが走るので、成功のメッセージが出ればOKです。

Deployment state saved successfully.
✔ Initialized provider successfully.
✅ Initialized your environment successfully.
✅ Your project has been successfully initialized and connected to the cloud!

デプロイの設定

ローカル環境のセットアップに加えて、デプロイの設定まで一緒にやってしまいます。
AWSのマネージドコンソールからAmplifyの画面に遷移します。
上述のセットアップが済んでいれば、以下のようにアプリの設定が作られているはずです。

こちらの画面から、「使用を開始」を選択して設定を進めていきます。

ソースコードプロバイダーはお好みで良いですが、今回はGitHubを選択します。

次へをクリックすると自動的にGitHubの認証が走り、リポジトリが選択できるようになります。
まだGitHub上のリポジトリへプッシュが済んでいない場合は、ローカルからプッシュした上で対象のリポジトリを選択しましょう。

また、今回ブランチはmainを選択します。

次にアプリケーションの設定をしていきます。

「アプリケーションの名前」〜「ビルドの設定」までは、変更の必要はありません。

「このブランチで使用するバックエンド環境を選択」では今回はdevを選択しておきます。
また、サービスロールにはAdministratorAccess-Amplifyの権限を持ったIAMロールを選択しましょう。
まだ作成していなければ、「新しいサービスロールを作成」より作成してから選択します。

「ビルドイメージ」以降も特に変更は必要ないので、以上の設定で保存します。
スクリーンショット 2024-08-15 18.05.53.png (271.2 kB)

設定が完了すると自動的にデプロイが走るので、しばらく待ってデプロイ済みのステータスに変わったら成功です!

以上でAmplifyのセットアップは完了になります!

CloudWatch RUMの導入

CloudWatch RUMの設定

まず、CloudWatch RUMの設定を行なっていきますが、手順としてはシンプルです。

Amazon CloudWatch RUM のページを開き、「アプリケーションモニターを追加」をクリックします。

サイト名には任意のプロジェクト名を入力し、ドメイン名には先ほどAmplifyでデプロイした先のドメインを指定します。
それ以外はデフォルトの設定のままで、モニター設定を作成します。

1分ほど待つとコードスニペットと設定手順が表示されるので、この手順通りにローカル環境で設定していきましょう。

まず、aws-rum-webをnpmでインストールします。

npm install aws-rum-web

次に、生成されたコードスニペットをアプリケーションに組み込んでいきます。
全てのページで実行される場所に組み込む必要があるので、src/App.tsxの先頭にコードを貼り付けます。

ただし、Viteのデフォルト環境ではLinterのルールでそのままのコードだとビルド時にエラーを吐いてしまうので、使用されていない変数は少し整理しました。

その上で変更したsrc/App.tsxの内容が以下になります。

import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

import { AwsRum, AwsRumConfig } from "aws-rum-web";

try {
  const config: AwsRumConfig = {
    sessionSampleRate: 1,
    identityPoolId: "ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx",
    endpoint: "https://dataplane.rum.ap-northeast-1.amazonaws.com",
    telemetries: ["performance", "errors", "http"],
    allowCookies: true,
    enableXRay: false,
  };

  const APPLICATION_ID: string = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
  const APPLICATION_VERSION: string = "1.0.0";
  const APPLICATION_REGION: string = "ap-northeast-1";

  // 変数に代入しないよう修正
  new AwsRum(
    APPLICATION_ID,
    APPLICATION_VERSION,
    APPLICATION_REGION,
    config
  );
} catch (error) {
  // エラーを出力するよう修正
  console.error(error);
}

function App() {
  const [count, setCount] = useState(0);

  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">
        Click on the Vite and React logos to learn more
      </p>
    </>
  );
}

export default App;

APPLICATION_IDなどはマスクしていますが、コードスニペットには実際の値が書き出されていると思うのでそのまま貼り付けてください。

また、今回の例では説明を簡易にするため、APPLICATION_IDなどはハードコーディングする前提で書いていますが、実際にアプリケーションに組み込む際は環境変数などに切り出し、外部から取得するようにしておきましょう。

Amplify環境へのデプロイ

次に、Amplifyで動いている本番環境にデプロイしていきます。
ローカル環境からmainブランチにコミットし、リモートにプッシュします。

git add .
git commit -m 'CloudWatch RUMを導入'
git push origin main

リモートにプッシュすると、自動的にAmplifyのデプロイが走ります。
しばらく待って、デプロイに成功することを確認します。

次に、AmplifyでデプロイしているサイトのURLにアクセスします。
その後、AWSのマネジメントコンソールからCloudWatch RUMの画面を見ると、モニタリングの結果が表示されていることが確認できます!

ちなみにエラーを意図的に投げるコードを実行してみると、エラーもしっかり記録されていることが確認できました。

以上でCloudWatch RUMの確認は完了です!

CloudWatch Logsへのログ書き込み

次に、CloudWatch Logsへのログの書き込みを試してみたいと思います。

今回はAmplifyを使っているので、Amplifyのライブラリを使ってログ設定を行なっていきます。

ログ送信用IAMロールの作成

認証基盤の設定

CloudWatch Logsにログを送信する権限を持ったIAMロールを作成します。
そのために、まずは認証基盤をセットアップします。

amplify add auth

すると順に質問事項が出てくるので、以下の部分のみデフォルトの選択肢から変更して設定するようにしてください。(それ以外はデフォルトのままでOKです)

Do you want to use the default authentication and security configuration? Manual configuration
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) Yes
Do you want to enable 3rd party authentication providers in your identity pool? No
Do you want to add User Pool Groups? No
Do you want to add an admin queries API? No
Do you want to use an OAuth flow? No
Do you want to configure Lambda Triggers for Cognito? No

Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM)の質問でYesにしていることで、未認証のユーザーにもIAM権限を渡せるようにしています。
これにより、未認証のユーザーからの操作でもCloudWatch Logsにログを書きこむことが可能になります。

CloudWatch Logsへの書き込み権限を付与する

amplify initするとAuth RoleとUnauth Roleの2つのIAMロールが作成されます。

このロールは、それぞれ認証済みのユーザー、未認証のユーザーにAWSのサービスを直接呼び出す権限を与えるために利用されます。

現状Amplify CLIではS3のバックエンド構築(S3の作成、S3にアクセスするための権限をAuth Role、Unauth Roleに付与するなど)のコマンドはありますが、CloudWatch Logsへのログ書き込み設定ができるようなコマンドは用意されていないため、手動で権限を設定する必要があります。

今回はAWSマネジメントコンソールから、IAMポリシーを修正していきます。
対象のIAMロールは以下の形式の名前になっているので、マネジメントコンソールから探して修正していきます。

  • amplify-PROJECT-NAME-ENV-ID-authRole
  • amplify-PROJECT-NAME-ENV-ID-unauthRole

以下の権限を持ったポリシーを作成し、どちらのロールにも作成したポリシーをアタッチします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowCloudWatchLogAccess",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": [
                "arn:aws:logs:ap-northeast-1:ACCOUNT_ID:log-group:*"
            ]
        }
    ]
}

ロガーの設定

いよいよCloudWatch Logsへのログ書き込みのコードを書いていきます。

aws-amplifyが必要になるので、ライブラリをインストールしておきましょう。

注意点として、こちらのIssueに記載の通り、Amplify v6では現時点でCloudWatchにログを送信するためのProviderは用意されていないようなので、v5を指定してインストールします。

npm i aws-amplify@5.3.12

また、CloudWatch Logsへの書き込みはこちらのPRの通り、AmplifyのLoggerに対してAWSCloudWatchProviderを使うことで簡単に設定できます。

ファイル名は何でも構いませんが、ここでは試しにutils/logger.tsを作成し、以下のコードを追加します。

import { AWSCloudWatchProvider, Amplify, Logger } from "aws-amplify";
import awsExports from "../aws-exports";

Amplify.configure({
  Logging: {
    logGroupName: "frontendmonitoring",
    logStreamName: "test",
  },
  aws_cognito_region: "ap-northeast-1",
  aws_cognito_identity_pool_id: ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx,
  ...awsExports,
});

const LOG_LEVEL = "INFO";

const logger = new Logger("MyLogger", LOG_LEVEL);
Amplify.register(logger);
logger.addPluggable(new AWSCloudWatchProvider());

export default logger;

また、このままだとaws-exports.jsのインポートができないエラーで怒られるので、以下の内容のsrc/aws-exports.d.tsも作成します。

declare const awsConfig: {
  // aws-exports.jsで定義されているプロパティの型定義
  aws_project_region: string;
};

export default awsConfig;

それから、以下のようにsrc/App.tsxに先ほどのlogger.tsをインポートし、ログを仕込んでみます。

import logger from "./utils/logger";
logger.info("App started");

さらにハマりどころですが、Viteのプロジェクトの場合、開発用サーバーを立ち上げた際にReferenceError: global is not definedのエラーが出てしまいます。
これは、Viteの開発用サーバーではglobalが定義されていないことによって発生するもののようです。

こちらのIssueを参考にvite.config.tsに設定を追加します。
vite.config.tsは最終的に以下のような内容になります。

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react-swc";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  ...(process.env.NODE_ENV === "development"
    ? {
        define: {
          global: {},
        },
      }
    : {}),
  resolve: {
    alias: {
      ...(process.env.NODE_ENV !== "development"
        ? {
            "./runtimeConfig": "./runtimeConfig.browser", //fix production build
          }
        : {}),
    },
  },
});

CloudWatch RUMのロールにもポリシーを追加

さらにもう一点ハマりどころがあり、CloudWatch RUMで作成したロールにもLogsへの書き込み権限を追加しなければいけませんでした。

そのため、amplify add authで作成したロールに付与したのと同じポリシーを、RUM用のロールにも付与してみます。

以上で設定は完了になるので、再度Gitでリモートのmainブランチに変更をプッシュします。

デプロイが成功したのち、サイトを確認してみるとコンソールにログが表示されているのが見れました。

CloudWatch Logsの方を見てみても、ログが出力されていることが確認できます!

おわりに

ReactからCloudWatch RUMとCloudWatch Logsに連携する方法を試してみました。
フロントエンドから分析に使えるデータを色々と収集することもできそうなので、有効な使い方を考えて今後活用していきたいと思いました。

AUTHOR
kohachan
kohachan
記事URLをコピーしました