Amplify Logger から CloudWatchLogsに送信する の補足、或いはAmplifyで未ログイン状態でもAWSリソースを操作したいときの覚書

Amplifyを組み込んだアプリケーションからCloudWatchLogsにログイベントを送信するには amplify add auth で認証関連の機能を組み込んでログ送信用のIAMロールを作る必要があると書いた。

jacoyutorius.hatenablog.com

amplify add authを実行すると対話的に認証設定ができるのだけど、デフォルトの設定だけでは未ログインのときにはAWS側の操作ができない状態になるので このコマンドによって生成されたamplify ディレクトリ配下のファイルを一箇所書き換えてあげる必要がある。

認証機能をつけない場合はだいたい適当に答える感じでOK。

ddd4143c63e0:/workspaces/amplify-dev# amplify add auth
Using service: Cognito, provided by: awscloudformation
 
 The current configured provider is Amazon Cognito. 
 
 Do you want to use the default authentication and security configuration? Default configuration
 Warning: you will not be able to edit these selections. 
 How do you want users to be able to sign in? Email
 Do you want to configure advanced settings? No, I am done.
Successfully added auth resource amplifyapp locally

Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
$  tree amplify 
amplify
├── backend
│   ├── amplify-meta.json
│   ├── auth
│   │   └── amplifyapp1122334455
│   │       ├── amplifyapp1122334455-cloudformation-template.yml
│   │       └── parameters.json   # これをいじる

この parameters.jsonallowUnauthenticatedIdentitiestrue に書き換える。

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

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

f:id:jacoyutorius:20211104054355j:plain
認証されていないIDのところ

これでログインしていなくてもAWS側のリソースを扱うことができるようになる。

もしAuth関連のリソースが作成済の場合は amplify update auth を実行し、Walkthrough all the auth configurations を選んで

Allow unauthenticated logins? (Provides scoped down permissions that you can control via AW S IAM)Yes にする。

その他

どのファイルで設定しているのかわからなくてdiffで頑張って調べました。

f:id:jacoyutorius:20211104055714j:plain

Amplify Logger から CloudWatchLogsに送信する

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

通常の使い方は前回の記事を見てみてください。

jacoyutorius.hatenablog.com

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

github.com

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

手順など

その前に余談

Amplify で Storage とか Geo を追加するときに、一緒に Auth関連のリソースも作らされるの、何でなんだろう?って思ってたんだけど、AmplifyのアプリケーションからAWSのリソースを操作するためのロールが必要だから一緒に作られるんですね。 なので、ログイン機能とかが不要な場合は後述する unauthRole だけ見ておけばいい。

(多分必要)ログ送信用のIAMロールを作る

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": "*"
        }
    ]
}

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

プルリクのコメント欄にサンプルコードが記載されてたのでそれを参考に。

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());

ログを送信する

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. 

ログストリームの中身が空なのが良くないみたい。

ということで、マネジメントコンソールから適当なログイベントを一個登録してみた。

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

f:id:jacoyutorius:20211029074929p:plain
1行目の 「Hello!」ってログがマネジメントコンソールから登録したやつ

ここまで長かった。。。

まとめ

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

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

(ユーザーの行動分析は amplify analytics があるけど、あれは Amazon Pinpointとのインテグレーションなので、CloudWatchにログを飛ばしてざっくり検証してみたいな〜みたいのとは使い所が異なるという認識です)

Amplify Loggerの使い方について

AWS Amplifyの utilitiesに loggerがある。

https://docs.amplify.aws/lib/utilities/logger/q/platform/js/

使い方

import { Logger } from 'aws-amplify';

const logger = new Logger("my-logger");
logger.warn("warning message");

デバッグコンソールにはこのように出力される。

[WARN] 56:32.69 my-logger - warning message

注意点としては、インスタンスに指定されているレベル以上のログしか出力されないということ。 ログレベルは上から順に以下のようになっている。

  • ERROR
  • WARN
  • INFO
  • DEBUG
  • VERBOSE

デフォルトのログレベルはWARN。 例えば、以下のようなloggerがある場合、WARNERRORのログでないと出力されないので logger.info を実行してもログは出力れない。

import { Logger } from 'aws-amplify';

const logger = new Logger("my-logger");
logger.info("this is info message");  // これはconsoleに出力されない

INFOレベルのログを出したい場合は以下のようにインスタンス生成時にログレベルを指定しておく必要がある。

import { Logger } from 'aws-amplify';

const logger = new Logger("my-logger", "INFO");
logger.info("this is info message");  // これならconsoleに出力される
続きを読む

AWSアクセスキーのコミットされたリポジトリが公開されているのを検知すると『AWSCompromisedKeyQuarantineV2』というロールが付与される

先日誤ってアクセスキーがコード中にベタ書き状態のコミットがあるのを忘れていて(修正したコミットが最新だったのよ。。) Githubにpushしたところ、すぐさまAWSGithubから注意メールが届きまして。

で、 AWSのCloudTrailで不正な操作がないか調べていたところ、そのIAMユーザーにポリシーを付与するという身に覚えのない操作をしている履歴があり。

続きを読む

Rubyで動的クラスを使ったクラス設計を考える

地元鉄道の時刻表や駅の情報を返すAPIを考えている。 (Alexaのスキルを作りたくて、そのためのデータを返すAPIがほしいのだった)

欲しいのは、駅の情報(info)・時刻表(timetable)・運賃(fare)。

Akaden というのは地元鉄道の愛称。正式名称は遠州鉄道、略して遠鉄。車体が赤いので市民は「赤電」と呼んでいる。

駅名をメソッドにする

::Akaden::Info.shin_hamamatsu
=> { name: '新浜松', address: '' , ...}
::Akaden::Timetable.kamijima
=> [ { detection: 'upto', week: 'weekday', time: '10:35' }, ... ]
続きを読む

Amplify Geo for AWS Amplify を試してみる

AWS Amplify に AWS Location Serviceのリソースを作成してくれる Amplify Geo のデベロッパープレビューが公開されたので試してみた。

できたものはこちらです。

amplifygeo-20210929141446-hostingbucket-dev.s3-website-ap-northeast-1.amazonaws.com

aws.amazon.com

実は AWS Location Serviceが出た頃に Location Service は試していて、以下のような簡単なアプリケーションを作っていたのだけど、

master.d1h878o7did8z6.amplifyapp.com

f:id:jacoyutorius:20210929225510p:plain
地図にピンを刺すだけ

Amplify Geo を使うとこんな感じの機能をもっと簡単に作れるようになる。

続きを読む

Alexaスキルのindex.js の見通しを良くする

Alexa Hostedで自動生成されるコードは index.js にすべてのインテントハンドラのコードが実装されていてすこぶる見通しが悪い。

そこでハンドラごとに handers 配下に切り出してみた。

f:id:jacoyutorius:20210807051504p:plain
いい感じな気がする

f:id:jacoyutorius:20210807051600p:plain

毎回これやるの面倒だな。。