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



[**Amplify Logger** ](https://docs.amplify.aws/lib/utilities/logger/q/platform/js/)というのだから、ただのログ出力じゃなくて CloudWatchLogsにいい感じインテグレーションしてくれるものと思っていたら全然そんなことはなかった。

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

[https://github.com/aws-amplify/amplify-js/pull/8309](https://github.com/aws-amplify/amplify-js/pull/8309)


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


## 手順など

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


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

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

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

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

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

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

![編集_amplifysandbox00e9db2e_identitypool_00e9db2e__dev_-_Amazon_Cognito_-_アマゾン_ウェブ_サービス.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8607/28e81a99-676b-eb03-f83a-5ad78f10df82.png)


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


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

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


- ` DescribeLogGroups`
- `DescribeLogStreams`
-  `CreateLogGroup`
- `CreateLogStream`
- `PutLogEvents`


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

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

![IAM_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8607/187af64a-33b3-e985-f143-b3ee53420549.png)


![IAM_Management_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8607/0dc5e142-8acc-dbbf-19be-bd1d8a7dcaf7.png)




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

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

今回の場合、

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

のようになる。

```javascript
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と使い方は変わらず。

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

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

```javascript
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を使っていれば問題ないのかなと。

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


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

![スクリーンショット_2021_10_29_7_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/8607/ff22aca6-3e56-ee82-bfae-157cf05381eb.png)

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


## まとめ

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

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


### JAWS Pankration 2021にて

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

[https://jawspankration2021.jaws-ug.jp/sessions/2](https://jawspankration2021.jaws-ug.jp/sessions/2)


<iframe class="speakerdeck-iframe" style="border: 0px none; background: rgba(0, 0, 0, 0.1) none repeat scroll 0% 0% padding-box; margin: 0px; padding: 0px; border-radius: 6px; box-shadow: rgba(0, 0, 0, 0.2) 0px 5px 40px; width: 560px; height: 314px;" src="https://speakerdeck.com/player/fa9d880edf1842c792d7ec103e7eab37" title="AWS Amplify で翻訳アプリを作った話とAmplifyからカジュアルにCloudWatchLogsにログを送る方法について" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true" data-ratio="1.78343949044586" frameborder="0"></iframe>