S3サーバーアクセスログの新機能、CloudWatch Logs配信とS3 Tables連携を試してみた

S3サーバーアクセスログの新機能、CloudWatch Logs配信とS3 Tables連携を試してみた

S3サーバーアクセスログの新機能として、CloudWatch Logsへの構造化JSON配信が追加されました。さらにS3 Tables連携を設定することで、ログをApache Iceberg形式のテーブルとしてAthenaから分析できます。今回は実際に配信設定からS3 Tables連携、CloudWatch Logs InsightsとAthenaでのクエリまで試しました。
2026.06.30

はじめに

2026年6月29日、S3サーバーアクセスログの新しい配信先としてAmazon CloudWatch Logsが追加されました。

https://aws.amazon.com/jp/about-aws/whats-new/2026/06/amazon-s3-cloudwatch-logs-tables/

従来はテキスト形式でS3バケットへ配信されるだけだったアクセスログが、CloudWatch Logsへ構造化JSONとして配信されるようになりました。ログ到着後はCloudWatch Logs Insightsでそのままクエリできるほか、S3 Tables連携を設定することでApache Iceberg形式の管理済みテーブルとしてAthenaからSQL分析できます。

従来の配信方式(S3バケット宛テキスト形式)と新方式の主な違いは以下のとおりです。

CloudWatch Logs 配信(新) S3バケット配信(従来)
フォーマット 構造化JSON スペース区切りテキスト
クエリ手段 CloudWatch Logs Insights / Athena (S3 Tables) Athena(要テーブル定義)
パース処理 JSONフィールドとして利用可能 スペース区切りテキストのパースが必要
クロスアカウント集約 対応 非対応(同一アカウントのみ)
クロスリージョン集約 対応 非対応(同一リージョンのみ)
KMS暗号化 対応 制約あり(条件付きでSSE-KMS可)
SQL分析 S3 Tables連携により管理済みテーブルとして利用可能 Athenaテーブル定義やパース設定が必要
追加コスト CloudWatch Logs 取り込み・保存料金 ストレージ料金のみ

今回は、新規バケットに対してCloudWatch Logs配信と従来のS3バケット配信を並行設定しました。テストリクエストを発行した後、CloudWatch Logs InsightsとAthena(S3 Tables)でクエリするところまでを確認します。

検証環境

  • リージョン: ap-northeast-1(東京)
  • AWS CLI: v2.35.12

リソース作成

S3バケット

aws s3api create-bucket \
  --bucket sal-cw-s3tables-demo-20260630 \
  --region ap-northeast-1 \
  --create-bucket-configuration LocationConstraint=ap-northeast-1

aws s3api create-bucket \
  --bucket sal-cw-s3tables-demo-20260630-logs \
  --region ap-northeast-1 \
  --create-bucket-configuration LocationConstraint=ap-northeast-1

CloudWatch Logs ロググループ

プレフィックスは /aws/vendedlogs/ 配下にする必要があります。ログクラスはStandardを指定しました。Infrequent AccessクラスではLogs Insightsのクエリ機能に一部制約があるためです。

aws logs create-log-group \
  --log-group-name /aws/vendedlogs/s3/sal-cw-s3tables-demo \
  --region ap-northeast-1

aws logs put-retention-policy \
  --log-group-name /aws/vendedlogs/s3/sal-cw-s3tables-demo \
  --retention-in-days 30 \
  --region ap-northeast-1

IAMロール(S3 Tables連携用)

S3 Tablesへのログ配信には、CloudWatch LogsがAssumeRoleするためのIAMロールが必要です。

信頼ポリシーでは logs.amazonaws.com をプリンシパルとし、Conditionで対象アカウントとロググループを制限します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "logs.amazonaws.com"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "aws:SourceAccount": "123456789012"
        },
        "ArnLike": {
          "aws:SourceArn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/s3/sal-cw-s3tables-demo"
        }
      }
    }
  ]
}

権限ポリシーは logs:integrateWithS3Table アクションのみを許可します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "logs:integrateWithS3Table",
      "Resource": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/s3/sal-cw-s3tables-demo",
      "Condition": {
        "StringEquals": {
          "aws:ResourceAccount": "123456789012"
        }
      }
    }
  ]
}
aws iam create-role \
  --role-name CWLogsS3TableIntegrationRole-demo \
  --assume-role-policy-document file://trust-policy.json

aws iam put-role-policy \
  --role-name CWLogsS3TableIntegrationRole-demo \
  --policy-name CWLogsS3TableIntegrationPolicy \
  --policy-document file://permission-policy.json

従来のアクセスログ配信設定

従来のアクセスログ配信設定(比較用)

ログ配信先バケットに、S3ログ配信サービスからの書き込みを許可するバケットポリシーを設定します。

