6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AWS APIGatewayのアクセスのログ(json)を作成し、Kinesis Firehose(S3)に投げる

Last updated at Posted at 2018-03-11

iOSアプリでユーザがあるいくつかのページを見たかどうかが知りたくなり、元々使ってみたかったAWS周りのサービスを使ってアプリの起動ログを送るようにしてみた覚書。

※2018/03/01前後の情報です。
※備忘録代わりメモなので雑なところあります。

やりたかったこと

  • iOSアプリで特定ページを見たかどうかのログを取りたい
  • アプリ自体はクライアントだけで動作するものなので、バックエンドはなく、そのためだけに用意もしない
  • 今はログを取得したいだけだが、今後何か処理が入るかもしれない
  • 今後ログの用途が広がる可能性も考えて、S3に置きたい

やったこと

  • APIGatewayでエンドポイントを作成
  • iOSアプリからリクエストを送る
  • APIGatewayからKinesis Firehoseへログを送る

この記事で説明しないこと

  • Kinesis Firehoseの話 (既にdelivery streamがある前提です)
  • iOSアプリ側の実装 (特に今回の話に依存することはないので書きません)

APIGatewayでエンドポイントを作成

エンドポイントを用意する

APIGatewayのトップ APIs から Create API を選択する。

API_Gateway.png

New API を選択し、必要情報を入力する。

API Name: 適当な判別可能な名前
Description: 必要なら説明
Endpoint Type: Regional or Edge Optimized を選ぶ、それぞれの詳細はこちら

情報入力後、画面右下の Create API を選択するとAPIが作成され、トップの APIs の下に今作成したAPIの名前が追加される。

リソースとメソッドの追加

追加されたAPIを選択すると、次のような画面が表示される。

スクリーンショット 2018-03-11 14.08.27.png

ここから、作成したAPIにresourceとmethodを追加していく。

Actions から Create Resource を選択すと、Resource作成画面へ遷移するので、必要情報を入力する。
今回は単純にiOSアプリからリクエストを叩きたいだけなので、proxy resourceやCORSにはチェックを入れずに進みます。

試しに test というResourceを作成しました。

スクリーンショット 2018-03-11 14.15.00.png

/test というResourceが追加されたのがわかります。
続いて、ここにMethodを追加します。再び Actions から Create Method を選択すると、次のようにセレクトリストが出現します。

スクリーンショット 2018-03-11 14.14.49.png

このセレクトリストでMethodを選択することができます。今回はログを残したいだけなのでGETにします。リストからGETを選択して、リストの隣に出るチェックマークを押すと、設定が保存され次のような画面が表示されます。

スクリーンショット 2018-03-11 14.18.46.png

APIGatewayへのリクエストをLambdaなどで処理したい場合はこのままで結構ですが、今は一旦リクエストを送れるところまで進むため、Mockを選択し、Saveします。

ここで、次のような画面になります。

スクリーンショット 2018-03-11 14.21.09.png

これで一旦形としては出来ましたが、アプリから叩く場合はそのレスポンスが気になるため、仮でレスポンスを入れておきます。

上記画像でいう右下の、Integration Responseを選択します。
ここで、レスポンスのStatus CodeContent TypeHeaderBodyのテンプレートなどを設定することが出来ます。
今は一旦返ればなんでも良いとして、Body Mapping Templateを下記のようにしました。

スクリーンショット 2018-03-11 14.26.09.png

テストとデプロイ

Resource > Method (GET) のページに戻ると、左上に TEST の項目があることがわかります。
ここでは、作成したAPIをテストすることができます。今回はMockでの作成であるため落ちることはないかと思いますが、試してみましょう。

API_Gateway.png

遷移後の画面の下の方で TEST をクリック。

API_Gateway.png

こんな感じで、ちゃんと動いていることがわかります。

問題なければ、外から叩くためにAPIをDeployしましょう。

Actions から Deploy API を選択すると、新たにウィンドウが表示されます。
Stageから [New Stage] を選択すると次のような画面になるため、必要情報を入力しましょう。
Stage はバージョンのようなもので、テストの場合は、 Beta などで良いと思います。

API_Gateway.png

Deployすると、自動的にStagesのページに遷移すると思います。
トップに表示されている Invoke URL: がエンドポイントのもととなるURLです。

API_Gateway.png

※実際に使用する場合は、ここままだと誰でもAPIを叩けてしまうため、API Keyを作成する、Authorizerを設定する、など何かしらの制限を設けましょう。

iOSアプリからリクエストを送る

ここはiOSアプリの実装になるので、アプリ実装の説明は割愛します。

先程までの設定で、決まった値を返すエンドポイントを作成することが出来ました。

https://xxxx.execute-api.ap-northeast-1.amazonaws.com/beta/test (stageがbeta, resourceがtestの場合)にリクエストを送り、Mockで設定した値が返ってくるかどうか確認してください。

APIGatewayからKinesis Firehoseへログを送る

Roleの作成

疎通の確認が取れたら、Mockで作っていたAPIをKinesis FirehoseにObjectをPUTするように作り変えます。

がその前に、APIGatewayからKinesis Firehoseを使えるように、roleを作ってあげないといけないため、別で作成しましょう。

