こんちは、まだまだ暑いっすね
趣味で作ってるReact+Lambda+DynamoDBのアプリに、AWSでフロントエンドのモニタリングを入れてみたいと思ったので方法を共有します
バックエンドにX-Rayを入れたので、ついでにフロントエンドにも何か監視の仕組みを入れたいと思って調査を始めました。
わざわざ別のサービスに契約するのが面倒なので、AWSでモニタリングツール提供していたりモニタリングできる方法がないかなぁと探し始めましたが、いくつかありました。
- フロントエンドから自前でCloudWatchLogsにログを出力する
- AWS Amplifyのモニタリングを使う
- CloudWatch RUMを利用する
Amplifyは、モニタリング以外にも色々な機能が入っていて、不要な設定やサービスは入れたくないのでなし。時間もかけたくなかったのでCloudWatch RUMを利用することにしました。
X-Rayを利用したトレーシングがフロントエンドからバックエンドまで連携できるのでそこもあって選びました。
導入方法
AWS公式ドキュメントのCloudWatch RUM を使用するためにアプリケーションをセットアップするとフロントエンドにインストールするaws-rum-webというライブラリのドキュメントを参照するだけで簡単に終わりました。
フロントエンドに記述するセットアップコードはこんな感じです
const config: AwsRumConfig = {
sessionSampleRate: 1,
guestRoleArn: "arn:aws:iam::xxxxx:role/RUM-Monitor-ap-northeast-1-xxxxx-xxxxx-Unauth",
identityPoolId: "ap-northeast-1:xxxx-xxxx-xxxx-xxxx",
endpoint: "https://dataplane.rum.ap-northeast-1.amazonaws.com",
telemetries: [
"performance",
"errors",
"http"
],
allowCookies: true,
enableXRay: true
};
const APPLICATION_ID: string = 'xxxx-xxxx-xxxx-xxxx-xxxx';
const APPLICATION_VERSION: string = '1.0.0';
const APPLICATION_REGION: string = 'ap-northeast-1';
const awsRum: AwsRum = new AwsRum(
APPLICATION_ID,
APPLICATION_VERSION,
APPLICATION_REGION,
config
);
加えて、RUMを最大限有効に活用するためにはいくつか追加で設定すべきことがありました。
Reactの場合だと、jsで補足しきれないような非同期コードやイベントハンドラをキャッチできるように、componentDidCatch()
でエラーを記録させます。
Hooksを利用している場合だとcomponentDidCatch()
というAPIは存在しないので、react-error-boundaryの<ErrorBoundary/>
で代替するのが良いでしょう。
今回の場合はフォールバック目的でErrorBoundaryを利用しているわけではなく、ログ記録用として利用してるのでApp.tsxで定義しています
const logError = (error: Error, info: { componentStack: string }) => {
awsRum.recordError(error);
}
root.render(
<React.StrictMode>
<ColorModeScript />
<ErrorBoundary fallbackRender={({error, resetErrorBoundary}) => {
return <Center><Button onClick={() => resetErrorBoundary()}>もう一度読み込む</Button></Center>
}} onError={logError}>
<Admin />
</ErrorBoundary>
</React.StrictMode>,
)
また、react-router-dom
を利用してるならルーティング移動時にページ移動を記録しておくと良いでしょう(今回のアプリでは導入してないので利用してませんが)
また、このままではX-Rayの連携がLambdaと行われません。サービスマップで見ても別々の領域として表現されてしまいます
ここを連携させるには、telemetries
オプションのHTTPを以下のように修正する必要があります。
ただし、addXRayTraceIdHeader
をtrueにしてしまうとLambdaに関わらずどんなHTTPリクエストにもX-Amzn-Trace-Id
ヘッダーを付与してきます。
今回のアプリはLIFF(LINE Front-end Framework)を利用しているためLINEAPIに対してリクエストが飛びますが、このリクエストにもX-Amzn-Trace-Id
ヘッダーが付与され、CORSエラーが発生してしまいます。
そうならないようにurlsToInclude
でLambdaだけが対象になるように正規表現をかけなければなりませんでした。
telemetries: [
"performance",
"errors",
- "http",
+ ["http",{ recordAllRequests: true, addXRayTraceIdHeader: true, urlsToInclude: [/.*execute-api.ap-northeast-1.amazonaws.com/] }],
],
しかし、そうなると今度は逆に、サービスマップで表示されていた外部リクエストがLambda以外全て消えてしまいました。
これは、urlsToInclude
で指定する範囲が、トレースする範囲でもあると同時にX-Amzn-Trace-Id
ヘッダーを送る範囲でもあることが原因です。
本来は、トレースする範囲は全リクエストとし、X-Amzn-Trace-Id
ヘッダーを送る範囲はLambdaだけとしたいですが、現在のバージョンでは対応していません。
この問題はissueになっていて、現在もOpenのままです。issueの起票者いわく、自分でプラグインを作ることで挙動をカスタマイズして解決させたらしいです。
確かにコードを見ると、telemetries
オプションでhttp
を指定すると結局はFetchPlugin
とXhrPlugin
が注入されるだけなので、ここらへんやここらへんをコピペしてカスタマイズできそうです。
マネコンだとどうなるか
まず、サービスマップ上では、フロントエンドからLambda・DynamoDBまで一気通貫で各要素の関係性が表示されます。
これだけでなんとなく気分がいいですし、もしどこかでエラーが発生しても視覚的にわかりやすく表示してくれます。
また、RUMの画面に移動すると、ページのロード時間分布や発生したエラーの一覧・時間分布やセッション分布デバイス情報など様々な情報を閲覧することができます。
一般的なフロントエンドのモニタリングツールとしてSentryとかDataDogとか色々ありますけど、CloudWatch RUMはそれらが提供する情報量と同等ではないかなと個人的には思っています。(最近そういうツール触ってないので今は違うかもしれないですが)
バックエンドにはCloudWatchLogsやX-Rayを利用して監視していて、フロントエンドだけ別の監視ツールを使っている場合に、利用しているサービスが分散しなくて良いことは大きなメリットだと思います。
ただし、上記で挙げた問題のようにまだまだ改良の余地があるんだろうなと感じます。