aws s3api put-bucket-policy \
  --bucket sal-cw-s3tables-demo-20260630-logs \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
        "Principal": {
          "Service": "logging.s3.amazonaws.com"
        },
        "Action": "s3:PutObject",
        "Resource": "arn:aws:s3:::sal-cw-s3tables-demo-20260630-logs/access-logs/*",
        "Condition": {
          "StringEquals": {
            "aws:SourceAccount": "123456789012"
          }
        }
      }
    ]
  }' \
  --region ap-northeast-1

ソースバケットのログ配信を有効化します。

aws s3api put-bucket-logging \
  --bucket sal-cw-s3tables-demo-20260630 \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "sal-cw-s3tables-demo-20260630-logs",
      "TargetPrefix": "access-logs/"
    }
  }' \
  --region ap-northeast-1

CloudWatch Logs配信設定

Delivery Source の作成

aws logs put-delivery-source \
  --name sal-demo-source \
  --resource-arn arn:aws:s3:::sal-cw-s3tables-demo-20260630 \
  --log-type S3_SERVER_ACCESS_LOGS \
  --region ap-northeast-1
{
    "deliverySource": {
        "name": "sal-demo-source",
        "arn": "arn:aws:logs:ap-northeast-1:123456789012:delivery-source:sal-demo-source",
        "resourceArns": [
            "arn:aws:s3:::sal-cw-s3tables-demo-20260630"
        ],
        "service": "s3",
        "logType": "S3_SERVER_ACCESS_LOGS"
    }
}

Delivery Destination の作成

aws logs put-delivery-destination \
  --name sal-demo-destination \
  --delivery-destination-configuration '{
    "destinationResourceArn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/s3/sal-cw-s3tables-demo"
  }' \
  --region ap-northeast-1
{
    "deliveryDestination": {
        "name": "sal-demo-destination",
        "arn": "arn:aws:logs:ap-northeast-1:123456789012:delivery-destination:sal-demo-destination",
        "deliveryDestinationType": "CWL",
        "deliveryDestinationConfiguration": {
            "destinationResourceArn": "arn:aws:logs:ap-northeast-1:123456789012:log-group:/aws/vendedlogs/s3/sal-cw-s3tables-demo"
        }
    }
}

Delivery の作成

ソースとデスティネーションをリンクすると、配信が開始されます。

aws logs create-delivery \
  --delivery-source-name sal-demo-source \
  --delivery-destination-arn arn:aws:logs:ap-northeast-1:123456789012:delivery-destination:sal-demo-destination \
  --region ap-northeast-1

レスポンスの recordFields には、配信対象となりうるフィールド一覧が含まれます。今回は29フィールドが定義されていました(リクエスト内容によって一部フィールドが未出力になる場合があります)。

{
    "delivery": {
        "id": "ishXsUfkyCuKMVtD",
        "arn": "arn:aws:logs:ap-northeast-1:123456789012:delivery:ishXsUfkyCuKMVtD",
        "deliverySourceName": "sal-demo-source",
        "deliveryDestinationArn": "arn:aws:logs:ap-northeast-1:123456789012:delivery-destination:sal-demo-destination",
        "deliveryDestinationType": "CWL",
        "recordFields": [
            "schema_version_id",
            "bucket_arn",
            "bucket_name",
            "request_time",
            "bucket_owner_id",
            "remote_ip",
            "requester",
            "request_id",
            "operation",
            "key_name",
            "request_uri",
            "http_status",
            "error_code",
            "bytes_sent_size",
            "object_size",
            "total_duration",
            "turn_around_duration",
            "referer",
            "user_agent",
            "version_id",
            "host_id",
            "signature_version",
            "cipher_suite",
            "authentication_type",
            "host_header",
            "tls_version",
            "access_point_arn",
            "acl_required",
            "source_region"
        ]
    }
}

S3 Tables連携の有効化

S3 Tables連携はアカウント・リージョン単位で作成し、S3アクセスログのデータソースを関連付けることで、対象のCloudWatch Logs配信ログがApache Iceberg形式の管理済みテーブルとしてS3 Tablesに格納されます。

aws observabilityadmin create-s3-table-integration \
  --role-arn arn:aws:iam::123456789012:role/CWLogsS3TableIntegrationRole-demo \
  --encryption '{"SseAlgorithm":"AES256"}' \
  --region ap-northeast-1
{
    "Arn": "arn:aws:observabilityadmin:ap-northeast-1:123456789012:s3tableintegration/evku8gpxr3pn42eqdfedhrvbr"
}

次に、S3アクセスログのデータソースを関連付けます。

aws logs associate-source-to-s3-table-integration \
  --integration-arn arn:aws:observabilityadmin:ap-northeast-1:123456789012:s3tableintegration/evku8gpxr3pn42eqdfedhrvbr \
  --data-source '{"name":"amazon_s3","type":"server_access"}' \
  --region ap-northeast-1
{
    "identifier": "24313a0f-2924-4b5f-a42c-eb8929a4f76a"
}

