Notes on logging custom headers when using AWS API Gateway

What are access and execution logs

It is common to leverage logs to monitor your API Gateway. It is a good and simple way to be able to validate your error rates, request times and some other basic information to help you troubleshoot when something wrong happens with your service.

AWS API Gateway supports two types of logs: Execution logs and Access logs. You can find a detail description about the difference between them at Alex Debrie’s excellent post about AWS API Gateway Access Logs. But in short access logs are a single log line that is logged per request and execution logs are detailed logs about the API Gateway internals that will contain multiple log lines entries per single request.

How to configure access and execution logs

See images below to learn how to enable both logs in the AWS Console by navigating to your API Gateway and selecting the stage you want enable logs for. Then navigate to the logs and tracing tab.

Enable execution logs
Enable access logs

Currently you can customize your access logs by providing a one line formatted JSON string(there are other formats supported as well). Example:

{
  "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"
}

Note: for the example above you need to remove all the new line characters or it will be invalid otherwise.

You will notice that we are only referencing the $context variable in our log format and that is because AWS API Gateway does not support any other of their very useful variables like $method and $integration. Because of that we are limited to only these values from the $context variable. If you take a look you will see that there is no way to access custom header values like you would normally do when using the $method variable available in request and response mapping templates, for example: $method.request.header.PARAM_NAME.

Workaround

I’ve submitted a feature request to the AWS API Gateway team to support at least the $method variable. In the meantime we will have to enable both access and execution logs making sure you are including $context.requestId in the access logs. From there once you have a specific request you can query your execution logs using your requestId to be able to pull all the related log entries.

I’ll post here once this new functionality is enabled and how to use it.

Extra: Enable the execution and access logs with Terraform

resource "aws_api_gateway_rest_api" "example" {
  body = jsonencode({
    ...
  })

  name = "example"
}

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

  ...
}

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

  # *** enable access logs with sample format ***
  access_log_settings {
    destination_arn = "<log-group-arn>"
    format          = "{\"requestId\": \"$context.requestId\",\"status\": \"$context.status\"}"
  }
}

resource "aws_api_gateway_method_settings" "all_execution_logs" {
  rest_api_id = aws_api_gateway_rest_api.example.id
  stage_name  = aws_api_gateway_stage.example.stage_name
  method_path = "*/*"

  # *** enable execution logs ***
  settings {
    metrics_enabled    = true
    logging_level      = "INFO"
    data_trace_enabled = true
  }
}

References