😸

AWSを用いたフロントエンドモニタリング入門 ~Next.js編~

2024/05/07に公開

こんにちは。ウェルスナビでソフトウェアエンジニアをしています水馬です.

今回のテーマはフロントエンドモニタリングです。
みなさんフロントエンドのモニタリングは正しく行えているでしょうか?

各ユーザの端末内で生した事象を正しくモニタリングするためにはサーバサイドとは異なる知見が求められます。今回は、AWS上でフロントエンドアプリケーション(Next.js)を運用するにあたってのモニタリングの知見をお伝えしたいと思います!

フロントエンドの「何を」モニタリングするのか?

ひとえにモニタリングといっても目的やサービスの特性に応じて期待されるモニタリングの範囲と意味合いが異なることが一般的です。
本記事では以下の項目に焦点を当ててAWSを用いたモニタリング手法について解説しています。

  1. ユーザの端末内で発生したエラーを正しく検知できているか?
  2. レンダリングサーバで発生したエラーを正しく検知できているか?
  3. Web Core Vitals のようなユーザーエクスペリエンスに関する情報を取得できているか?

なお、今回はNext.jsで Server Side Rendering を用いたアプリケーションを前提に解説を行なっています。Next.jsというフレームワークがプリレンダリングの責務を担うという特性からSSRを行なっているレンダリングサーバもフロントエンドモニタリングの対象として扱っています。

モニタリング方法と使用したAWSアーキテクチャ

ここからは具体的なモニタリング手法と AWS アーキテクチャについて解説していきます。

レンダリングサーバのモニタリング

ウェルスナビではNext.jsのデプロイに Serverless Nextjs Plugin を用いています。Next.jsのトップディレクトリにserverless.yamlというyamlファイルを作成し、以下のような設定を加えてcliコマンドを発行するだけで簡単にSSRを用いたNext.jsをデプロイすることができます。Serverless Nextjs Plugin に関する詳細は本記事の趣旨からずれるため、詳しくは公式ドキュメントを参照してください。

# serverless.yml
# Next.jsのトップディレクトリにserverless.ymlを配置
myNextApplication:
  component: "@sls-next/serverless-component"

serverless コマンドを発行するだけで上記 yaml の設定に応じた環境がデプロイされます。

$ serverless

serverless コマンドを発行すると以下のようなアーキテクチャがデプロイされます。Next.jsのサーバサイドは Lambda@Edge で実行され、処理内容に応じて3つのLambda@Edgeが生成されます。アプリケーションの状態を正確に把握するには、これらすべてのLambda@Edgeに対してもモニタリングを行う必要があります。

Serverless Nextjs Plugin で生成された Lambda@Edge はそれぞれ固有の Cloudwatch Log Group を持ち、それぞれのロググループに Next.js のサーバサイド処理の中で生成されたログが出力されます。

これらのロググループに対してCloudwatch Logsのフィルタリング機能を用いることで、特定の文字列(ERRORException等)を検出することができます。フィルタリングされたログに対してCloudwatch Alarmを設定することで、エラーログの中に異常を検知したときに、AWS ChatBot を経由して Slack に通知を行う仕組みを構築することができます。この仕組みを用いることで、開発者はどこの処理でエラーが発生したかを即座に検知することができます。

ユーザの端末(ブラウザ)内で発生したエラーのモニタリング

ログの出力先としてはレンダリングサーバ同様 Amazon CloudWatch Logs を選択します。
また、フロントエンドから直接CloudWatch Logs にログを出力するためには、適切なIAMポリシーをフロントエンドアプリケーションに付与する必要があります。ブラウザで動作するアプリケーションに対して IAM ポリシーを付与するには Amazon Cognito Identity Pool を用います。Identity Pool に未認証ロールを紐づけることで、フロントエンドアプリケーションはAWSクレデンシャルを保持することなく、Cloudwatch Logs へのログ出力権限を持つことができます。

エラーの通知方法はNext.jsのサーバサイド同様、Cloudwatch Alarm と AWS ChatBot を用いたSlackへのリアルタイム通知を行います。

