概要
AWSでGoogle Analyticsのようなログ収集ができるCloudWatch RUM(Realtime User Monitor)を試してみる。
CDKで作成したいが、L2はまだ未対応の模様(2022/12/31)。L1で作成できるか試してみる。
ReactのSPAでのページ遷移時にイベントが送信されるところまで確認する。
ソースコード:CDK
ソースコード: client - rum
ソースコード: client - router
CDK構成
import * as core from 'aws-cdk-lib'
import * as s3 from 'aws-cdk-lib/aws-s3'
import * as cf from 'aws-cdk-lib/aws-cloudfront'
import * as iam from 'aws-cdk-lib/aws-iam'
import * as origins from 'aws-cdk-lib/aws-cloudfront-origins'
import * as cognito from '@aws-cdk/aws-cognito-identitypool-alpha';
import { Construct } from 'constructs'
import { basePath } from '../constants/paths'
import { aws_rum as rum } from 'aws-cdk-lib';
interface Props extends core.StackProps {
bucketName: string
identityName: string
defaultCachePolicyName: string
distributionName: string
projectNameTag: string
}
export class AWSCloudFrontStack extends core.Stack {
constructor(scope: Construct, id: string, props: Props) {
super(scope, id, props)
// CloudFront オリジン用のS3バケットを作成
const bucket = this.createS3(props.bucketName)
// CloudFront で設定する オリジンアクセスアイデンティティ を作成
const identity = this.createIdentity(bucket, props.identityName)
// S3バケットポリシーで、CloudFrontのオリジンアクセスアイデンティティを許可
this.createPolicy(bucket, identity)
// CloudFrontディストリビューションを作成
const distribution = this.createCloudFront(bucket, identity, props)
// 確認用にCloudFrontのURLに整形して出力
new core.CfnOutput(this, `${props.distributionName}-top-url`, {
value: `https://${distribution.distributionDomainName}/${basePath}`,
})
new core.CfnOutput(this, `${props.distributionName}-distribution-id`, {
value: `${distribution.distributionId}`,
})
+ this.createRUM(distribution.distributionDomainName, props.env!.account!, props.env!.region!);
core.Tags.of(this).add('Project', props.projectNameTag)
}
private createS3(bucketName: string) {
// s省略
}
private createIdentity(bucket: s3.Bucket, identityName: string) {
// 省略
}
private createPolicy(bucket: s3.Bucket, identity: cf.OriginAccessIdentity) {
// 省略
}
private createCloudFront(
bucket: s3.Bucket,
identity: cf.OriginAccessIdentity,
props: {
defaultCachePolicyName: string
distributionName: string
},
) {
// 省略
}
+ private createRUM(domain: string, accountId: string, region: string) {
+ const { identityPool, role } = this.createIdentityPool();
+ const cfnAppMonitor = new rum.CfnAppMonitor(this, 'MyCfnAppMonitor', {
+ domain,
+ name: `rum-for-croudront-${domain}`,
+ appMonitorConfiguration: {
+ identityPoolId: identityPool.identityPoolId,
+ guestRoleArn: role.roleArn,
+ // サンプルレート。デフォルトは0.1(10%)。
+ // 1にするとセッションの100%をモニタする。高くするほどコストがかかるので注意。
+ // 今回は動作確認のため1を設定
+ sessionSampleRate: 1,
+ },
+ });
+ // CognitoIdのIAMロールにRUMへの送信権限を付与
+ role.addToPrincipalPolicy(new iam.PolicyStatement({
+ effect: iam.Effect.ALLOW,
+ actions: ['rum:PutRumEvents'],
+ resources: [`arn:aws:rum:${region}:${accountId}:appmonitor/${cfnAppMonitor.name}`],
+ }));
+ return cfnAppMonitor
+ }
+ private createIdentityPool(): {
+ identityPool: cognito.IIdentityPool
+ role: iam.IRole,
+ } {
+ const identityPool = new cognito.IdentityPool(this, 'IdentityPool', {
+ allowUnauthenticatedIdentities: true,
+ });
+ return { identityPool, role: identityPool.unauthenticatedRole };
+ }
}
デプロイ
RUMがデプロイされたことをコンソールから確認できた。
アプリケーションに反映。
コンソールの「JavaScriptを表示」からスニペットを取得する。
reactを参考に設定。
import { AwsRum, AwsRumConfig } from 'aws-rum-web';
let cwr: AwsRum | null = null;
try {
const config: AwsRumConfig = {
sessionSampleRate: 1,
guestRoleArn: "arn:aws:iam::00000000:role/AWSCloudFrontStack-IdentityPoolUnauthenticatedRole-1U7PMD1IH60RX",
identityPoolId: "ap-northeast-1:00000000-0000-0000-0000-xxxxxx",
endpoint: "https://dataplane.rum.ap-northeast-1.amazonaws.com",
telemetries: [],
allowCookies: false,
enableXRay: false
};
const APPLICATION_ID = '00000000-0000-0000-0000-xxxxxxxxxx';
const APPLICATION_VERSION = '1.0.0';
const APPLICATION_REGION = 'ap-northeast-1';
const awsRum: AwsRum = new AwsRum(
APPLICATION_ID,
APPLICATION_VERSION,
APPLICATION_REGION,
config
);
cwr = awsRum
} catch (error) {
// Ignore errors thrown during CloudWatch RUM web client initialization
}
export const getRum = () => {
return cwr
}
const App: React.FC = () => {
+ const location = useLocation()
+ React.useEffect(() => {
+ if (import.meta.env.DEV) return
+ const cwr = getRum()
+ if (!cwr) return
+ cwr.recordPageView(location.pathname)
+ }, [location])
return (
<Routes>
<Route path="/admin" element={<AdminRoute />}>
<Route path="/admin/top" element={<Top />} />
</Route>
<Route path="/private" element={<PrivateRoute />}>
<Route path="/private/top" element={<Top />} />
</Route>
<Route element={<PublicRoute />}>
<Route path="/" element={<Redirect />} />
<Route path="/login" element={<Login />} />
<Route path="/test" element={<Test />} />
</Route>
</Routes>
)
}
デプロイ
クライアントの確認
コンソールの確認
反映までは少し時間がかかる。
イベントが送信されることを確認できた。
参考
AWS 公式 - CloudWatch RUM を使用する
新機能 – Amazon CloudWatch RUM をご紹介
One Observability Workshop - 新しい CloudWatch RUM アプリケーションを作成する
CloudWatch新機能増えすぎ問題。Synthetics? RUM? Evidently?? ... Next.jsでの設定例
One Observability Workshop をやってみた
アプリケーションのクライアント側のパフォーマンスをモニタリングする Amazon CloudWatch RUM の紹介
CloudWatch RUMでReactアプリのパフォーマンスやエラーのモニタリングを試してみた
CloudWatch RUM App Monitor with AWS CDK ... cfnCognito sample
cdk - CfnAppMonitor
AWS CDKでデプロイ時の値を使ってフロントエンドとバックエンドを一発でデプロイできるようになったので試してみる
AWS CDKでWebフロントエンドをデプロイする3つの方法
github - cdk-rfc - RFC L2 AppMonitor
github - cdk - L2 AppMonitor ... v1 sample