How to enable access logging on API Gateway with SAM

Create a Log Group

To be able to read the access logs for your API, you must first create a dedicated log group on CloudWatch.

Navigate to the CloudWatch console and under Logs, select Log Groups. Click on Create Log Group on the top right of the page. This will prompt you to create a new log group. Give it a name, choose a retention period and click create.

Save the log group to SSM

Now that we have a new log group, the next step is going to be to save its arn to the AWS Systems Manager Parameter Store. Navigate to the page from your console, and click Parameter Store (found under Application Management) on the left sidebar. There, you will have to click on Create New Parameter. Much like the previous step, give it a name, a type and an optional description. Under value, you want to paste the arn of the log group. Click save and you should be good to go.

Update your API Gateway definition

Now that we have the parameter set up, it’s time to use it. We will create a Parameter resource in our template that will hold that variable. The syntax looks a bit unfamiliar but it’s ok.

Parameters:
  AccessLogsGroup:
    Type: AWS::SSM::Parameter::Value<String>
    Default: name-of-your-ssm-parameter

The reason we do it like this is so that we can keep our code DRY. This significantly decreases the risk of making errors when copy/pasting etc … and if we ever decide to use a different log group, we can just change the name once at the top of the file and all references will point to the correct resource.

Next we have to configure our API resource to use our newly created log group:

BarzLolAPI:
  Type: AWS::Serverless::Api
  Properties:
    Name: "my_api"
    StageName: my-stage
    AccessLogSetting:
      DestinationArn: !Ref AccessLogsGroup
      Format: $context.extendedRequestId $context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] "$context.httpMethod $context.resourcePath $context.protocol" $context.status $context.responseLength $context.requestId

For the format, I picked the default string I found in the docs but know that you can use json or even csv.

Don’t deploy your stack just yet, we have one more thing to do.

Roles and Policies

If you deploy the stack as is you will get an error about a missing role that’s required.

The reason is simple: for API Gateway to actually push logs to CloudWatch, it needs to have the correct permissions. Thankfully, AWS provides us with a managed policy called AmazonAPIGatewayPushToCloudWatchLogs.

To assign the correct permissions, we’ll need to do two things:

  • Create the role with the proper trust policy
  • Attach the managed policy to the role

This can be done easily with the CLI.

First, let’s create a trust policy for API Gateway to assume the role.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

With this, we can create the role by running this command:

aws iam create-role --role-name my-role --assume-role-policy-document file://trust.json

This command should output the arn of the newly created role, keep it somewhere becaus we will need it shortly.

Now we can attach the managed policy to the role like so:

aws iam attach-role-policy --role-name my-role --policy-arn arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs

Add the role to your API Gateway Settings

Navigate to your API Gateway, under settings (last link on the left sidebar), paste the arn of the role you just created in the CloudWatch log role ARN field.

Deploy Your Stack

You can now safely deploy your stack, make a few requests to your api and see the logs appear in your log group.