API Gateway が CloudWatch Logs delivery をサポート。S3 と CloudWatch Logs への配信を試してみた
はじめに
2026年9月10日、API Gateway REST API の実行ログを CloudWatch Logs delivery 経由で配信できるようになりました。
自分の CloudWatch Logs ロググループ、S3 バケット、Firehose ストリームを配信先に指定できます。標準の実行ログはイベントが 1KB で切り詰められますが、配信ではイベントを最大 1MB まで記録できます。1MB を超えるペイロードは引き続き切り詰められます。
従来の実行ログと今回の配信を実際に構成し、同じリクエストのログがどう見えるかを比べました。
検証内容
最小構成の REST API と Lambda を CloudFormation で作成し、実行ログを CloudWatch Logs と S3 の両方へ配信する設定を追加しました。配信を追加する前後で同じリクエストを実行し、自動管理ロググループと各配信先のログを確認しました。
検証環境
CloudFormation のプロパティ制約と、CLI が受け入れる値の違いが結果に影響するため、今回の環境を示します。
| 項目 | 値 |
|---|---|
| リージョン | ap-northeast-1 |
| AWS CLI | 2.36.44 |
| Lambda ランタイム | python3.13 |
| REST API | /echo(ANY・Lambda プロキシ統合・ステージ prod) |
| ステージ設定 | LoggingLevel: INFO / DataTraceEnabled: true |
最小構成の作成
実行ログを出力するには、アカウントとリージョンごとに cloudWatchRoleArn を設定しておく必要があります。今回の検証アカウントでは設定済みのロールを流用しました。未設定の場合は、apigateway.amazonaws.com を信頼するロールに AmazonAPIGatewayPushToCloudWatchLogs を付与します。付与したロールの ARN を aws apigateway update-account で設定します。この値はアカウントとリージョン全体で共有されるため、テンプレートには含めていません。
aws apigateway get-account --region ap-northeast-1
{
"cloudwatchRoleArn": "arn:aws:iam::123456789012:role/example-role",
"throttleSettings": {
"burstLimit": 5000,
"rateLimit": 10000.0
},
"features": [],
"apiKeyVersion": "3"
}
ステージでは LoggingLevel を INFO、DataTraceEnabled を true に設定しました。loggingLevel が INFO か ERROR でなければ、実行ログのイベント自体が発生しません。配信は、配信元の DeliverySource、宛先の DeliveryDestination、両者を結ぶ Delivery の3リソースで構成されます。次は、CloudWatch Logs の JSON 宛と S3 の JSON 宛を定義した抜粋です。
ApiStage:
Type: AWS::ApiGateway::Stage
Properties:
RestApiId: !Ref RestApi
DeploymentId: !Ref Deployment
StageName: !Ref StageName
MethodSettings:
- ResourcePath: "/*"
HttpMethod: "*"
LoggingLevel: INFO
DataTraceEnabled: true
DeliverySource:
Type: AWS::Logs::DeliverySource
Condition: WithDelivery
DependsOn: ApiStage
Properties:
Name: !Sub "${StackPrefix}-source"
LogType: EXECUTION_LOGS
ResourceArn: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:${AWS::AccountId}:/restapis/${RestApi}/stages/${StageName}"
DestinationCwlJson:
Type: AWS::Logs::DeliveryDestination
Condition: WithDelivery
Properties:
Name: !Sub "${StackPrefix}-cwl-json"
DestinationResourceArn: !GetAtt DeliveryLogGroupJson.Arn
OutputFormat: json
DeliveryCwlJson:
Type: AWS::Logs::Delivery
Condition: WithDelivery
Properties:
DeliverySourceName: !Ref DeliverySource
DeliveryDestinationArn: !GetAtt DestinationCwlJson.Arn
DestinationS3Json:
Type: AWS::Logs::DeliveryDestination
Condition: WithDelivery
DependsOn: DeliveryBucketPolicy
Properties:
Name: !Sub "${StackPrefix}-s3-json"
DestinationResourceArn: !GetAtt DeliveryBucket.Arn
OutputFormat: json
DeliveryS3Json:
Type: AWS::Logs::Delivery
Condition: WithDelivery
Properties:
DeliverySourceName: !Ref DeliverySource
DeliveryDestinationArn: !GetAtt DestinationS3Json.Arn
まず EnableLogDelivery を false にしてデプロイし、標準の実行ログだけを有効にしました。テンプレートは名前付き IAM ロールを作成するため、CAPABILITY_NAMED_IAM を指定します。
aws cloudformation deploy \
--template-file apigw-exec-logs-demo.yaml \
--stack-name apigw-exec-logs-demo \
--parameter-overrides EnableLogDelivery=false \
--capabilities CAPABILITY_NAMED_IAM \
--region ap-northeast-1
aws cloudformation describe-stacks \
--stack-name apigw-exec-logs-demo \
--region ap-northeast-1 \
--query 'Stacks[0].Outputs'
CloudFormation テンプレート全文と制約
AWSTemplateFormatVersion: "2010-09-09"
Description: >-
API Gateway REST API execution logs - CloudWatch Logs delivery (Vended Logs) verification stack.
Deploy with EnableLogDelivery=false first (standard execution logging only),
then redeploy with EnableLogDelivery=true to add the deliveries.
Parameters:
StackPrefix:
Type: String
Default: apigw-exec-logs-demo
Description: Prefix used for resource names.
StageName:
Type: String
Default: prod
EnableLogDelivery:
Type: String
Default: "false"
AllowedValues: ["true", "false"]
Description: >-
false = standard execution logging only (API Gateway managed log group).
true = create delivery source / destinations / deliveries.
LogRetentionInDays:
Type: Number
Default: 30
Conditions:
WithDelivery: !Equals [!Ref EnableLogDelivery, "true"]
Resources:
# ---------------------------------------------------------------
# NOTE: the account-level CloudWatch role for API Gateway
# (AWS::ApiGateway::Account / cloudWatchRoleArn) is intentionally NOT in this
# template. It is a single account/Region-wide setting shared by every REST
# API, so managing it here could overwrite or delete another API's setting.
# Configure it outside CloudFormation beforehand; see verification-procedure.md
# step 1 ("account level CloudWatch role").
# ---------------------------------------------------------------
# ---------------------------------------------------------------
# Lambda (echo backend)
# ---------------------------------------------------------------
FunctionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub "${StackPrefix}-lambda-role"
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
FunctionLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub "/aws/lambda/${StackPrefix}-fn"
RetentionInDays: !Ref LogRetentionInDays
Function:
Type: AWS::Lambda::Function
DependsOn: FunctionLogGroup
Properties:
FunctionName: !Sub "${StackPrefix}-fn"
Runtime: python3.13
Handler: index.lambda_handler
Role: !GetAtt FunctionRole.Arn
Timeout: 10
Code:
ZipFile: |
import json
def lambda_handler(event, context):
body = event.get("body") or ""
response = {
"path": event.get("path"),
"httpMethod": event.get("httpMethod"),
"queryStringParameters": event.get("queryStringParameters"),
"receivedBodyBytes": len(body.encode("utf-8")),
"echo": body[:200],
}
return {
"statusCode": 200,
"headers": {"content-type": "application/json"},
"body": json.dumps(response),
}
FunctionPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt Function.Arn
Action: lambda:InvokeFunction
Principal: apigateway.amazonaws.com
SourceArn: !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/*/*/*"
# ---------------------------------------------------------------
# REST API (minimal: /echo -> Lambda proxy)
# ---------------------------------------------------------------
RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Ref StackPrefix
Description: Execution log delivery verification API
EndpointConfiguration:
Types: [REGIONAL]
EchoResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
PathPart: echo
EchoMethod:
Type: AWS::ApiGateway::Method
Properties:
RestApiId: !Ref RestApi
ResourceId: !Ref EchoResource
HttpMethod: ANY
# Verification-only endpoint: no authorizer. Do not copy this to production.
AuthorizationType: NONE
Integration:
Type: AWS_PROXY
IntegrationHttpMethod: POST
Uri: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Function.Arn}/invocations"
Deployment:
Type: AWS::ApiGateway::Deployment
DependsOn: EchoMethod
Properties:
RestApiId: !Ref RestApi
ApiStage:
Type: AWS::ApiGateway::Stage
Properties:
RestApiId: !Ref RestApi
DeploymentId: !Ref Deployment
StageName: !Ref StageName
MethodSettings:
- ResourcePath: "/*"
HttpMethod: "*"
LoggingLevel: INFO
DataTraceEnabled: true
# ---------------------------------------------------------------
# Delivery destinations (conditional)
# ---------------------------------------------------------------
DeliveryLogGroupJson:
Type: AWS::Logs::LogGroup
Condition: WithDelivery
Properties:
LogGroupName: !Sub "/aws/vendedlogs/apigateway/${StackPrefix}/${StageName}"
RetentionInDays: !Ref LogRetentionInDays
DeliveryLogGroupPlain:
Type: AWS::Logs::LogGroup
Condition: WithDelivery
Properties:
LogGroupName: !Sub "/aws/vendedlogs/apigateway/${StackPrefix}/${StageName}-plain"
RetentionInDays: !Ref LogRetentionInDays
DeliveryBucket:
Type: AWS::S3::Bucket
Condition: WithDelivery
Properties:
BucketName: !Sub "${StackPrefix}-${AWS::AccountId}-${AWS::Region}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
# SSE-S3 (Amazon S3 managed keys). No KMS key is used: SSE-S3 needs no
# extra configuration and adds no key charges.
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
# Bucket policy per "Logs sent to Amazon S3" (V2 permissions):
# https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/AWS-logs-infrastructure-V2-S3.html
DeliveryBucketPolicy:
Type: AWS::S3::BucketPolicy
Condition: WithDelivery
Properties:
Bucket: !Ref DeliveryBucket
PolicyDocument:
Version: "2012-10-17"
Id: AWSLogDeliveryWrite20150319
Statement:
- Sid: AWSLogDeliveryWrite
Effect: Allow
Principal:
Service: delivery.logs.amazonaws.com
Action: s3:PutObject
Resource: !Sub "${DeliveryBucket.Arn}/AWSLogs/${AWS::AccountId}/*"
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
aws:SourceAccount: !Ref AWS::AccountId
ArnLike:
aws:SourceArn: !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:delivery-source:*"
# ---------------------------------------------------------------
# Vended Logs delivery (conditional)
# ---------------------------------------------------------------
DeliverySource:
Type: AWS::Logs::DeliverySource
Condition: WithDelivery
# PutDeliverySource fails unless execution logging is already enabled on the
# stage, so the stage must exist with LoggingLevel INFO/ERROR first.
DependsOn: ApiStage
Properties:
Name: !Sub "${StackPrefix}-source"
LogType: EXECUTION_LOGS
ResourceArn: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:${AWS::AccountId}:/restapis/${RestApi}/stages/${StageName}"
# (1) own CloudWatch Logs log group, structured JSON (all record fields)
DestinationCwlJson:
Type: AWS::Logs::DeliveryDestination
Condition: WithDelivery
Properties:
Name: !Sub "${StackPrefix}-cwl-json"
DestinationResourceArn: !GetAtt DeliveryLogGroupJson.Arn
OutputFormat: json
DeliveryCwlJson:
Type: AWS::Logs::Delivery
Condition: WithDelivery
Properties:
DeliverySourceName: !Ref DeliverySource
DeliveryDestinationArn: !GetAtt DestinationCwlJson.Arn
# (2) S3 bucket, JSON
DestinationS3Json:
Type: AWS::Logs::DeliveryDestination
Condition: WithDelivery
DependsOn: DeliveryBucketPolicy
Properties:
Name: !Sub "${StackPrefix}-s3-json"
DestinationResourceArn: !GetAtt DeliveryBucket.Arn
OutputFormat: json
DeliveryS3Json:
Type: AWS::Logs::Delivery
Condition: WithDelivery
Properties:
DeliverySourceName: !Ref DeliverySource
DeliveryDestinationArn: !GetAtt DestinationS3Json.Arn
# (3) own log group, payload only (equivalent to standard execution log text).
# The CLI guidance uses --field-delimiter "" (empty string), but the
# CloudFormation property has a minimum length of 1, so it is omitted here.
# Verify whether RecordFields: [payload] alone reproduces the plain output.
DestinationCwlPlain:
Type: AWS::Logs::DeliveryDestination
Condition: WithDelivery
Properties:
Name: !Sub "${StackPrefix}-cwl-plain"
DestinationResourceArn: !GetAtt DeliveryLogGroupPlain.Arn
OutputFormat: plain
DeliveryCwlPlain:
Type: AWS::Logs::Delivery
Condition: WithDelivery
Properties:
DeliverySourceName: !Ref DeliverySource
DeliveryDestinationArn: !GetAtt DestinationCwlPlain.Arn
RecordFields:
- payload
Outputs:
ApiInvokeUrl:
Value: !Sub "https://${RestApi}.execute-api.${AWS::Region}.amazonaws.com/${StageName}"
RestApiId:
Value: !Ref RestApi
StageArn:
Value: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:${AWS::AccountId}:/restapis/${RestApi}/stages/${StageName}"
AutoManagedLogGroupName:
Description: API Gateway managed log group (not managed by this stack; delete manually at teardown)
Value: !Sub "API-Gateway-Execution-Logs_${RestApi}/${StageName}"
DeliveryLogGroupJsonName:
Condition: WithDelivery
Value: !Ref DeliveryLogGroupJson
DeliveryLogGroupPlainName:
Condition: WithDelivery
Value: !Ref DeliveryLogGroupPlain
DeliveryBucketName:
Condition: WithDelivery
Value: !Ref DeliveryBucket
DeliverySource は ApiStage の作成後に作る必要があります。DependsOn を外したテンプレートでは、DeliverySource が CREATE_FAILED になりました。エラーは Invalid stage identifier specified でした。ステージで実行ログを有効にしていない状態では、配信元を登録できません。
S3 宛の DestinationS3Json は、バケットポリシーの適用後に作成します。配信先のロググループ名を /aws/vendedlogs/ 配下にする必要はありません。/apigw-exec-logs-demo/v4-prefix-test を宛先にした put-delivery-destination と create-delivery は成功しました。RecordFields を省略した場合は、次の7フィールドが既定で選ばれます。
{
"delivery": {
"id": "<delivery-id>",
"deliverySourceName": "apigw-exec-logs-demo-source",
"deliveryDestinationType": "CWL",
"recordFields": [
"resource_arn",
"event_timestamp",
"api_id",
"stage",
"resource_path",
"http_method",
"payload"
]
}
}
配信前の実行ログ
配信前の自動管理ロググループで切り詰めを確認するため、GET、本文が小さい POST、4KB の POST、存在しないパスへの GET を実行しました。前の3本は200を返し、存在しないパスは403を返しました。4KB の本文は、40文字のダミー値を55件並べる次のスクリプトで生成しました。
#!/usr/bin/env bash
# Generate a ~4KB JSON request body used to check the 1 KB truncation of
# standard execution logging. Dummy values only (no PII, no real domains).
#
# bash artifacts/make-payload-4kb.sh > artifacts/payload-4kb.json
# wc -c artifacts/payload-4kb.json
set -euo pipefail
n=${1:-55}
printf '{"note":"execution log truncation check","items":['
for i in $(seq 1 "$n"); do
[ "$i" -gt 1 ] && printf ','
printf '{"id":%d,"key":"item-%03d","value":"%s"}' \
"$i" "$i" "aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd"
done
printf ']}\n'
bash make-payload-4kb.sh > payload-4kb.json
BASE=https://<rest-api-id>.execute-api.ap-northeast-1.amazonaws.com/prod
curl -s -i "$BASE/echo?name=kiro"
curl -s -i -X POST "$BASE/echo" -H 'content-type: application/json' -d '{"msg":"small"}'
curl -s -i -X POST "$BASE/echo" -H 'content-type: application/json' --data-binary @payload-4kb.json
curl -s -i "$BASE/notfound"
4,333 bytes の JSON を本文に送ったリクエストでは、Method request body before transformations のイベントは1,036 bytesでした。末尾には [TRUNCATED] が付きました。Endpoint request headers と Endpoint request body after transformations でも、同じ1,036 bytesに切り詰められました。
(05dd036b-14c9-4207-aa4f-0875c1a5c39a) Method request body before transformations: {"note":"execution log truncation check","items":[{"id":1,"key":"item-001","value":"aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd"},{"id":2,"key":"item-002","value":"aaaaaaaaaabbbbbb
(中略)
aaabbbbbbbbbbccccccccccdddddddddd"},{"id":12,"key":"item-012","value":"aaaaaaa [TRUNCATED]
配信の追加
同じテンプレートで EnableLogDelivery=true を指定して再デプロイしました。ロググループ2本、S3 バケット、DeliverySource 1本、DeliveryDestination 3本、Delivery 3本が追加されました。
aws cloudformation deploy \
--template-file apigw-exec-logs-demo.yaml \
--stack-name apigw-exec-logs-demo \
--parameter-overrides EnableLogDelivery=true \
--capabilities CAPABILITY_NAMED_IAM \
--region ap-northeast-1
aws logs describe-deliveries --region ap-northeast-1
| delivery ID | 配信先 | OutputFormat | recordFields |
|---|---|---|---|
| <delivery-id-1> | 自前のロググループ | json | 未指定(既定の7フィールド) |
| <delivery-id-2> | 自前のロググループ(plain 用) | plain | payload |
| <delivery-id-3> | S3 バケット | json | 未指定(既定の7フィールド) |
1つの DeliverySource から、JSON 形式の CloudWatch Logs と S3、plain 形式の CloudWatch Logs へ並行して配信されました。
配信先での見え方
4KB の本文を送った POST リクエストでは、JSON 形式の CloudWatch Logs 宛に構造化されたレコードが届きました。イベント全体は 5,198 bytes で、payload に [TRUNCATED] は付きませんでした。
{
"resource_arn": "arn:aws:apigateway:ap-northeast-1:123456789012:/restapis/<rest-api-id>/stages/prod",
"event_timestamp": 1789278878697,
"api_id": "<rest-api-id>",
"stage": "prod",
"resource_path": "/echo",
"http_method": "POST",
"payload": "(783aca98-1241-4a3a-a73c-b996b19046df) Method request body before transformations: {\"note\":\"execution log truncation check\",\"items\":[{\"id\":1,\"key\":\"item-001\",\"value\":\"aaaaaaaaaabbbbbbbbbbccccccccccdddddddddd\"},(中略)]}"
}
RecordFields に payload だけを指定し、plain 形式を選んだ配信では、従来の実行ログと同じテキストだけが1イベントとして届きました。既存のテキスト形式を前提としたパーサを使う場合にも、この形式を選択できます。
(dc8caeee-4a2c-4829-accd-576372439c69) Starting execution for request: dc8caeee-4a2c-4829-accd-576372439c69
(dc8caeee-4a2c-4829-accd-576372439c69) HTTP Method: GET, Resource Path: /echo
(dc8caeee-4a2c-4829-accd-576372439c69) Method request query string: {name=kiro}
同じリクエストの Method request body before transformations を、配信前後で比較しました。
| 記録先 | イベントのバイト数 | 切り詰め |
|---|---|---|
| 自動管理ロググループ(配信前) | 1,036 | あり([TRUNCATED]) |
| 自前のロググループ(json 配信) | 5,198 | なし |
| 自前のロググループ(plain 配信・payload のみ) | 4,416 | なし |
今回の 4KB リクエストでは、JSON と plain のどちらの配信でも本文は切り詰められませんでした。出力フォーマットは宛先ごとに選べます。CloudWatch Logs 宛は json / plain、S3 宛は json / plain / w3c / parquet、Firehose 宛は json / plain です。詳細はログ配信の公式ドキュメントを参照してください。
JSON 配信のロググループでは、配信で付与されたフィールドを名前で指定して検索できました。1本目のクエリは20行を返し、2本目のクエリは4KBのPOST、小さいPOST、GETの3行を返しました。
fields @timestamp, api_id, stage, http_method, resource_path, @ingestionTime
| sort @timestamp desc
| limit 20
filter payload like /Method request body/
| fields @timestamp, resource_path, http_method, payload
| sort @timestamp desc
| limit 5
| @timestamp | resource_path | http_method |
|---|---|---|
| 2026-09-13 05:54:38.697 | /echo | POST |
| 2026-09-13 05:54:38.610 | /echo | POST |
| 2026-09-13 05:54:38.327 | /echo | GET |
S3 では、オブジェクトが AWSLogs/{アカウントID}/APIGateway/ExecutionLogs/{リージョン}/{REST API ID}/{ステージ}/ の階層に保存されました。ファイル名には日時が入り、gzip 圧縮された 6,909 bytes のオブジェクトに57レコードが入っていました。
AWSLogs/123456789012/APIGateway/ExecutionLogs/ap-northeast-1/<rest-api-id>/prod/2026-09-13-05.APIGateway_<rest-api-id>_prod_2026-09-13-05_c4120841.log.gz
展開後のファイルは1行1レコードの JSON で、フィールドは CloudWatch Logs 宛の JSON 配信と同じでした。
{"resource_arn":"arn:aws:apigateway:ap-northeast-1:123456789012:/restapis/<rest-api-id>/stages/prod","event_timestamp":1789278878606,"api_id":"<rest-api-id>","stage":"prod","resource_path":"/echo","http_method":"POST","payload":"(012d5149-bf27-4d3e-92c6-ba0e45aa3711) Extended Request Id: Dn4Y3HADNjMEKZA="}
curl を実行した時刻(2026-09-13T05:54:38Z)を起点として、各配信先の初回到着時刻も確認しました。
| 配信先 | 到着時刻の判定に用いた値 | 到着時刻 | 起点からの時間 |
|---|---|---|---|
| 自前のロググループ(json) | @ingestionTime |
2026-09-13T05:54:52Z | 約14秒 |
| S3 バケット | オブジェクトの LastModified |
2026-09-13T05:59:09Z | 約4分31秒 |
今回のリクエストでは、CloudWatch Logs 宛が先に到着しました。なお、実行ログの配信は best-effort で、ログが届かない場合もあると公式ドキュメントに明記されています。
自動管理ロググループへの記録の停止
配信を作成した後に実行した4本のリクエストについて、curl を実行した時刻以降で自動管理ロググループを絞って確認しました。
aws logs filter-log-events \
--log-group-name "API-Gateway-Execution-Logs_<rest-api-id>/prod" \
--start-time 1789278878000 \
--region ap-northeast-1
{
"events": [],
"searchedLogStreams": []
}
該当するイベントは0件でした。CloudWatch Logs delivery を設定すると、API Gateway は自動管理ロググループへの書き込みを停止します。既存のアラームやサブスクリプションフィルタが自動管理ロググループを参照している場合は、切り替え前に移行するか、同じロググループを配信先に含めます。
料金
CloudWatch Logs delivery の配信は、月間データ量に応じた段階制の Vended Logs 料金です。次の単価はCloudWatch の料金ページの北バージニアの計算例にある値です。ティアは毎月リセットされます。
| 月間データ量 | 単価 |
|---|---|
| 0〜10TB | $0.50/GB |
| 10〜30TB | $0.25/GB |
| 30〜50TB | $0.10/GB |
| 50TB超 | $0.05/GB |
表の金額は配信料金です。宛先が S3 でも配信課金は発生し、請求には <リージョン>-S3-Egress-Bytes として現れます。S3 のストレージ料金と、Apache Parquet など任意のフォーマット変換の料金は、表の金額に含みません。Firehose 宛では Firehose の取り込み料金も別途かかります。
従来の自動管理ロググループは、標準の CloudWatch Logs 取り込み課金です。同じ料金ページの北バージニア・Standard クラスの計算例では、取り込みが $0.50/GB、保存が $0.03/GB でした。Vended Logs の第1ティアも $0.50/GB で、単価は同水準です。
費用差は、課金対象となるログの総バイト数と宛先側の保存費用で生じます。自動管理ロググループではイベントが 1KB で切り詰められますが、配信では最大 1MB まで記録されます。今回の 4KB リクエストでは、自動管理ロググループの 1,036 bytes に対し、JSON 配信は 5,198 bytes でした。
CloudWatch Logs delivery では Vended Logs の料金体系が適用されるため、従来方式とは単価やティアが異なる可能性があります。また、ログの切り詰め上限が 1 KB から 1 MB に引き上げられ、JSON 形式で出力するとレコードサイズも増えるため、ログ量が増加する可能性があります。多くのログが発生する環境では、想定どおりのコストで利用できるか確認することをおすすめします。
まとめ
CloudWatch Logs delivery により、従来は1KBで切り詰められ、出力先も CloudWatch Logs に限られていた API Gateway REST API の実行ログを、最大1MBまで記録し、S3や Firehose へ配信したり、JSONや Parquet 形式で保存したりできるようになりました。
JSON形式で保存すると、CloudWatch Logs ではフィールドを指定した Logs Insights の検索や、そのクエリ結果に基づく Log Based Alarm を活用でき、S3では Athena などによる分析もしやすくなります。
ただし、Vended Logs として記録するログ容量が増加した場合は、従来方式よりコストが増加する可能性があるため、設定後に想定外のコストが発生していないか確認することをおすすめします。