テストリクエストの発行

ログを生成するために、ソースバケットに対して以下の7つのコマンドを実行しました。

# PUT(3ファイル)
echo "test content 1" | aws s3 cp - s3://sal-cw-s3tables-demo-20260630/test/file1.txt
echo "test content 2" | aws s3 cp - s3://sal-cw-s3tables-demo-20260630/test/file2.txt
echo "test content 3" | aws s3 cp - s3://sal-cw-s3tables-demo-20260630/test/file3.txt

# GET
aws s3 cp s3://sal-cw-s3tables-demo-20260630/test/file1.txt /tmp/downloaded.txt

# LIST
aws s3 ls s3://sal-cw-s3tables-demo-20260630/test/

# HEAD
aws s3api head-object --bucket sal-cw-s3tables-demo-20260630 --key test/file2.txt

# DELETE
aws s3 rm s3://sal-cw-s3tables-demo-20260630/test/file3.txt

ログの確認

テストリクエスト発行から約50分後にCloudWatch Logsへログが到着しました。公式ドキュメントでは配信まで数時間かかる場合があるとされており、best-effort配信のためタイミングは保証されません。

CloudWatch Logsのログ形式

CloudWatch Logsに配信されたログは、1レコード1行の構造化JSONです。以下は到着した1レコードの例です。

{
  "schema_version_id": "V_1_0",
  "bucket_arn": "arn:aws:s3:::sal-cw-s3tables-demo-20260630",
  "bucket_name": "sal-cw-s3tables-demo-20260630",
  "request_time": "2026-06-30T01:31:22.000Z",
  "bucket_owner_id": "cd31ac5589...",
  "remote_ip": "133.201.xx.xx",
  "requester": "arn:aws:sts::123456789012:assumed-role/cm-user.name/cm-user.name",
  "request_id": "MWT6320RGF2TTJ98",
  "operation": "REST.PUT.LOGGING_STATUS",
  "request_uri": "PUT /?logging HTTP/1.1",
  "http_status": 200,
  "total_duration": 328,
  "user_agent": "aws-cli/2.35.12 ...",
  "signature_version": "SigV4",
  "cipher_suite": "TLS_AES_128_GCM_SHA256",
  "authentication_type": "AuthHeader",
  "host_header": "sal-cw-s3tables-demo-20260630.s3.ap-northeast-1.amazonaws.com",
  "tls_version": "TLSv1.3",
  "acl_required": false
}

requester フィールドにはAssumed RoleのARNがセッション名付きで記録されており、従来のS3バケット配信ではロール名までしか確認できなかったケースと比べて、アクセス元を追跡するための手がかりが増えています。

CloudWatch Logs Insights でのクエリ

各レコードがフラットな構造化JSONのため、フィールド名をそのまま指定してクエリできます。

オペレーション別リクエスト数

stats count(*) as request_count by operation
| sort request_count desc
operation request_count
REST.OPTIONS.PREFLIGHT 43
REST.GET.LOGGING_STATUS 11
REST.GET.NOTIFICATION 8
REST.GET.ACCELERATE 8
REST.GET.BUCKET_TAGS 7
REST.HEAD.BUCKET 7
REST.PUT.OBJECT 4
REST.HEAD.OBJECT 4
REST.GET.OBJECT 3
REST.DELETE.OBJECT 1

テストリクエスト7件に対し、AWS ConfigやAccess Analyzerなどのサービスによる確認リクエストやコンソール操作も含め、合計163レコードを確認しました(各表は集計時点・フィルタ条件が異なるため件数は完全には一致しません)。

エラーステータス分析

filter http_status >= 400
| stats count(*) as error_count by http_status, error_code, operation
| sort error_count desc
http_status error_code operation error_count
404 ReplicationConfigurationNotFoundError REST.GET.REPLICATION 6
404 NoSuchLifecycleConfiguration REST.GET.LIFECYCLE 4
404 NoSuchWebsiteConfiguration REST.GET.WEBSITE 3
404 NoSuchBucketPolicy REST.GET.BUCKETPOLICY 3
404 ObjectLockConfigurationNotFoundError REST.GET.OBJECT_LOCK_CONFIGURATION 3

今回確認された404は、Replication、Lifecycle、Websiteなど未設定の項目を確認した際の応答であり、この検証環境では想定内の結果です。エラーコードごとに集計できるため、実際の問題の切り分けが容易になります。

リクエスター別アクセス集計

