こんにちは。クラウドソリューション事業部の石垣です。
最近、古いS3バケットのサーバーアクセスログ(いわゆるS3アクセスログ)を調査する機会があって、ちょっと大変でした。
今(2026年7月現在)、設定するならばどういう設定にしておくのがいいか改めて、調査・確認しましたので共有します。
S3サーバーアクセスログ
まず従来型のS3サーバーアクセスログについて。
2023年11月の時点でログオブジェクトキーの形式として、日付ベースのパーティショニングに対応しました。
- 従来まで
- [DestinationPrefix][YYYY]-[MM]-[DD]-[hh]-[mm]-[ss]-[UniqueString]
- 選択できるようになった形式
- [DestinationPrefix][SourceAccountId]/[SourceRegion]/[SourceBucket]/[YYYY]/[MM]/[DD]/[YYYY]-[MM]-[DD]-[hh]-[mm]-[ss]-[UniqueString]
Athena等で分析用にクエリを流す際に、パーティション設定をすることで全件スキャンではなく特定の日付([YYYY]/[MM]/[DD])のログに対してクエリを実行できるようになりました。
スキャンデータ量を制限できることで、スキャンデータ量依存のコストを低減し、また実行時間も短縮させることができました。
このアップデートの何が嬉しかったかの検証blogは以下となります。
https://www.seeds-std.co.jp/blog/creators/s3accesslog-objectkey/
CloudWatch へのログ配信
2026年6月のアップデートとして、CloudWatchへのログ配信の機能が追加されました。
AWS の新機能についてさらに詳しく知るには、 Amazon S3 サーバーアクセスログが Amazon CloudWatch Logs と Amazon S3 Tables への配信に対応
CloudWatch Logsだけでなく、S3(汎用バケット)やData Firehoseも配信先として選択できます。これはCloudWatch Logsそのものではなく、CloudWatchのログ配信基盤(CloudWatch Log Delivery)を利用しているためです。CloudFrontのv2ログと同じ形ですね。
1S3
2 ↓
3CloudWatch Log Delivery
4 ├─ CloudWatch Logs
5 ├─ S3
6 └─ FirehoseS3への配信
S3への配信も出力形式としてjsonとparquetが選択できるようなっています。
- 従来の形式
- 出力形式 json の場合
CloudWatch Logsへの配信
CloudWatch Logsへの配信では、そのままS3 Tablesへのミラーリングを配信設定と一括でできるようでした。
その上で、S3 Tablesのバケットがない場合は新規で作成されて、その中にテーブルが作成されるようです。
CloudWatch上はどこに設定があるかわかりにくいですが、「設定>Global>S3テーブル統合」です。
CloudWatchとS3 Tablesとの統合は以下参照。
CloudWatch Logs Amazon S3 Tables Integration は、ログデータの高度な分析を可能にします。この統合により、CloudWatch Logs が S3 Tables に接続されます。複雑なインフラストラクチャを管理することなく、強力なクエリエンジンにアクセスできます。
試しに手元のDuckDBでクエリしてみます。
参考:
Support for S3 Tables is currently experimental. The iceberg extension supports reading Iceberg tables stored in Amazon S3 Tables. Requirements Install the following extensions: INSTALL aws; INSTALL httpfs; INSTALL iceberg; Connecting to Amazon S3 Tables You can let DuckDB detect your AWS credentials and configuration based on the default profile in your ~/.aws directory by creating the following secret using the Secrets Manager: CREATE SECRET ( TYPE s3, PROVIDER credential_chain ); Alternatively, you can set the values manually: CREATE SECRET ( TYPE s3, KEY_ID 'key_id', SECRET 'secret', REGION 'region' ); For the full range of credential options (assumed roles, SSO,…

接続
1INSTALL aws;
2INSTALL httpfs;
3INSTALL iceberg;
4CREATE SECRET (
5 TYPE S3,
6 PROVIDER credential_chain
7);
8
9ATTACH 'arn:aws:s3tables:ap-northeast-1:xxxxxxx:bucket/aws-cloudwatch(S3テーブルバケットのARN)' AS s3_tables_db (
10 TYPE iceberg,
11 ENDPOINT_TYPE s3_tables
12);
13
14SHOW ALL TABLES;1┌─────────┐
2│ Success │
3│ boolean │
4├─────────┤
5│ true │
6└─────────┘
7┌──────────────┬─────────┬──────────────────────────┬──────────────┬──────────────┬───────────┐
8│ database │ schema │ name │ column_names │ column_types │ temporary │
9│ varchar │ varchar │ varchar │ varchar[] │ varchar[] │ boolean │
10├──────────────┼─────────┼──────────────────────────┼──────────────┼──────────────┼───────────┤
11│ s3_tables_db │ logs │ amazon_s3__server_access │ [__] │ [INTEGER] │ false │
12└──────────────┴─────────┴──────────────────────────┴──────────────┴──────────────┴───────────┘テーブル見えてそうですね。テーブルが見えたので、実際にクエリを実行してみます。
基本的なデータ確認
1SELECT
2 request_time,
3 operation,
4 http_status,
5 bucket_name,
6 key_name,
7 remote_ip,
8 bytes_sent_size,
9 user_agent
10FROM s3_tables_db.logs.amazon_s3__server_access
11LIMIT 3;1┌──────────────────────────┬─────────────────────────────┬─────────────┬──────────────────┬──────────┬─────────────┬─────────────────┬────────────────────────────────────────────────────────────────┐
2│ request_time │ operation │ http_status │ bucket_name │ key_name │ remote_ip │ bytes_sent_size │ user_agent │
3│ timestamp with time zone │ varchar │ int64 │ varchar │ varchar │ varchar │ int64 │ varchar │
4├──────────────────────────┼─────────────────────────────┼─────────────┼──────────────────┼──────────┼─────────────┼─────────────────┼────────────────────────────────────────────────────────────────┤
5│ 2026-07-09 13:44:58+09 │ REST.GET.ENCRYPTION │ 200 │ my-bucket │ NULL │ NULL │ 407 │ NULL │
6│ 2026-07-09 13:44:58+09 │ REST.GET.LOGGING_STATUS │ 200 │ my-bucket │ NULL │ NULL │ 316 │ NULL │
7│ 2026-07-08 18:29:28+09 │ REST.GET.OWNERSHIP_CONTROLS │ 200 │ my-bucket │ NULL │ 192.0.2.100 │ 193 │ Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ... │
8└──────────────────────────┴─────────────────────────────┴─────────────┴──────────────────┴──────────┴─────────────┴─────────────────┴────────────────────────────────────────────────────────────────┘従来の形式と異なり、構造化されたデータとして各フィールドが分離されているため、扱いやすくなっています。
オペレーション別のエラー率
どのオペレーションでエラーが発生しているかを確認します。
1SELECT
2 operation,
3 COUNT(*) as count,
4 COUNT(CASE WHEN http_status >= 400 THEN 1 END) as errors,
5 ROUND(COUNT(CASE WHEN http_status >= 400 THEN 1 END) * 100.0 / COUNT(*), 2) as error_rate
6FROM s3_tables_db.logs.amazon_s3__server_access
7GROUP BY operation
8HAVING errors > 0
9ORDER BY error_rate DESC;1┌────────────────────────────────────┬───────┬────────┬────────────┐
2│ operation │ count │ errors │ error_rate │
3│ varchar │ int64 │ int64 │ double │
4├────────────────────────────────────┼───────┼────────┼────────────┤
5│ REST.GET.CORS │ 1 │ 1 │ 100.0 │
6│ REST.GET.WEBSITE │ 1 │ 1 │ 100.0 │
7│ REST.GET.OBJECT_LOCK_CONFIGURATION │ 4 │ 4 │ 100.0 │
8│ REST.GET.BUCKETPOLICY │ 1 │ 1 │ 100.0 │
9└────────────────────────────────────┴───────┴────────┴────────────┘
10
11この例では、設定されていない機能(CORS、Website、Object Lock、Bucket Policy)へのアクセスが404エラーとなっています。
普通に使用できそうですね。
まとめ
S3のサーバーアクセスログを改めて整理してみました。
2026年7月現在の今設定するならば、CloudWatch Log Deliveryを利用してログを配信するのがいいかと思います。
個人的には、Athenaなどで後から分析するだけであれば、S3(Parquet + Hive互換)への配信が最もシンプルだと感じました。
一方、リアルタイム分析やLogs Insightsを利用したい場合はCloudWatch Logs、既存のIceberg基盤へ直接取り込みたい場合はData Firehoseと、用途に応じて選択するとよさそうです。
