メインコンテンツにスキップ

API GatewayのアクセスログをLogs Insightsで分析する

はじめに

API Gatewayの利用状況を把握するために、アクセスログを分析したいというニーズは非常に多いです。例えば、「時間帯別のリクエスト数を知りたい」「特定のエラーが多発しているエンドポイントを特定したい」といったケースです。

AWSでは、CloudWatch Logs Insightsを使うことで、ログデータを柔軟にクエリし、分析・可視化できます。そして、ログをJSON形式で出力しておくことで、この分析が格段に簡単です。

課題:デフォルトログ形式の不便さ

API Gatewayのデフォルトのアクセスログ形式は、そのままでは分析しにくいことがあります。特定のIPアドレスからのリクエスト数や、特定のエンドポイントへのアクセス数を集計したい場合、テキストベースのログではクエリが複雑になりがちです。

ログをJSON形式で出力することで、各フィールド(ip, status, resourcePathなど)がキーと値のペアとして構造化されるため、CloudWatch Logs Insightsでの分析が非常に容易になります。

SAMでのJSONアクセスログ設定

AWS SAMのtemplate.ymlでAPI Gateway (AWS::Serverless::Api) のアクセスログを設定するには、AccessLogSettingプロパティを使用します。

「Format must be a single line」エラーの回避

ここで最も重要なポイントは、Formatプロパティの指定方法です。YAMLファイル内で複数行にわたるJSON文字列をそのまま記述しようとすると、デプロイ時に「Format property must be a single line」というエラーが発生します。

これを回避するには、JSONオブジェクト全体をエスケープなどせず、1行の文字列として記述する必要があります。

template.yml の設定例

以下に、template.ymlの記述例を示します。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: API Gateway with JSON access logging

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Prod
      AccessLogSetting:
        DestinationArn: !GetAtt MyApiLogGroup.Arn
        # JSONオブジェクトを1行の文字列として記述する
        Format: '{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","caller":"$context.identity.caller","user":"$context.identity.user","requestTime":"$context.requestTime","httpMethod":"$context.httpMethod","resourcePath":"$context.resourcePath","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength"}'

  MyApiLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      # ロググループ名を明示的に指定
      LogGroupName: /aws/api-gateway/my-api-json-log
      RetentionInDays: 14

この設定により、API Gatewayへのすべてのアクセスが、指定したFormatに従ってJSON形式でCloudWatch Logsの/aws/api-gateway/my-api-json-logロググループに出力されます。

CloudWatch Logs Insightsでのクエリ実行

アクセスログがJSON形式で出力されるようになると、Logs Insightsでのクエリが非常にシンプルになります。ログの各フィールドは、クエリ内で直接参照できます。

1時間ごとのリクエスト数を集計する

fields @timestamp, status
| stats count(*) as requests by bin(1h)
| sort @timestamp asc

bin(1h)で1時間単位のバケットを作成し、その期間内のリクエスト数を集計しています。

このように出力します。

ステータスコード別のリクエスト数を集計する

stats count(*) as requestCount by status
| sort requestCount desc

アクセス元IPアドレスのトップ10を調べる

stats count(*) as requestCount by ip
| sort requestCount desc
| limit 10

CloudWatchダッシュボードでの可視化

Logs Insightsで実行したクエリの結果(グラフやテーブル)は、簡単にCloudWatchダッシュボードに追加できます。

クエリ実行後、**「ダッシュボードに追加」**ボタンをクリックし、既存または新規のダッシュボードにウィジェットとして保存します。これにより、APIの利用状況をリアルタイムで監視するダッシュボードを簡単に作成できます。