API Gateway が CloudWatch Logs delivery をサポート。S3 と CloudWatch Logs への配信を試してみた

API Gateway が CloudWatch Logs delivery をサポート。S3 と CloudWatch Logs への配信を試してみた

CloudWatch Logs delivery の対応ログソースにAPI Gateway REST API の実行ログが加わりました。従来は1 KBを超えるログが切り詰められましたが、最大1 MBまで記録可能になりました。CloudWatch LogsやS3への配信に加え、Firehose連携、JSON・Parquet形式での保存もサポートしました。
2026.09.13

はじめに

2026年9月10日、API Gateway REST API の実行ログを CloudWatch Logs delivery 経由で配信できるようになりました。

https://aws.amazon.com/about-aws/whats-new/2026/09/amazon-api-gateway-1-mb-execution-logs/

自分の CloudWatch Logs ロググループ、S3 バケット、Firehose ストリームを配信先に指定できます。標準の実行ログはイベントが 1KB で切り詰められますが、配信ではイベントを最大 1MB まで記録できます。1MB を超えるペイロードは引き続き切り詰められます。

https://aws.amazon.com/blogs/compute/customize-amazon-api-gateway-destinations-for-execution-logs/

従来の実行ログと今回の配信を実際に構成し、同じリクエストのログがどう見えるかを比べました。

検証内容

最小構成の 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 として記録するログ容量が増加した場合は、従来方式よりコストが増加する可能性があるため、設定後に想定外のコストが発生していないか確認することをおすすめします。

この記事をシェアする

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

関連記事