CloudWatchメトリクスを定期的に取得するログ監視システムの構築(2)

[先月の記事](https://qiita.com/Kept1994/items/aa76ae065d8d16c557a8) でCloudWatchのログを定期的に取得してS3に保存するshellscriptを書いたが、EC2上に置いておくとその都度インスタンスを起動しないといけないため、ちょっとイケてない。

そこで、今回はlambdaから実行するバージョンを作ってみる。


##概要（今回学んだこと）

* lambdaには一時的に利用できる/tmp/ディレクトリがあること。
* s3のライフサイクル
* 日付の取得（python）
* csvファイルの作成（python）

## ソースコード

[コード全部](https://github.com/K53/getMetrics/blob/master/getMetrics.py)

##構成

###メイン関数

```python
def lambda_handler(event, context):
    all_metrics_list = [
        {
        'NameSpaceHeader' : 'AWS/',
        'NameSpace' : 'EC2',
        'MetricName':'CPUUtilization',
        'Dimensions':[{"Name" : "InstanceId","Value" : "i-xxxxxxxxxxxxxxxxx"}],
        'Statistics' : 'Average'
        }
    ]

    for target in all_metrics_list:
        #メトリクス取得
        logs = getMetricStatistics(target)

        #CSVへ変換・ソート
        convertCSV(logs['Datapoints'])
        sortCSV()

        #適当なフォルダ名/ファイル名を生成して、s３へアップ
        uploadToS3(generateFileName(target))

    return {
        'statusCode': 200,
        'body': json.dumps('finish lambda')
    }
```
##各論

###日付の取得

CloudWatchメトリクスはGMTなので、JSTで1日分のログを収集するには15時で切る。(JSTで指定したい）

```python
today = datetime.today()
yesterday = today - timedelta(days=1)
dayBeforeYesterday = today - timedelta(days=2)

folder_title = datetime.strftime(yesterday, '%Y-%m-%d')
endTime = datetime.strftime(yesterday, '%Y-%m-%dT15:00:00Z')
startTime = datetime.strftime(dayBeforeYesterday, '%Y-%m-%dT15:00:00Z')
```

###メトリクスの取得

メトリクスによっては必要なDimensionが異なる。これが正しくないとNo Dataが返り続ける。

```python
def getMetricStatistics(target_dict):
    cloudwatch = boto3.client('cloudwatch', region_name='ap-northeast-1')

    logs = cloudwatch.get_metric_statistics(Namespace=target_dict["NameSpaceHeader"] + target_dict["NameSpace"],
                                MetricName=target_dict["MetricName"],
                                Dimensions=target_dict["Dimensions"],
                                StartTime=startTime,
                                EndTime=endTime,
                                Period=300,
                                Statistics=[target_dict["Statistics"]]
                                )
    return logs
```


###csvへの変換とソート
lambdaではtmpディレクトリが用意されている。ここに整形するファイルを置いておく。

```python
def convertCSV(datapoints):
    with open('/tmp/tmp.txt','w') as f:
        if len(datapoints) == 0:
            print("Empty")
            pass
        else:
            csv.register_dialect('dialect1', doublequote=True, quoting=csv.QUOTE_ALL)
            writer = csv.DictWriter(f, fieldnames=datapoints[0].keys(), dialect="dialect1")
            for row in datapoints:
                writer.writerow(row)


def sortCSV():
    with open('/tmp/tmp.txt', 'r') as f:
        reader = csv.reader(f)
        result = sorted(reader, key=operator.itemgetter(0))

    with open('/tmp/tmp.txt', 'w') as f:
        data = csv.writer(f,delimiter=',')
        for row in result:
            data.writerow(row)
```

`with`キーワードを使用すると、ファイル操作中にエラー終了してもcloseしてくれるので有能。

#####csvモジュールについて

`register_dialect` : dialectを登録する。デリミタやクオーテーションの種類などはここで指定して、1つのセットを作り命名する。

`DictWriter ` : 辞書を出力行にマップするオブジェクトを生成する。fieldnamesはファイルに書き込む順番。

書き出す前に`writer.writeheader()`でヘッダーをつけられる。今回はいらない。

### S3ライフサイクルの設定

Intelligent-TIERINGは、参照頻度に応じてStandardとStandard-IAに割り振ってくれる。30日アクセスされなければStandard-IAへ格納する。

管理タブから設定。S3はオブジェクトのバージョン管理もできるので有効にする場合には、現行のバージョン（LATEST）と以前のバージョンのそれぞれにライフサイクルのルールを設定できる。

→[公式ドキュメント](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/create-lifecycle.html)

## おわりに

あとはCloudWatch Events(cron)で定期実行していけば自然にログが貯まる。

ただ、0時ちょうどに実行すると日付が正しくないような気がする？（今後もう少し検討）


##EC2上にスクリプトを置いてログ収集をする以前の方法

[CloudWatchメトリクスを定期的に取得するログ監視システムの構築](https://qiita.com/Kept1994/items/aa76ae065d8d16c557a8)