stats count(*) as request_count by requester
| sort request_count desc
requester request_count
arn:aws:sts::123456789012:assumed-role/cm-user.name/cm-user.name 96
(記録なし) 44
arn:aws:sts::123456789012:assumed-role/AWSServiceRoleForConfig/AWSConfig-Describe 17
arn:aws:sts::123456789012:assumed-role/AWSServiceRoleForAccessAnalyzer/access-analyzer 4

Assumed Roleのセッション名まで記録されるため、IAMロールを共有している環境でもセッション名を手がかりにユーザーやサービスを追跡しやすくなります。

Athena (S3 Tables) でのクエリ

Athenaからはカタログ s3tablescatalog/aws-cloudwatch 経由でアクセスします。階層構造は以下のとおりです。

  • カタログ: s3tablescatalog/aws-cloudwatch
  • データベース: logs
  • テーブル: amazon_s3__server_access
SHOW TABLES IN logs
-- 結果: amazon_s3__server_access

オペレーション別リクエストミックス

SELECT operation, COUNT(*) as request_count
FROM amazon_s3__server_access
WHERE bucket_name = 'sal-cw-s3tables-demo-20260630'
GROUP BY operation
ORDER BY request_count DESC
LIMIT 10

CloudWatch Logs Insightsと同様の傾向が得られました。テーブル定義やパーティション設定を自分で管理する必要はありません。以下では、AthenaからSQLで分析する例を示します。

レイテンシ分析(p50/p95)

SELECT operation,
  COUNT(*) as cnt,
  approx_percentile(total_duration, 0.5) as p50_ms,
  approx_percentile(total_duration, 0.95) as p95_ms
FROM amazon_s3__server_access
WHERE bucket_name = 'sal-cw-s3tables-demo-20260630'
  AND total_duration IS NOT NULL
GROUP BY operation
HAVING COUNT(*) >= 3
ORDER BY p95_ms DESC
operation cnt p50_ms p95_ms
REST.GET.BUCKET_TAGS 7 25 67
REST.GET.LOGGING_STATUS 12 22 48
REST.PUT.OBJECT 4 41 42
REST.HEAD.OBJECT 4 29 35
REST.GET.OBJECT 3 26 31
REST.OPTIONS.PREFLIGHT 43 3 5

approx_percentile() で複数パーセンタイルを一度に算出でき、他テーブルとの結合も容易です。

ソースIP別アクセスパターン

SELECT remote_ip,
  COUNT(*) as request_count,
  COUNT(DISTINCT operation) as distinct_ops
FROM amazon_s3__server_access
WHERE bucket_name = 'sal-cw-s3tables-demo-20260630'
GROUP BY remote_ip
ORDER BY request_count DESC
remote_ip request_count distinct_ops
133.201.xx.xx 141 29
(null) 24 19

まとめ

S3サーバーアクセスログの新機能としてCloudWatch Logs配信が追加されたことで、従来よりもログ分析に取り組みやすくなりました。従来のS3バケット配信ではスペース区切りテキストが出力されるため、Athenaで分析するにはテーブル定義やパース設定の管理が必要でした。一方、CloudWatch Logs配信ではログが構造化JSONとして配信されるため、ログ到着後はCloudWatch Logs Insightsでフィールドを指定してクエリできます。さらにS3 Tables連携を設定することで、AthenaからApache Iceberg形式のテーブルとしてSQL分析できることも確認できました。

CloudTrailデータイベントとの使い分けは、目的に応じて考えるのがよさそうです。S3サーバーアクセスログには total_durationobject_sizebytes_sent_size など、アクセスパターンやレイテンシ分析に役立つフィールドがあります。一方、CloudTrailデータイベントはAPI操作の追跡や監査証跡としての利用に向いています。コスト面では、参照を含むすべてのデータイベントをCloudTrailで記録した場合、リクエスト数に応じた課金が高額になることがあります。配信の遅延が許容できるワークロードや、Athenaでのクエリ処理を多用する用途であれば、S3サーバーアクセスログ+S3 Tables連携の方がコスト効率に優れる場合があります。

S3のアクセスログ記録には、CloudTrailデータイベント、従来のS3バケット配信、今回追加されたCloudWatch Logs配信があります。CloudWatch Logs配信+S3 Tables連携は、構造化JSONをCloudWatch Logs Insightsで扱え、AthenaのSQL分析にもつなげられる新しい選択肢です。用途、遅延許容度、監査要件、コストを踏まえて使い分けるのがよさそうです。

参考リンク

https://aws.amazon.com/blogs/storage/query-amazon-s3-access-logs-instantly-with-cloudwatch-and-s3-tables/

https://docs.aws.amazon.com/AmazonS3/latest/userguide/sal-cw-enabling.html

https://docs.aws.amazon.com/AmazonS3/latest/userguide/sal-cw-querying-s3tables.html

この記事をシェアする

AWSのお困り事はクラスメソッドへ

関連記事