CloudWatchLogsからS3へのエクスポートを自動化

この辺りをものすごく参考

> * [CloudWatch Logs に蓄積したログをS3に定期的にエクスポートする - あくまで個人的なメモ](https://honeybe.hatenablog.jp/entry/2018/03/14/114635)
> * [Lambda を使って CloudWatch Logs から S3 へ自動的にエクスポートする](https://blog.manabusakai.com/2016/08/cloudwatch-logs-to-s3/)
> * [Task timed out after 3.00 secondsの対処法 | CloudAdvisor](https://hacknote.jp/archives/11625/)

Lambda を使って定期的にS3へとログを飛ばす

## Lambda関数のロール設定例

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateExportTask",
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:PutLogEvents"
            ],
            "Resource": "arn:aws:logs:*:*:*"
        }
    ]
}
```

`logs:CreateExportTask` が CloudWatchLogs からログをエクスポートするために必要

それ以外は、LambdaからCloudWatchLogsへのログ書き込みに必要

## Lambda関数作成

以下、ソース

Node.js (8.10)

```js
'use strict';

const aws = require('aws-sdk');
aws.config.update({region: 'ap-northeast-1'});

exports.handler = (event, context, callback) => {
    const getToTS = function() {
        let now = new Date();
        now.setHours(0);
        now.setMinutes(0);
        now.setSeconds(0);
        now.setMilliseconds(0);
        return now.getTime();
    };
    const getFromTS = function(toTS, from) {
        let to = new Date(toTS);
        to.setDate(to.getDate() - from);
        return to.getTime();
    };
    const dateFormat = function(date) {
        let y = ('000' + date.getFullYear()).slice(-4);
        let m = ('0' + (date.getMonth() + 1)).slice(-2);
        let d = ('0' + date.getDate()).slice(-2);
        return y + "-" + m + "-" + d;
    };

    const s3 = new aws.S3();
    const cloudwatchlogs = new aws.CloudWatchLogs();
    const createExportTask = (opt) => {
        return new Promise((resolve, reject) => {
            cloudwatchlogs.createExportTask(opt, (err, data) => {
                if (err) {
                    console.log("createExportTask failed", err, err.stack);
                    resolve(false);
                    return;
                }
                console.log("createExportTask success", opt.logGroupName, data);
                resolve(data);
            });
        });
    };
    const sleep = (msec) => {
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve();
            }, msec);
        });
    };
    
    const s3BucketName = 's3-bucket'; // 出力先のS3バケット
    const logGroups = [
        {'name':'/var/log/httpd/access_log','s3prefix':'httpd_access_log'},
        {'name':'/var/log/httpd/error_log','s3prefix':'httpd_error_log'},
    ];

    // 日次での処理を想定
    const toTS = getToTS();             // 実行日の00:00:00.0
    const fromTS = getFromTS(toTS, 30); // 実行日30日前の00:00:00.0

    const trial = 5;    // 失敗時の試行回数
    const trial_duration = 6000;

    const run = async function() {
        for (let i = 0; i < logGroups.length; i++) {
            let s3prefix = `${logGroups[i].s3prefix}/` + dateFormat(new Date(fromTS));
            let taskName = `export_task_${logGroups[i].s3prefix}` + (dateFormat(new Date(fromTS)));
            // logGroups[i].name というロググループに溜められたログの内容が s3://${s3BucketName}/${logGroups[i].name}/yyyy-mm-dd/ に出力される。
            try {
                let count = 1;
                let opt = {
                    'taskName': taskName,
                    'logGroupName': logGroups[i].name,
                    'from': fromTS,
                    'to': toTS,
                    'destination': s3BucketName,
                    'destinationPrefix': s3prefix
                };
                let result = await createExportTask(opt);
                while (result == false) {
                    if (count >= trial) {
                        break;
                    }
                    await sleep(trial_duration);
                    count++;
                    result = await createExportTask(opt);
                }
                if (result == false) {
                    throw new Error("createExportTask failed. logGroupName: " + logGroups[i].name);
                }
            } catch (err) {
                console.error("createExportTask Exception", err);
                callback(err);
                return;
            }
        }
        callback(null, "Finish");
    };
    run();
};
```

### Lambda関数のタイムアウト設定

![image.png](https://qiita-image-store.s3.amazonaws.com/0/53200/d013320f-272f-b173-644e-7b737a11eaf2.png)

各々設定

---

あとは、CloudWatchEventsで上記が実行されるように設定するのと、CloudWatchLogsのログ保持期間を設定するだけ

適宜 `logGroups` を増やして対応していく