クライアントから直接 Cloudwatch Logs にデータを送信するには、Cognito Identity Pool に適切なポリシーを割り振ったIAMロールを指定します。以下はIAMポリシーの例です。ウェルスナビではロググループ名のprefixを/nextjs/*としているため以下のようなポリシーを生成しています。また、logs:PutLogEventsを実行できるリソースはloggerPrefixがついたログストリームに絞ることで、ユーザが任意のログストリームに出力ができないよう制御しています。 rum:PutRumEvents では 後述する Cloudwatch RUM にデータを転送する権限を付与しています。(※ ${AWSアカウントID}のところは適宜読み替えてください。)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "logs:PutLogEvents",
            "Resource": "arn:aws:logs:ap-northeast-1:${アカウント:log-group:/nextjs/*:log-stream:*",
            "Effect": "Allow"
        },
        {
            "Action": [
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:DescribeLogStreams"
            ],
            "Resource": "arn:aws:logs:ap-northeast-1:${AWSアカウントID}:log-group:/nextjs/*",
            "Effect": "Allow"
        },
        {
            "Action": "logs:DescribeLogGroups",
            "Resource": "arn:aws:logs:ap-northeast-1:${AWSアカウントID}:log-group:*",
            "Effect": "Allow"
        },
        {
            "Action": "rum:PutRumEvents",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

フロントエンドからログを出力する処理には Amplify Libraries というクライアントライブラリを用います。Amplify Libraries は AWSCloudWatchProvider というプラグインが用意されており、Cognito Identity Pool の Identity Id を指定することで、クライアントから直接指定したロググループにログを出力することができます。

1.ログ出力の設定処理

import Amplify, { Logger as AmplifyLogger, AWSCloudWatchProvider } from 'aws-amplify'

// ログ出力に必要な情報を設定する
Amplify.configure({
  aws_project_region: region,
  Logging: {
    logGroupName: "/nextjs/myapp", // 任意のロググループ名
    logStreamName: "myLogStream" // ログストリーム名
  },
  aws_cognito_region: "ap-northeast-1", // 使用するリージョン
  aws_cognito_identity_pool_id: "ap-northeast-1:xxxx..." // Cognito Identity Pool Id
})

2.ログ出力処理(try~catch文中などで使用)


try {
   // 何かしらのエラーが発生
} catch(e) {
     // ログのprefixとログレベルを指定
     const amplifyLogger = new AmplifyLogger("log-prefix", 'ERROR') 
     Amplify.register(amplifyLogger)
     logger.addPluggable(new AWSCloudWatchProvider())
     // 指定されたCloudwatch Logグループに「Something wrong!」という文字列を表示
     logger.error("Something wrong!") 
}


上記の手順で以下のようなログを出力することができます。

今回はサンプルのため、ログストリーム名にはmyLogGroupという文字列を指定しましたが、実運用では 「企業ID」や「ユーザID」のようにログの分割単位として意味のある項目を指定することで、エラー時の影響範囲が把握しやすくなります。

ユーザーエクスペリエンスのモニタリング

ユーザーエクスペリエンスに関する情報の取得には Amazon CloudWatch RUMを用います。このサービスは2021年の re:Invent で発表された比較的新しいサービスです。CloudWatch RUM から提供されるコードスニペットをアプリケーションに組み込むだけで、「サイトの健全性に関する情報」「エラー情報」「ユーザの属性情報やページ遷移情報」 といった情報を取得することができます。

サイトの健全性に関する情報 (Web Core Vitals)

Cloudwatch RUM を用いることでCore Web Vitalsの各指標をモニタリングすることができます。Core Web Vitalsは以下のような指標で構成されています。

  • Largest Contentful Paint (最大視覚コンテンツの表示時間、LCP)
  • First Input Delay (初回入力までの遅延時間、FID)
  • Cumulative Layout Shift (累積レイアウト シフト数、CLS)

これらの情報はユーザ体験が適切に保たれているかの重要な指標となります。各指標には「Positive(問題なし)」「Tolerable(許容できる)」「Frustrating(イライラさせる)」の3つの指標があり、どの程度のユーザがこれらの指標に当てはまるかを可視化、分析することができます。

また、「Page load steps over time」 の項目ではユーザの画面表示までに時間を要している箇所を特定するのに有効なビジュアルを提供します。

ハンドリングできなかったエラー情報

前項ではAmplify Librariesを用いたエラーログの出力方法とそのモニタリング方法について述べました。しかし、実装の考慮不足などでtry catch文等で適切にハンドリングできなかったエラーが発生する場合もあります。(jsエラーが発生してブラウザが一切操作できなくなってしまった・・といった経験をされた方も多いともいます)。Cloudwatch RUM では、このようなブラウザでハンドリングできなかったエラー処理をモニタリングすることができます。

以下の例では、単位時間当たりのエラー数をグラフで表示しています。

これらの具体的なエラー内容はCloudwatch RUMのコンソール画面やCloudwatch Logsから確認することができます。ウェルスナビでは、これらの情報もAWS Chatbotを用いてSlackに通知することで、アプリケーション内でハンドリングできなかったエラー情報についても取りこぼすことなく検知することができるようになりました。

ユーザの属性情報やページ遷移情報

ユーザの利用端末やどのブラウザを使用しているかといったユーザ属性を収集することができます。

また、ユーザがどういったページ遷移を行っているかを確認することもできます。これらの情報からサービス改善のための洞察を得ることもできます。

まとめ

今回は一般的に難しいとされているフロントエンドのモニタリング手法についてウェルスナビの取り組みを紹介しました。皆様のフロントエンドモニタリングの参考になれば幸いです!

--

ウェルスナビではエンジニアを積極採用中です!
https://corp.wealthnavi.com/recruit

筆者プロフィール:

水馬拓也(みずま たくや)
2022年2月ウェルスナビにフルスタックエンジニアとして入社。

前職はAmazon Web Service Japanでソリューションアーキテクトとして働いていました。

好きな技術はフロントエンド、AWSです。

WealthNavi Engineering Blog

Discussion