CloudWatch RUM に設定値を渡す仕組み
このページは、RUM の設定値がサーバーからブラウザへ渡り、aws-rum-web が初期化されるまでの流れをコードで説明する。実際の ID やアカウント情報はこのページに含めていない。
1. CDK が設定値をコンテナの環境変数に入れる
App Monitor ID と Identity Pool ID はデプロイ時に決まるため、CDK がリソースの参照をそのまま環境変数に渡す。イメージには値を焼き込まないので、同じイメージを別のアカウントやリージョンでも使える。
// cdk/lib/web-stack.ts (ECS Express Mode service)
primaryContainer: {
image: image.imageUri,
containerPort: 3000,
environment: [
{ name: 'RUM_APP_MONITOR_ID', value: appMonitor.attrId },
{ name: 'RUM_IDENTITY_POOL_ID', value: identityPool.ref },
{ name: 'RUM_REGION', value: this.region },
{ name: 'APP_VERSION', value: image.assetHash.slice(0, 12) },
],
},2. server function が環境変数を読む
createServerFn の処理は常にサーバーで実行される。process.env を読むコードはブラウザ向けのバンドルに入らない。
// src/lib/rum-config.ts
export const getRumConfig = createServerFn({ method: 'GET' }).handler(
(): RumConfig => ({
appMonitorId: process.env.RUM_APP_MONITOR_ID ?? '',
identityPoolId: process.env.RUM_IDENTITY_POOL_ID ?? '',
region: process.env.RUM_REGION ?? process.env.AWS_REGION ?? 'us-east-1',
appVersion: process.env.APP_VERSION ?? '0.0.0',
}),
)3. ルートの loader が SSR 時に値を HTML へ埋め込む
ルートの loader が server function を呼び、結果を SSR の出力に含める。ブラウザはハイドレーション時にこの値を受け取る。staleTime: Infinity により、画面遷移のたびに再取得することはない。
// src/routes/__root.tsx
export const Route = createRootRoute({
loader: () => getRumConfig(),
staleTime: Infinity,
component: RootComponent,
})
function RootComponent() {
const rumConfig = Route.useLoaderData()
return (
<RootDocument>
<RumInit config={rumConfig} />
...
</RootDocument>
)
}4. ブラウザだけで aws-rum-web を初期化する
aws-rum-web はインスタンス生成時に window と document を参照するので、SSR 中には実行できない。useEffect の中で動的に import することで、ブラウザでだけ評価する。window.__awsRum の確認で二重初期化を防ぎ、初期化に失敗してもアプリは止めない。
// src/components/RumInit.tsx
useEffect(() => {
if (!config.appMonitorId || !config.identityPoolId || window.__awsRum) return
let cancelled = false
import('aws-rum-web')
.then(({ AwsRum }) => {
if (cancelled || window.__awsRum) return
window.__awsRum = new AwsRum(config.appMonitorId, config.appVersion, config.region, {
sessionSampleRate: 1,
identityPoolId: config.identityPoolId,
endpoint: `https://dataplane.rum.${config.region}.amazonaws.com`,
telemetries: ['errors', 'performance', ['http', { recordAllRequests: true }]],
allowCookies: true,
enableXRay: false,
})
})
.catch((err) => console.warn('CloudWatch RUM init failed', err))
return () => {
cancelled = true
}
}, [config])5. ブラウザは Cognito の一時認証情報で送信する
identityPoolId だけを渡すと、aws-rum-web は Cognito の enhanced flow で未認証 ID の一時認証情報を取得し、PutRumEvents に SigV4 署名を付ける。ゲストロールが許可するのは、この App Monitor への PutRumEvents だけである。
// cdk/lib/web-stack.ts (Cognito guest role)
const guestRole = new iam.Role(this, 'RumGuestRole', {
assumedBy: new iam.WebIdentityPrincipal('cognito-identity.amazonaws.com', {
StringEquals: { 'cognito-identity.amazonaws.com:aud': identityPool.ref },
'ForAnyValue:StringLike': { 'cognito-identity.amazonaws.com:amr': 'unauthenticated' },
}),
})
guestRole.addToPolicy(
new iam.PolicyStatement({ actions: ['rum:PutRumEvents'], resources: [appMonitorArn] }),
)ブラウザに渡る値について
App Monitor ID、Identity Pool ID、リージョン、アプリのバージョンはブラウザが RUM にデータを送るために必要な値であり、ページの HTML に含まれる。これらはシークレットではない。送信できる操作はゲストロールの権限で PutRumEvents に限られる。