CloudWatch LogsにあるログをLambdaを使用してS3に転送する

# このページはなに
表題通りとなりますが、Lambda関数を使用してCloudWatch LogsにあるログをS3に転送する仕組みを実装します。

# 参考にさせていただいたURL
https://qiita.com/blackriver/items/372f5e9c667a8a107f13

# 背景
AWSサービスの中でファイルの配置の料金が一番安いのはS3です。
なので、例えばCloudWatch Logsに格納しているログについては7日保管にして、S3には1年間保管のような仕組みをつくりたいと考えました。

# 仕組み（構成図）
![構成図.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/413372/99f7d608-78d3-efb8-eaad-900dd41b8ac2.png)

# 作業手順
## IAMロール（ポリシー）の作成
以下、IAMポリシーを作成してIAMロールにアタッチしてLambdaに紐づけてください。
（LambdaがS3バケットへの操作の権限が必要なため）

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"　　　　　#　S3バケット内のリスト表示に必要（最初に接続確認をするため）
            ],
            "Resource": "arn:aws:s3:::S3バケット名"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",　　　　　#　S3バケットにログを配置する前にS3バケット内のファイルの存在を確認するための権限
                "s3:PutObject"　　　　　#　S3バケットにログを配置するための権限
            ],
            "Resource": "arn:aws:s3:::S3バケット名/*"
        }
    ]
}
```


## S3バケットポリシーの設定
・吐き出す先のS3バケットポリシーに、以下のバケットポリシーを設定してください。
・補足：この設定は、CloudWatch LogsからS3バケットにログを配置するために必要なバケットポリシーになります。
　（CloudWatch LogsにIAMロールは紐づけられないので、S3のバケットポリシーで制御します。）

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.対象リージョン.amazonaws.com"
            },
            "Action": "s3:GetBucketAcl",　　　　　#　CloudWatch Logsから S3バケットのアクセス（s3apiコール）制御のポリシー 
            "Resource": "arn:aws:s3:::S3バケット名"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.対象リージョン.amazonaws.com"
            },
            "Action": "s3:PutObject",　　　　　#　CloudWatch Logsから S3バケットへログ配置するためのS3バケットポリシー 
            "Resource": "arn:aws:s3:::S3バケット名/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}
```

## Lambda関数の作成
・使用言語は、python3。バージョンは、3.7です。

### コード
コードは「参考にさせていただいたサイト」に記載してある内容とほぼ同じです。
コード内に記載されているコメントアウト部分を、環境ごとに適宜編集していく形になります。

```
import datetime
import time
import boto3

log_group_name = 'ロググループ名'    #CloudWatchLogsのロググループ名
s3_bucket_name = 'S3バケット名'    #保存先S3バケット名
s3_string_prefix = 'S3内のプレフィックス'    #S3内のプレフィックス（文字列）を定義
s3_date_prefix = '/%s' % (datetime.date.today() - datetime.timedelta(days = 1))    #S3内のプレフィックス（日付）を定義

def get_from_timestamp():
    today = datetime.date.today()
    yesterday = datetime.datetime.combine(today - datetime.timedelta(days = 1), datetime.time(0, 0, 0))
    timestamp = time.mktime(yesterday.timetuple())
    return int(timestamp)

def get_to_timestamp(from_ts):
    return from_ts + (60 * 60 * 24) - 1

def lambda_handler(event, context):
    from_ts = get_from_timestamp()
    to_ts = get_to_timestamp(from_ts)
    print('Timestamp: from_ts %s, to_ts %s' % (from_ts, to_ts))

    client = boto3.client('logs')
    response = client.create_export_task(
        logGroupName      = log_group_name,     #取得するCloudWatchロググループ名
        fromTime          = from_ts * 1000,
        to                = to_ts * 1000,
        destination       = s3_bucket_name,     #保存先S３バケット名
        destinationPrefix = s3_string_prefix + s3_date_prefix   #保存先S3バケット名配下の任意のサブフォルダ名（プレフィックス名）
    )
    return response
```

## CloudWatch Eventの作成
毎日AM4:00にイベント発生する場合、CloudWatch Eventに以下のcron設定をすればおkです。
（時間がUTC形式のため、UTCから+9時間することを忘れずに。）

```
0 19 * * ? *
```

# 悩んだこと
S3に吐き出したときのファイル名が「000000.gz」になってしまうので、ファイル名を変える方法を調べましたが、boto3のapiの仕様で「000000」で吐き出されてしまうようです（ファイル名を別ネームで保存したい場合は、別途仕組みを実装する必要あり）。

## boto3_URL
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/logs.html#CloudWatchLogs.Client.create_export_task