AWS Android SDK for Kinesis Firehoseを動かして見る

## はじめに

以前、[Kinesis Firehoseを動かして見る](http://qiita.com/taka4sato/items/a3f37205cd4aceb525f4)でもチラシの裏しましたが、Kinesis Firehoseは、Backend Serverを自分で構築/管理せずとも、Android Client AppやIOT機器のLogをS3やRedshiftに簡単に溜められる、というServiceです。

`Google Analytics`, `Amazon Mobile Analytics`と競合する領域ではありますが、Backend serverは構築管理したくないけど、生Logは欲しい、と言う場合に使えるServiceです (みんな大好き`Google Analytics`は、無料版だと生LogのExportが出来ないのと、[個人情報に該当する情報は送ってはいけない](https://support.google.com/analytics/answer/6004245?hl=ja)と言う制約があります)。

その他、詳細は紹介記事を参照して下さい。

* [AWSが外部に一般供用するKinesis FirehoseはIoTなどのセンサデータをクラウドへ直接送る](http://jp.techcrunch.com/2015/10/08/20151007aws-introduces-kinesis-firehose-to-move-sensor-data-to-cloud/)
* [Amazon Kinesis StreamsとAmazon Kinesis Firehoseは何が違うのか](http://dev.classmethod.jp/cloud/aws/difference-between-kinesis-streams-and-kinesis-firehose/)

先日、やっとAndroid SDK v2.2.10で[Kinesis Firehoseをサポート](https://github.com/aws/aws-sdk-android/blob/master/CHANGELOG.md)してくれました、[FirehoseのClassも追加されています。](http://docs.aws.amazon.com/AWSAndroidSDK/latest/javadoc/)動作確認用のAndroid Appを作ってFirehose経由でS3にLogをUploadするProtoを作ってみたので、その時の設定手順、Android AppのCodeをチラシの裏しておきます。


## 作業の全体の流れ
全体の流れは以下の様になっています。

1. **CognitoのIdendity Poolの作成**
1. **Kinesis FirehoseのStreamの作成**
1. **Android Appの作成、LogのUpload**
1. **UploadされたLogの確認**


#### 注意

* `Firehose`はまだ`N. Virginia`, `Oregon`, `Ireland`の3 Regionのみサポートなので、`Firehose`, `Cognito`とも同じRegionで、この3つのどこかに作って下さい。

## AWS CognitoのIdendity Poolの作成
CognitoのIdentity Poolを作成します。Cognitoは馴染みが無い方が多いと思いますが、最低限の設定でOKなので怖くないです、1分で終わります。Regionを`N. Virginia`等に変更し、AWS Console→Cognitoに行き、`Create new identity pool`を選択します

#### **Step1**
`Identity pool name`は適当な名前を入れて、`Enable access to unauthenticated identities`にCheckeを付けて、`Create Pool`を選択し下さい。これでCognitoの設定は実質終わりです。

![Cognito1.png](https://qiita-image-store.s3.amazonaws.com/0/67592/ded0dc7a-04e9-b0c8-210b-9391bafd3e5e.png)

#### **Step2**
`Your Cognito identities require access to your resources`と言うUIに移り、IAM roleの設定画面が出ますが、何も変えずに`Allow`を選択して下さい。

![image](https://qiita-image-store.s3.amazonaws.com/0/67592/b7262319-3e37-bd0f-45b1-971c361c4ef4.png)


#### **Sample Code**
すると、Sample codeのUIに移ります。`"us-east-1:3030cfcf-4210-4980-99ab-34f941f4b16e"`などのIdentity Pool IDをコピペしておきます。

![Cognito2.png](https://qiita-image-store.s3.amazonaws.com/0/67592/82ac7283-b331-2c7a-63b2-e2f4be9f8f7b.png)


## Kinesis FirehoseのStreamの作成
続いて、Kinesis FirehoseのDelivery Streamを作成します。
AWS Console→Kinesis firehoseに行き、`Create Delivery Stream`を選択します

#### **Step1**
* `Destination`はS3を選択
* `Delivery stream name`は適当な名前を付ける (ここでは`test_kinesis`とします)
* `S3 bucket`は既存のS3 bucketを選択するか、新規作成する

上記設定をすれば、指定したS3 bucketにFirehoseのLogが溜まる様になります。

![image](https://qiita-image-store.s3.amazonaws.com/0/67592/1e9edbe4-b71d-7176-d1c2-bd7bbea17255.png)

#### **Step2**
* `Buffer Size`は`5`(MB)
* `Buffer interval`は`60`(秒)
* `IAM role`は`Firehose delivery IAM role`を選択  
  選択すると、`IAM role`の設定画面になりますが、Defaultのままで`Allow`を選択

上記設定で、60秒経過する or 5MB以上FirehoseにDataが溜まるとS3に書き出す設定になります。
 
![image](https://qiita-image-store.s3.amazonaws.com/0/67592/bb8c6c78-0c94-10c2-d696-34fa402a03af.png)

#### **Step3**
このStepは只の最終確認なので、`Create Delivery Stream`を選択して完了です。


## Androidのcode
最後に、Cognito, Firehoseの設定内容をAndroid Appへの組み込みます。

##### **Sample Appの仕様**
以下Sample codeの仕様概要です。[Amazonの公式FirehoseのAPI仕様書](http://docs.aws.amazon.com/mobile/sdkforandroid/developerguide/kinesis.html)を参考に作りましたが、公式Documentに微妙に(Criticalな)Typoがあるので注意です。


* Buttonが3つある
* Button1/2をTapするとLogがたまり、Button3をTapするとServerにLogがPushされる
* Logは以下の様なjson形式

```
{"model":"SO-02H","andoid_ID":"434c3786437e39cf","date":"20160112T090920","message":"onCreate","app_name":"Firehose Test1"}
```


##### **Customizeが必要な個所**
以下のSample codeで個別にCustomizeが必要なのは、3箇所です

* CognitoのIdentity pool ID `us-east-1:3030cfcf-4210-4980-99ab-34f941f4b16e`
* Kinesis FirehoseのStream名 `test_kinesis`
* AWSのRegion `US_EAST_1`

##### **Sample code**

```java
package com.your.package.name;

import android.os.AsyncTask;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.os.Build;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import android.provider.Settings;

import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.mobileconnectors.kinesis.kinesisrecorder.KinesisFirehoseRecorder;
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.regions.Regions;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.json.JSONException;
import org.json.JSONObject;

public class MainActivity extends ActionBarActivity{

  KinesisFirehoseRecorder firehoseRecorder;
  String android_id;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    saveFirehoseRecord("onCreate");
    setContentView(R.layout.activity_main);
  }

  public void saveFirehoseRecord(String put_string) {
    File directory = this.getCacheDir();
    Regions region = Regions.US_EAST_1;
    AWSCredentialsProvider provider = new CognitoCachingCredentialsProvider(
        this,
        "us-east-1:3030cfcf-4210-4980-99ab-34f941f4b16e",
        Regions.US_EAST_1);

    android_id = Settings.Secure.getString(this.getContentResolver(), Settings.Secure.ANDROID_ID);
    firehoseRecorder = new KinesisFirehoseRecorder(directory, region, provider);
    Date date = new Date();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'kkmmss");

    JSONObject json = new JSONObject();
    try {
      json.accumulate("model", Build.MODEL);
      json.accumulate("andoid_ID", android_id);
      json.accumulate("date", sdf.format(date));
      json.accumulate("message", put_string);
      json.accumulate("app_name", "Firehose Test1");
      Log.e("KinesisFirehoseTest1", json.toString());

    } catch (JSONException e) {
      e.printStackTrace();
    }
    firehoseRecorder.saveRecord(json.toString(), "test_kinesis");
  }


  public void onClick(View v) {
    switch(v.getId()) {
      case R.id.button1:
        Toast.makeText(MainActivity.this, "Button1 pressed", Toast.LENGTH_LONG).show();
        saveFirehoseRecord("onClick-1 event");
        break;
      case R.id.button2:
        Toast.makeText(MainActivity.this, "Button2 pressed", Toast.LENGTH_LONG).show();
        saveFirehoseRecord("onClick-2 event");
        break;
      case R.id.button3:
        Toast.makeText(MainActivity.this, "Button3 pressed - submit events", Toast.LENGTH_LONG).show();
        new AsyncTask<Void, Void, Void>() {
          @Override
          protected Void doInBackground(Void... v) {
            try {
              saveFirehoseRecord("onClick3 - submit events");
              firehoseRecorder.submitAllRecords();
            } catch (AmazonClientException ace) {
              Log.e("KinesisFirehoseTest1", "submitAll failed!");
            }
            return null;
          }
        }.execute();
        break;
    }
  }

  @Override
  protected void onPause() {
    super.onPause();
  }

  @Override
  protected void onResume() {
    super.onResume();
  }
}
```

#### **その他**

釈迦に説法だと思いますが、build.gradeの設定と、manifest.xmlの設定もお忘れ無く。

(1) build.gradleでKinesisのSDKを設定する

```
compile 'com.amazonaws:aws-android-sdk-kinesis:2.2.10'
```

(2)AndroidManifest.xmlで以下のPermissionを設定する
 
```
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
```

## UploadされたLogの確認
最後に、実際にLogがS3にUploadされているかの確認です。Android app上のボタンをポチポチ押して、Logを発生させてみて下さい。  
暫くすると、Firehoseで指定したS3 bucket上に、`test_kinesis-2-2016-01-17-12-23-01-4e3483ed-44ce-44ed-87d7-43b77347ce7b`みたいなファイルが出来ると思うので、中身を確認して見て下さい。


## 最後に

Firehoseは、Serverの構築/管理無し(Zero administration)で、各種logをCloudにあげる事が可能なServiceです。同様のServiceとして`Google Analytics`や`AWS Mobile Analytics`との比較を書いておきますが、Needsに合致すれば、とてもよいんじゃないかな、と思います。


## (おまけ) Serverの構築/管理無しでLog収集可能なService比較
Serverの構築/管理無しに、Client AppやIOT機器のLogをCloudに溜めて解析したい、と言うNeedsに対して、`Google Analytics`, `Amazon Mobile Analytics`, `Kinesis Firehose`の3つをProtoして触って来ました、以下、簡単ですが3者の比較です。

#### **Google Analytics**
###### Pros
* みんな大好きGA、無料
* GAのQuery APIを使いこなすとかなり複雑なQueryも可能、[Query Explorerさいこーです](https://ga-dev-tools.appspot.com/query-explorer/)
* GAのDashboardがかなり秀逸、多くのNeedsはこのDashboardの中で完結
* GAのTag Managerがかなり秀逸
* Client側からのLog Uploadの細かい制御(Networkが繋がっていない場合の遅延Upload, 途中で切れた場合のRetry etc)をGoogle Play Serviceがほぼ自動でやってくれる

###### Cons
* 生ログにはアクセスできない(GA Premium Accountという有料契約だと生ログ貰えます)
* [個人情報に該当する情報は送ってはいけない](https://support.google.com/analytics/answer/6004245?hl=ja)
* Record件数が多いと、Query時に問答無用にSamplingされる
* Log Updateが1日1回とやや遅い


#### **Amazon Mobile Analytics**
自分の周りでも認知度が微妙に低い気がしますが、[Amazon版のGoogle Analytics対抗Serviceです](http://aws.amazon.com/jp/mobileanalytics/)

###### Pros
* 生ログがS3に溜まる
* Log Updateが1時間毎

###### Cons
* 有料 (やや高く`$1/100万 events`、1万台で1台辺り100 event/dayだと、$1/day)
* Dashboardの機能はやや簡易的、Query機能も無し
* Google Analyticsに比べるとSDK側で補完してくれる機能、Configurationの余地が少ない

#### **Kinesis Firehose**
微妙にAmazon Mobile Analyticsと被ってる気もしますが、IOT用のLog収集Serviceで、Logを低遅延でS3/Redshiftに溜められる所に特化したServiceです

###### Pros
* 生ログがS3に溜まる
* Log Updateが最短1分毎

###### Cons
* 有料 (`$0.035/1GB`とデータ量課金、1万台で1台辺り100 event/day、1KB/eventとすると、$0.035/day)
* Dashboardの機能はほぼ無い, Query機能も無し
* Sessionの開始/終了の判定が無く、必要なら自前で実装する必要が有る
* Android SDK側の機能は余り期待するな