Skip to content

API Gateway Logging

Level: Error

Initial version: 0.1.3

cfn-lint: ES2000

tflint (REST): aws_apigateway_stage_logging_rule

tflint (HTTP): aws_apigatewayv2_stage_logging_rule

Amazon API Gateway can send logs to Amazon CloudWatch Logs and Amazon Kinesis Data Firehose for centralization.

Implementations for REST APIs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import { LogGroup } from '@aws-cdk/aws-logs';
import { LogGroupLogDestination, RestApi } from '@aws-cdk/aws-apigateway';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myLogGroup = new LogGroup(
      scope, 'MyLogGroup'
    );

    const myApi = new RestApi(
      scope, 'MyApi',
      {
        deployOptions: {
          // Setup logging for API Gateway
          accessLogDestination: new LogGroupLogDestination(myLogGroup),
          accessLogFormat: JSON.stringify({
            "stage" : "$context.stage",
            "request_id" : "$context.requestId",
            "api_id" : "$context.apiId",
            "resource_path" : "$context.resourcePath",
            "resource_id" : "$context.resourceId",
            "http_method" : "$context.httpMethod",
            "source_ip" : "$context.identity.sourceIp",
            "user-agent" : "$context.identity.userAgent",
            "account_id" : "$context.identity.accountId",
            "api_key" : "$context.identity.apiKey",
            "caller" : "$context.identity.caller",
            "user" : "$context.identity.user",
            "user_arn" : "$context.identity.userArn",
            "integration_latency": $context.integration.latency
          }),
        }
      }
    );
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "Resource": {
    "Type": "AWS::Serverless::Api",
    "Properties": {
      "DefinitionUri": "openapi.yaml",
      "StageName": "prod",

      // Setup logging for API Gateway
      "AccessLogSetting":{
        "DestinationArn": "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group",
        "Format": "{ \"stage\" : \"$context.stage\", \"request_id\" : \"$context.requestId\", \"api_id\" : \"$context.apiId\", \"resource_path\" : \"$context.resourcePath\", \"resource_id\" : \"$context.resourceId\", \"http_method\" : \"$context.httpMethod\", \"source_ip\" : \"$context.identity.sourceIp\", \"user-agent\" : \"$context.identity.userAgent\", \"account_id\" : \"$context.identity.accountId\", \"api_key\" : \"$context.identity.apiKey\", \"caller\" : \"$context.identity.caller\", \"user\" : \"$context.identity.user\", \"user_arn\" : \"$context.identity.userArn\", \"integration_latency\": $context.integration.latency }"
      }
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Resources:
  Api:
    Type: AWS::Serverless::Api
    Properties:
      DefinitionUri: openapi.yaml
      StageName: prod

      # Setup logging for API Gateway
      AccessLogSetting:
        DestinationArn: "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group"
        Format: |
          {
            "stage" : "$context.stage",
            "request_id" : "$context.requestId",
            "api_id" : "$context.apiId",
            "resource_path" : "$context.resourcePath",
            "resource_id" : "$context.resourceId",
            "http_method" : "$context.httpMethod",
            "source_ip" : "$context.identity.sourceIp",
            "user-agent" : "$context.identity.userAgent",
            "account_id" : "$context.identity.accountId",
            "api_key" : "$context.identity.apiKey",
            "caller" : "$context.identity.caller",
            "user" : "$context.identity.user",
            "user_arn" : "$context.identity.userArn",
            "integration_latency": $context.integration.latency
          }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
provider:
  name: aws
  logs:
    # Setup logging for API Gateway
    restApi:
      accessLogging: true
      format: |
        {
          "stage" : "$context.stage",
          "request_id" : "$context.requestId",
          "api_id" : "$context.apiId",
          "resource_path" : "$context.resourcePath",
          "resource_id" : "$context.resourceId",
          "http_method" : "$context.httpMethod",
          "source_ip" : "$context.identity.sourceIp",
          "user-agent" : "$context.identity.userAgent",
          "account_id" : "$context.identity.accountId",
          "api_key" : "$context.identity.apiKey",
          "caller" : "$context.identity.caller",
          "user" : "$context.identity.user",
          "user_arn" : "$context.identity.userArn",
          "integration_latency": $context.integration.latency
        }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
resource "aws_api_gateway_rest_api" "this" {
  body = file("openapi.yaml")
}

resource "aws_api_gateway_deployment" "this" {
  rest_api_id = aws_api_gateway_rest_api.this.id

  triggers = {
    redeployment = sha1(jsonencode(aws_api_gateway_rest_api.this.body))
  }

  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "this" {
  deployment_id = aws_api_gateway_deployment.this.id
  rest_api_id   = aws_api_gateway_rest_api.this.id
  stage_name    = "prod"

  # Setup logging for API Gateway
  access_log_settings {
    destination_arn = "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group"
    format = <<EOF
{
  "stage" : "$context.stage",
  "request_id" : "$context.requestId",
  "api_id" : "$context.apiId",
  "resource_path" : "$context.resourcePath",
  "resource_id" : "$context.resourceId",
  "http_method" : "$context.httpMethod",
  "source_ip" : "$context.identity.sourceIp",
  "user-agent" : "$context.identity.userAgent",
  "account_id" : "$context.identity.accountId",
  "api_key" : "$context.identity.apiKey",
  "caller" : "$context.identity.caller",
  "user" : "$context.identity.user",
  "user_arn" : "$context.identity.userArn",
  "integration_latency": $context.integration.latency
}
EOF
  }
}

Implementations for HTTP APIs

Remark: this is currently not supported in AWS CDK as an L2 construct at the moment. See this GitHub issue for more details.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import { CfnStage, HttpApi } from '@aws-cdk/aws-apigatewayv2';

export class MyStack extends cdk.Stack {
  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const myApi = new HttpApi(
      scope, 'MyApi'
    );

    // Setup logging for API Gateway using escape hatch.

    // See https://github.com/aws/aws-cdk/issues/11100 and
    // https://docs.aws.amazon.com/cdk/latest/guide/cfn_layer.html#cfn_layer_resource
    // for more information.
    const defaultStage = myApi.defaultStage.node.defaultChild as CfnStage;
    defaultStage.accessLogSettings = {
      destinationArn: '',
      format: JSON.stringify({
        "stage" : "$context.stage",
        "request_id" : "$context.requestId",
        "api_id" : "$context.apiId",
        "resource_path" : "$context.resourcePath",
        "resource_id" : "$context.resourceId",
        "http_method" : "$context.httpMethod",
        "source_ip" : "$context.identity.sourceIp",
        "user-agent" : "$context.identity.userAgent",
        "account_id" : "$context.identity.accountId",
        "api_key" : "$context.identity.apiKey",
        "caller" : "$context.identity.caller",
        "user" : "$context.identity.user",
        "user_arn" : "$context.identity.userArn",
        "integration_latency": $context.integration.latency
      }),
    };
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "Resource": {
    "Type": "AWS::Serverless::HttpApi",
    "Properties": {
      "DefinitionUri": "openapi.yaml",
      "StageName": "prod",

      // Setup logging for API Gateway
      "AccessLogSettings":{
        "DestinationArn": "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group",
        "Format": "{ \"stage\" : \"$context.stage\", \"request_id\" : \"$context.requestId\", \"api_id\" : \"$context.apiId\", \"resource_path\" : \"$context.resourcePath\", \"resource_id\" : \"$context.resourceId\", \"http_method\" : \"$context.httpMethod\", \"source_ip\" : \"$context.identity.sourceIp\", \"user-agent\" : \"$context.identity.userAgent\", \"account_id\" : \"$context.identity.accountId\", \"api_key\" : \"$context.identity.apiKey\", \"caller\" : \"$context.identity.caller\", \"user\" : \"$context.identity.user\", \"user_arn\" : \"$context.identity.userArn\", \"integration_latency\": $context.integration.latency }"
      }
    }
  }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Resources:
  Api:
    Type: AWS::Serverless::HttpApi
    Properties:
      DefinitionUri: openapi.yaml
      StageName: prod

      # Setup logging for API Gateway
      AccessLogSettings:
        DestinationArn: "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group"
        Format: |
          {
            "stage" : "$context.stage",
            "request_id" : "$context.requestId",
            "api_id" : "$context.apiId",
            "resource_path" : "$context.resourcePath",
            "resource_id" : "$context.resourceId",
            "http_method" : "$context.httpMethod",
            "source_ip" : "$context.identity.sourceIp",
            "user-agent" : "$context.identity.userAgent",
            "account_id" : "$context.identity.accountId",
            "api_key" : "$context.identity.apiKey",
            "caller" : "$context.identity.caller",
            "user" : "$context.identity.user",
            "user_arn" : "$context.identity.userArn",
            "integration_latency": $context.integration.latency
          }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
provider:
  name: aws
  logs:
    httpApi:
      format: |
        {
          "stage" : "$context.stage",
          "request_id" : "$context.requestId",
          "api_id" : "$context.apiId",
          "resource_path" : "$context.resourcePath",
          "resource_id" : "$context.resourceId",
          "http_method" : "$context.httpMethod",
          "source_ip" : "$context.identity.sourceIp",
          "user-agent" : "$context.identity.userAgent",
          "account_id" : "$context.identity.accountId",
          "api_key" : "$context.identity.apiKey",
          "caller" : "$context.identity.caller",
          "user" : "$context.identity.user",
          "user_arn" : "$context.identity.userArn",
          "integration_latency": $context.integration.latency
        }
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
resource "aws_apigatewayv2_api" "this" {
  name          = "my-api"
  protocol_type = "HTTP"
  body          = file("openapi.yaml") 
}

resource "aws_apigatewayv2_stage" "this" {
  api_id = aws_apigatewayv2_api.this.id
  name   = "prod"

  # Setup logging for API Gateway
  access_log_settings {
    destination_arn = "arn:aws:logs:eu-west-1:123456789012:log-group:my-log-group"
    format = <<EOF
{
  "stage" : "$context.stage",
  "request_id" : "$context.requestId",
  "api_id" : "$context.apiId",
  "resource_path" : "$context.resourcePath",
  "resource_id" : "$context.resourceId",
  "http_method" : "$context.httpMethod",
  "source_ip" : "$context.identity.sourceIp",
  "user-agent" : "$context.identity.userAgent",
  "account_id" : "$context.identity.accountId",
  "api_key" : "$context.identity.apiKey",
  "caller" : "$context.identity.caller",
  "user" : "$context.identity.user",
  "user_arn" : "$context.identity.userArn",
  "integration_latency": $context.integration.latency
}
EOF
  }
}

See also