Lambda を使って CloudWatch Logs から S3 へ自動的にエクスポートする
Posted on
- #aws
Lambda のログは自動的に CloudWatch Logs に保存されますが、他と連携する場合は S3 のほうが何かと都合がいいです。
CloudWatch Logs にはログデータを一括で Amazon S3 にエクスポートする機能があります。リアルタイム性が求められずバッチ処理で定期的にエクスポートする用途に向いています(リアルタイム性が求められる場合は、サブスクリプションを使用したログデータのリアルタイム処理が用意されています)。
というわけで Lambda と CloudWatch Events を使って、CloudWatch Logs から S3 へ自動的にエクスポートしてみます。
CloudWatch Logs API の CreateExportTask
S3 へのエクスポートは CloudWatch Logs API の CreateExportTask を使います。 from
と to
のパラメータを指定することで対象日時を絞れます。今回は日次単位でエクスポートしたいので、前日の 00:00:00 〜 23:59:59 を指定します。
ちなみに、Lambda と CloudWatch Logs のタイムゾーンは UTC
で統一されています。タイムゾーンの変換は必要ないので、コードの中では UTC
のまま処理します。
S3 のバケットポリシーを追加する
CloudWatch Logs が書き込めるように S3 のバケットポリシーを追加します。バケットは CloudWatch Logs と同じリージョンにする必要があります。
バケット名を xxxxx-lambda-log
にしているので読み替えてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "logs.ap-northeast-1.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::xxxxx-lambda-log"
},
{
"Effect": "Allow",
"Principal": {
"Service": "logs.ap-northeast-1.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::xxxxx-lambda-log/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}
「CloudWatch コンソールを使用してログデータを Amazon S3 にエクスポートする」を参考に、手動でエクスポートできることを確認しておきます。
Python でバッチスクリプトを書く
エクスポートするためのバッチスクリプトを Python で書きます。 lambda_name
や s3_bucket_name
は読み替えてください。
import datetime
import time
import boto3
lambda_name = 'xxxxx'
log_group_name = '/aws/lambda/' + lambda_name
s3_bucket_name = 'xxxxx-lambda-log'
s3_prefix = lambda_name + '/%s' % (datetime.date.today() - datetime.timedelta(days = 1))
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,
fromTime = from_ts * 1000,
to = to_ts * 1000,
destination = s3_bucket_name,
destinationPrefix = s3_prefix
)
return response
実行された前日 0 時のタイムスタンプを取得して、そこから 86399 秒間 (00:00:00 〜 23:59:59) のログを対象にエクスポートしています。引数のタイムスタンプは秒ではなくミリ秒なので、Python で計算したタイムスタンプを 1000 倍しています。
うまく実行されていれば、CloudWatch Logs の View all exports to Amazon S3 でエクスポート結果が確認できます。実際のログは s3://xxxxx-lambda-log/xxxxx/yyyy-mm-dd/
にエクスポートされています。
CreateExportTask は非同期 API なので、エクスポートが終わるまでに最大 12 時間かかる場合があるようです(データ量によると思いますが、自分は数秒で完了しました)。なので、この時間までに必ずエクスポートしたいという要件には向いていません。
最後に CloudWatch Events でスケジュール実行のトリガーを設定します。 UTC 時間の 0 時 5 分に実行させるには cron(5 0 * * ? *)
と指定します。
まとめ
数十行の Lambda ファンクションで CloudWatch Logs から S3 へのエクスポートが自動化できました。 Lambda を使って人がオペレーションする作業をどんどん減らしていきたいです。