AmplifyでウェブフロントエンドのログをCloudWatchに送る
概要
最近検証する機会があったので、Amplifyが導入されたウェブフロントエンド(React)からAmazon CloudWatch Logsにログを送り、閲覧するまでの手順をまとめてみました。
動作確認したバージョン
- Amplify CLI -> @aws-amplify/cli@7.6.13
- Amplify Libraries -> aws-amplify@4.3.13
書かないこと、及び参考記事
- Amplifyの基礎
- AWS IAMの概要
- ウェブフロントエンドでのログの取り回しについて
- フロントエンドエンジニアなら知っておきたい、JavaScriptのログ収集方法まとめ
- Web VitalsとJavaScriptエラーの可視化 - フロントエンドにおけるObservabilityとは
- (自分も知見がないので、良いコンテンツがあったらコメントで教えていただけると嬉しいです!)
サンプルを下のGitHub Repositoryにあげました。
jaga810/amplify-logger-example: A sample repository for using Amplify Logger with CloudWatch Logs.
Amplify Logger とは
Amplify LoggerはAWS Amplifyの機能の一つです。
ドキュメントにはローカルでエラーログを出力する仕組みしか記述されていませんが、実はAmazon CloudWatch Logsにログを送ることが可能です。
手順
0. Bootstrap
まず、Reactのプロジェクトを作成し、Amplifyを初期化します。
npx create-react-app amplify-logger-test
cd amplify-logger-test
amplify init # すべてデフォルトの選択肢でOK
次に、ユーザーが認証する前と後のどちらでもログを送れることを確認するために、認証基盤をセットアップします。
amplify add auth
最初の選択肢でManual Configurationを選びます。その後は以下のようにセットアップします。デフォルトの選択肢を選ばないほうがよいものは、太字 で示しています。
プロンプトの受け答えの参考
Do you want to use the default authentication and security configuration? Manual configuration
Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables
per-user Storage features for images or other content, Analytics, and more)
Provide a friendly name for your resource that will be used to label this category in the project: amplifyloggertest18699a5818699a58
Enter a name for your identity pool. amplifyloggertest18699a58_identitypool_18699a58
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
Provide a name for your user pool: amplifyloggertest18699a58_userpool_18699a58
Warning: you will not be able to edit these selections.
How do you want users to be able to sign in? Username
Do you want to add User Pool Groups? No
Do you want to add an admin queries API? No
Multifactor authentication (MFA) user login options: OFF
Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
Specify an email verification subject: Your verification code
Specify an email verification message: Your verification code is {####}
Do you want to override the default password policy for this User Pool? No
Warning: you will not be able to edit these selections.
What attributes are required for signing up? Email
Specify the app's refresh token expiration period (in days): 30
Do you want to specify the user attributes this app can read and write? No
Do you want to enable any of the following capabilities?
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にしているところです。
未認証のユーザーがアプリを利用しているときにもログを送ることができるように、未認証ユーザーのログインを許可しています。一方で、未認証ユーザーのログは必要ない、ということであれば、許可しないほうが良いでしょう。
1. ウェブフロントエンドからCloudWatch Logsにログを送るための権限設定
$ amplify init
するとデフォルトで作られるリソースの中に、Auth Role と Unauth Role があります。この2つのIAMロールは、未認証ユーザーや認証済みユーザーへ、AWSのサービスを直接呼び出す権限を与えるために利用されます。
- Unauth Role : 未認証ユーザーへ権限を付与する
- Auth Role : 認証済みユーザーへ権限を付与する
たとえば$ amplify add storage
でS3バケットを追加した場合、S3のサービスAPIをウェブフロントエンドから直接呼び出して画像や動画をアップロード・閲覧することを許可できます。
この際、権限付与にはUnauthRoleやAuthRoleが用いられ、Amplify CLIはS3バケットに読み書きする権限をそれぞれのIAMロールに付与します。
現状Amplify CLIを使用してAuthRoleとUnauthRoleにCloudWatch Logsへ書き込む権限を与えることがでないため($ amplify add logger
のようなコマンドがない)、手動で権限を付与する必要があります。
本記事ではAmplfiyが作成したリソースをカスタマイズすることができる、Amplify Overrideを用いて権限を付与します。
amplify override project
作成されたoverride.ts
ファイルを開き、以下のように書き換えます。
import { AmplifyRootStackTemplate } from '@aws-amplify/cli-extensibility-helper';
export function override(resources: AmplifyRootStackTemplate) {
const accountId = 'Your Account ID' // 自身のAWSアカウントID
const region = 'us-east-1' // ログを送りたいリージョン
const loggerPrefix = 'amplify-logger' // ロググループのPrefix(後述)
const loggerPolicy =
{
'policyDocument': {
'Version': '2012-10-17',
'Statement': [
{
'Effect': 'Allow',
'Action': 'logs:PutLogEvents',
'Resource': `arn:aws:logs:${region}:${accountId}:log-group:/${loggerPrefix}/*:log-stream:*`,
},
{
'Effect': 'Allow',
'Action': [
'logs:CreateLogStream',
'logs:CreateLogGroup',
'logs:DescribeLogStreams',
],
'Resource': `arn:aws:logs:${region}:${accountId}:log-group:/${loggerPrefix}/*`
},
{
'Effect': 'Allow',
'Action': [
'logs:DescribeLogGroups',
],
'Resource': `arn:aws:logs:${region}:${accountId}:log-group:*`
}
]
},
'policyName': 'amplifyLoggerCWLogsPolicy'
}
resources.authRole.policies = [
loggerPolicy
]
resources.unauthRole.policies = [
loggerPolicy
]
}
記述内容をより理解するためのヒント
- アプリのユーザーが任意のログストリームにログを出力できないように、
logs:PutLogEvents
を実行できるリソースはloggerPrefix
がついたログストリームに絞られるようにしています -
logs:DescribeLogGroups
は任意のロググループを参照する必要があるため、log-group:*
としています - projectのoverrideを用いて任意のIAMポリシーを追加することができます。このとき、Storageカテゴリなど、他のカテゴリによって付与されたIAMポリシーは消えません
CloudWatch Logsではログストリームとロググループという単位でログを集積します。ドキュメントによれば
ログストリームは、同じソースを共有する一連のログイベントです。CloudWatch Logs でのログの各ソースで各ログストリームが構成されます。
ロググループは、保持、監視、アクセス制御について同じ設定を共有するログストリームのグループです。ロググループを定義して、各グループに入れるストリームを指定することができます。1 つのロググループに属することができるログストリームの数に制限はありません。
ログの設計方針は様々考えられますが、本記事ではCloudwatchLogsのロググループ設計には気をつけよう | DevelopersIOを参考として、以下のようにしました。
- ロググループ : アプリケーションの環境ごとにユニーク (ex. 本番環境のロググループと開発環境のロググループは分かれている)
- ログストリーム : クライアントごとにユニーク (ex. ユーザーAのログとユーザーBのログは異なるログストリームに格納される)
特にログストリームは1 秒、1 ログストリームあたり 5 リクエスト
の制限があるため(ドキュメント)、クライアントごとにユニークに分けたほうが良いと思います。
以上でバックエンドの設定が終わったため、変更をクラウド上に反映します。
amplify push -y
2. ウェブフロントエンドでログを送る設定
ReactアプリケーションからCloudWatch Logsに送る設定をしていきます。
まず、必要なライブラリをインストールしましょう。
npm install aws-amplify @aws-amplify/ui-react
次に、App.js
を開き、以下のように書き換えます。
import { Amplify, Logger, AWSCloudWatchProvider} from 'aws-amplify'
import { Authenticator } from '@aws-amplify/ui-react'
import '@aws-amplify/ui-react/styles.css'
import awsExports from './aws-exports'
const loggerPrefix = 'amplify-logger'
const appName = 'test-app'
const username = 'test-user' //実際に利用するときは、未認証ユーザーにはUUID、認証済みユーザーにはusernameなどの識別子を入れてログを取ると良さそう
Amplify.configure({
Logging: {
logGroupName: `/${loggerPrefix}/${appName}/${process.env.NODE_ENV}`,
logStreamName: username,
},
...awsExports,
})
const LOG_LEVEL = 'INFO' //どのレベルのログまでロギングするか
const logger = new Logger('TestLogger', LOG_LEVEL)
Amplify.register(logger)
logger.addPluggable(new AWSCloudWatchProvider())
export default function App() {
logger.error(username, 'test error')
logger.warn(username, 'test warn')
logger.info(username, 'test info')
logger.debug(username, 'test debug')
return (
<Authenticator>
{({ signOut, user }) => (
<main>
<h1>Hello {user.username}</h1>
<button onClick={signOut}>Sign out</button>
</main>
)}
</Authenticator>
);
}
記述内容をより理解するためのヒント
-
LOG_LEVEL
ではどのレベルのログまでログングするか指定することができます-
VERBOSE
<DEBUG
<INFO
<WARN
<ERROR
- デフォルトの値は
WARN
のため、指定しなければWARN
とERROR
レベルのログが出力されます - LOG_LEVELの詳しい指定方法について知りたい人はこちらのドキュメントをご参照下さい
-
- CloudWatch Logsの連携に必要な記述部分のソースは以下のPull Requestです
- Auth RoleとUnauth Roleへ与えた権限と、おなじPrefixをもつロググループを指定しています。
3. 動作確認
では、実際にアプリを動かしてログがCloudWatch Logsに出力されることを確かめてみましょう。
npm start
検証ツールを使うと、以下のようにログが出ていることが確認できます。
次に、CloudWatch Logsの画面を確認してみましょう
- ロググループの画面を開く
- フィルターに
/amplify-logger/
と打ち込み、でてきた/amplify-logger/test-app/development
ロググループをクリックします
- ログストリームの中から、
test-user
をクリックします
- フロントエンドから送ったログが格納されていることを確認します
同様の手順で、認証後にもログが送れることを確認できますので、是非試してみて下さい。
まとめ
Amplify Loggerを利用してCloudWatch Logsにログを送る方法をまとめてみました。現状、自前でAuth RoleとUnauth Roleのポリシーを追加しなければならないのが少し手間ですが、セットアップする際の参考になれば幸いです。
感想や疑問点あれば気軽にコメント下さい!
謝辞的な
Amplify Advent Calndar 2021のAWS AmplifyでCloudWatchLogsにカジュアルにログを送りたいをベースとして、いくつか補足を加えてみました。
執筆者の@jacoyutoriusさん、Amplify LoggerとCloudWatch Logsの連携を教えていただいた@enishさん、ありがとうございます!
Discussion