AWS AmplifyでCloudWatchLogsにカジュアルにログを送りたい


Amplify Logger というのだから、ただのログ出力じゃなくて CloudWatchLogsにいい感じインテグレーションしてくれるものと思っていたら全然そんなことはなかった。

で、issueを漁ってみたら同じことを考えている人がいて、AWSCloudWatchProvider を通してAmplify Loggerでログを出すだけで CloudWatchLogsにログを飛ばしてくれるようになるプルリクエストがあって、既にマージされていた。

https://github.com/aws-amplify/amplify-js/pull/8309

マージされてはいるものの、公式のドキュメントには何も記載は無かったのでプルリクエストのコードとかコメントを読みつつ試してみました。

手順など

ログ送信用のIAMロールを作る

amplify add auth でIAM関連のリソースを作成する。ログイン機能を付けない場合は認証情報に関する問いには雑に回答してOK。

ここで重要なのは add authコマンドの対話によって生成されるjson。

$  tree amplify 
amplify
├── backend
│   ├── amplify-meta.json
│   ├── auth
│   │   └── amplifyapp1122334455
│   │       ├── amplifyapp1122334455-cloudformation-template.yml
│   │       └── parameters.json   # これをいじる

この parameters.json の allowUnauthenticatedIdentities を true に書き換える。

{
    "identityPoolName": "amplifyapp1122334455_identitypool_00aabbcc",
    "allowUnauthenticatedIdentities": true, 
    "resourceNameTruncated": "amplifyapp1122334455",
    "userPoolName": "amplifyapp1122334455_userpool_00aabbcc",
    "autoVerifiedAttributes": [
        "email"
    ],

この変更をしてから amplify push を実施すると以下のようにフェデレーテッドアイデンティティが作成される。

認証機能を利用しない、ないし未ログイン状態のユーザーの操作でもログを送りたい場合はこのフラグが on になっている必要がある。

作成されたIAMロールに対してCloudWatchの操作を許可する

amplify add auth を実行すると XXXX-authRoleXXXX-unauthRole の2つのロールが作られる。認証していなくてもログを送信したいので、コンソールから unauthRole に以下のロールを付与する。
.amplify 配下に生成される parameters.json をいじればロールも一緒に登録できそうだけども、今回はマネジメントコンソールから登録した。

  • DescribeLogGroups
  • DescribeLogStreams
  • CreateLogGroup
  • CreateLogStream
  • PutLogEvents
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        }
    ]
}

このようにIAMポリシーとしてまとめておけば他のプロジェクトでも使い回せるので便利。

アプリケーションに Amplify Loggerを組み込む

プルリクのコメント欄にサンプルコードが記載されてたのでそれを参考に。
aws-exports.js を読み込んだ後にログ関連の設定を加えて `Amplify.configure`に渡す。

今回の場合、

ロググループ ログストリーム
app_logs develop_LOGS

のようになる。

import { Amplify, Logger, AWSCloudWatchProvider } from 'aws-amplify';
import awsconfig from './aws-exports';

Amplify.configure({
  Logging: {
    logGroupName: 'app-logs',
    logStreamName: `${process.env.VUE_APP_ENV}_LOGS`,
  },
  ...awsconfig
});

const amplifyLogger = new Logger('app-logs', "INFO");
Amplify.register(amplifyLogger);
amplifyLogger.addPluggable(new AWSCloudWatchProvider());

ログを送信する

ログを送信するには通常のAmplify Loggerと使い方は変わらず。

amplifyLogger.info("hello world!!");

これで送信されるはずなのだけど、こんなエラーになった。

Failed to load resource: the server responded with a status of 400 (Bad Request)
ConsoleLogger.js?7aaf:127 [ERROR] 31:13.887 AWSCloudWatch - failure during log push: InvalidParameterException: 1 validation error detected: Value '' at 'sequenceToken' failed to satisfy constraint: Member must have length greater than or equal to 1

sequenceToken がブランクなのでエラーになっている様子。

CloudWatchEventsにログを送信する場合は、sequenceTokenをパラメータに含めることでログの連続性を保つ仕組みになっているみたい。

この sequenceToken の解決自体は AWSCloudWatchProvider でやってくれているようなのだけど、最初のログ送信に失敗してしまう。

プルリクのコメントを見てみると同じ問題に遭遇した人がいて、曰く

After adding an event to the stream manually, the sequenceToken value was populated correctly and subsequent log calls worked without issue. 

ログストリームの中身が空なのが原因の様子。
この問題はCloudWatchProviderの不具合とのことでissueが登録されていましたが、先日closeされたようなので、最新のAmplifyを使っていれば問題ないのかなと。

このあとアプリケーションからログを出力してみると・・・!


(1行目の 「Hello!」ってログがマネジメントコンソールから登録したやつ)

まとめ

AmplifyからカジュアルにCloudWatchLogsにログを送ることができるようになるとアプリケーションの監視やデータ分析がかなり便利になると思う。

エラーの収集はもとより、ユーザーの操作履歴などを CloudWatchLogs => S3 に溜めておいてS3 SelectとかAthenaで分析するみたいな使い方ができると絶対楽しい。

JAWS Pankration 2021にて

先日開催されたJAWS Pankration 2021でこの話をしたので、よかったら見てみてください。。

https://jawspankration2021.jaws-ug.jp/sessions/2