[小ネタ]CloudWatch LogsのS3エクスポート機能にて指定した時間範囲単位でエクスポートする

大量のデータを含むログストリームをS3に手動エクスポートする際に便利かもしれないスクリプトです。
2023.01.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

お疲れさまです。とーちです。

CloudWatch Logs には指定したロググループのログを S3 にエクスポートする機能があります。便利な機能なのですが、下記の記事にも記載のある通り、大量のログを一度にエクスポートしようとするとエクスポートに失敗することがあります。

S3 バケットへのエクスポートに失敗する CloudWatch Logs のトラブルシューティング

作成後に失敗したタスクをトラブルシューティングするには、[時間範囲] 設定を確認します。大量のデータを含むログストリームをエクスポートし、長い時間範囲を指定すると、エクスポートタスクが失敗することがあります。その場合、短い時間範囲を指定します。

上記の通り、 大量のデータを一度にエクスポートするのではなく、短い時間範囲を指定 すると S3 へのエクスポートが失敗しにくくなるとのことなので、あるロググループ内の全ログを「指定した時間範囲に区切って」エクスポートする、といった処理をスクリプトで書いてみました。

CloudWatch Logs の S3 エクスポートについて

前提としてお伝えしておきたいのですが、CloudWatch Logs の S3 エクスポート機能はあくまでも スポット(一時的な作業用途) で CloudWatch Logs のログを S3 にコピーしたいというときに使うもので、以下の理由から日常的に(例えばバッチとして)使う用途には不向きだと思います。

  • エクスポートタスクは同一アカウント内で、一度に 1 つしか動かせない
  • ログ量が多いとエクスポートに失敗することがある

CloudWatch Logs のログを常に S3 にも送りたいという要件の場合は、以下記事のようにサブスクリプションフィルターと Kinesis Data Firehose を併用する構成がオススメです。

作成したスクリプト

想定しているユースケースとしては以下です。

  • CloudWatch Logs のログを S3 に手動で退避する(一度限りの作業を想定)
  • ロググループ内のログ量が大きいため、大きな時間範囲を指定してエクスポートすると失敗する可能性がある
  • でも、一つずつ時間範囲を指定してエクスポートするのは面倒

以下が作成したスクリプトになります。 なお、動作確認は macOS 13.1 にて行っております。

cwlogstos3.sh

#!/bin/sh

S3BucketName=<エクスポート先バケット名を記載>
LogGroupName=$1
Interval_sec=$2
Interval_msec="${Interval_sec}000"

wait_for_export() {

  # エクスポートタスクの完了確認間隔。
  # デフォルト値10秒
  # wait_for_export関数を呼ぶ際に第二引数を指定するとその値を使用
  local sleep_time=${2:-10}

  # エクスポートタスクのジョブステータスがCOMPLETEDまたはFAILEDになるまで続ける
  while true; do

    job_status=$(aws logs describe-export-tasks \
                    --task-id ${1} \
                    --query "exportTasks[0].status.code" \
                    --output text)

    echo ${job_status}

    if [ "$job_status" == "COMPLETED" ]; then
     break
    elif [ "$job_status" == "FAILED" ]; then
     echo "--- The following export task failed ---"
     aws logs describe-export-tasks --task-id ${1}
     break
    fi

    sleep ${sleep_time}

  done
}

# ロググループ内の最初のイベント時刻を取得
 Loggroup_FirstEventTime=$(aws logs describe-log-streams \
  --log-group-name "${LogGroupName}" \
  --query "min(logStreams[*].firstEventTimestamp)" --output json)
# ロググループ内の最後のイベント時刻を取得
Loggroup_LastEventTime=$(aws logs describe-log-streams \
  --log-group-name "${LogGroupName}" \
  --query "max(logStreams[*].lastEventTimestamp)" --output json)
# Interval_msecの単位でエクスポートタスクを作成。
# 最初のイベント時刻から最後のイベント時刻までエクスポートする
TimeFrom=$Loggroup_FirstEventTime


while [ $TimeFrom -lt $Loggroup_LastEventTime ]
do
    TimeTo=$(($TimeFrom+$Interval_msec))
    task_id1=$(aws logs create-export-task \
      --task-name "cloudwatch-log-group-export1" \
      --log-group-name "${LogGroupName}" \
      --from $TimeFrom --to $TimeTo \
      --destination "${S3BucketName}" \
      --query 'taskId' --output text)


    wait_for_export ${task_id1}
    TimeFrom=$(($TimeTo+1))

done

使い方は以下の通りです。

bash -x ./cwlogstos3.sh <エクスポートしたいロググループ名> <エクスポート時の時間範囲を区切る間隔(秒)>

まとめ

以上となります。
この記事が誰かのお役に立てばなによりです。
以上、とーちでした。