詳細は割愛しますが、IAMを開き、

  • Policyから、Kinesis FirehoseにPut RecordPut Record BatchのActionを許可するようなPolicyを作成する
  • 作成したPolicyを持つRoleを作成する
  • 作成したRoleの Trusted entitiesapigateway.amazonaws.com を追加する

で出来ると思います。

とりあえず送れるようにする

roleを作成できたら、先程Mockの選択した Integration Request の設定画面へ行きます。
先程Mockにした部分を、AWS Serviceに変更し、各項目を次のように埋めます。

execution roleには、先程作成したroleのARNを入れてください。

API_Gateway.png

Saveしたあとは、一旦テストしてみましょう。おそらく次のようなExceptionが返ってきます。

{
  "__type": "SerializationException"
}

これは、Kinesis FirehoseにPUT RECORDする際は、次の形式で送らないといけないためです。

{
  "DeliveryStreamName": "[Kinesis Firehose上のdelivery streams名]",
  "Record": {
    "Data": "[データのオブジェクト(Base64エンコード)]"
  }
}

この形式にするために、Integration Requestの下の方にある設定の、Body Mapping Templateを編集します。

まずはAdd mapping template から、Content-Typeに application/json を指定します。

API_Gateway.png

追加した application/json を選択すると、更に下にスクロールできるようになり、Mapping Templateを編集できます。

API_Gateway.png

ここにTemplateを記入していくわけですが、詳しくは下記を参照すると良いです。

API Gateway のマッピングテンプレートリファレンス
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html

テストの場合は、簡単に次のような形式で送ってみると良いと思います。

# set($data = "{ ""key"": ""property""}")
{
  "DeliveryStreamName": "xxxxx",
  "Record": {
    "Data": "$util.base64Encode($data)"
  }
}

ここまで出来たら動作確認しつつ、欲しい形式にTemplateを変えていきましょう。
動作確認では、まずはAPI GatewayでのTest機能で確認し、エラーが出なければ実際にリクエストを送るもの(今回はアプリ)から確認すると良いでしょう。

設定に問題がなければ、少しの時間の後にKinesis Firehoseの方にデータが送られます。
ここでは解説していませんが、実際はそこからS3などにデータを吐き出していると思いますので、そちらで意図した出力になっているか確認してください。

最終的なMapping Template

今回は、基本的にはログ目的だったので、resource名や時間、あればrequest parameterなどを拾えれば良いということで、最終的に下記のようになりました。(もっとスマートなやり方があるかもしれません...)

# set($allParams = $input.params())
# set($params = $allParams.get('querystring'))
# set($headers = $allParams.get('header'))
# set($json = "{ 
    ""requestId"":""$context.requestId"", 
    ""ip"": ""$context.identity.sourceIp"", 
    ""user"":""$context.identity.user"",
    ""requestTime"":""$context.requestTime"", 
    ""requestTimeEpoch"":""$context.requestTimeEpoch"",
    ""httpMethod"":""$context.httpMethod"",
    ""resourcePath"":""$context.resourcePath"", 
    ""queryParams"": {
    #foreach($paramName in $params.keySet())
    ""$paramName"":""$util.escapeJavaScript($params.get($paramName))""#if($foreach.hasNext),
    #end
    #end
    },
    ""queryStrings"": ""#foreach($paramName in $params.keySet())$paramName=$util.escapeJavaScript($params.get($paramName))#if($foreach.hasNext)&#end#end"",
    ""protocol"":""$context.protocol""
}")
# set($data = $json.replaceAll(" ","").replaceAll("\n","")+"
")
{
  "DeliveryStreamName": "xxxxx",
  "Record": {
    "Data": "$util.base64Encode($data)"
  }
}

各変数やメソッドはリファレンスを参照していただくとして、他の部分を少し説明します。

# set($json = "{ (略

ここで、 $json にログとして残したい各パラメータをjson形式で無理やり作成しています。

# set($data = $json.replaceAll(" ","").replaceAll("\n","")+"
")

ここでは、ログを一行一レコードで保存したいので、先程作成した $json の無駄な余白や改行を削除し、最後に無理やり改行を加えています。
(改行周りが "\n"などで上手くいかなかったので、+のあとに改行の入力を無理やりいれています...。)

その後、最後の実際に送るjsonのブロックで、Kinesis Firehoseの形式に合わせつつ、$dataをbase64エンコードしたものを追加しています。

おまけ

CloudWatch Logsを使わなかった理由

最初はAPI Gateway > Kinesis Firehose > S3 ではなく、API GatewayにはCloudWatch Logsと連携して簡単にアクセスログを取得できることから、そちらを利用する方法を考えていました。

参考記事:
Amazon API Gateway でアクセスログ記録をサポート
【新機能】Amazon API Gateway でアクセスログを記録する #reinvent

ただし今回は、ユーザがendpointを叩いたときのget parameterもログとして取得したいという前提があり、その実現方法が分からなかったため断念しました。
単純にアクセスログを残したいだけであれば、CloudWatch Logsを使うほうが楽だと思います。

以上です。何か間違いあればコメント頂けると幸いです。

6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?