有关查看API Gateway CloudWatch日志和通过Terraform启用的问题。

huangapple go评论72阅读模式
英文:

Trouble viewing API Gateway CloudWatch logs and enabling through Terraform

问题

我正在使用Terraform来构建我的架构,具体来说是与CloudWatch Logs集成的API Gateway。然而,我遇到了一个问题,无法查看API Gateway CloudWatch日志。我已经集成了apigateway-sns,并且可以看到Lambda日志订阅了SNS主题,但我无法查看API Gateway方法的CloudWatch日志。

从控制台中,我注意到"CloudWatch Logs"选项未选中。我可以从控制台手动选中它,但我想通过Terraform进行配置。我已经检查了我的Terraform代码,但无法找出错误在哪里。

resource "aws_api_gateway_rest_api" "main" {
  name        = "gitlab-slack-api-${var.environment}"
  description = "Gitlab Slack notifications integration"

  endpoint_configuration {
    types = ["REGIONAL"]
  }
}

resource "aws_cloudwatch_log_group" "main_api_gw" {
  name              = "/aws/api-gw/${aws_api_gateway_rest_api.main.name}"
  retention_in_days = 14
}

resource "aws_api_gateway_resource" "resource" {
  path_part   = "slack"
  parent_id   = aws_api_gateway_rest_api.main.root_resource_id
  rest_api_id = aws_api_gateway_rest_api.main.id
}

resource "aws_api_gateway_method" "method" {
  rest_api_id   = aws_api_gateway_rest_api.main.id
  resource_id   = aws_api_gateway_resource.resource.id
  http_method   = "POST"
  authorization = "NONE"

  request_parameters = {
    "method.request.querystring.TopicArn" = false
    "method.request.querystring.Message"  = false
  }
}

resource "aws_api_gateway_integration" "integration" {
  rest_api_id             = aws_api_gateway_rest_api.main.id
  resource_id             = aws_api_gateway_resource.resource.id
  http_method             = aws_api_gateway_method.method.http_method
  type                    = "AWS"
  integration_http_method = "POST"
  uri                     = "arn:aws:apigateway:${data.aws_region.region.name}:sns:action/Publish"
  credentials             = aws_iam_role.api_gateway_role.arn

  request_parameters = {
    "integration.request.querystring.TopicArn" = "'${aws_sns_topic.sns_topic_name.arn}'"
    "integration.request.querystring.Message"  = "method.request.body"
  }

  request_templates = {
    "application/json" = <<EOF
   {
      "Message": "$input.body",
      "TopicArn": "$input.params('TopicArn')"
   }
   EOF
  }
}

resource "aws_api_gateway_method_response" "response_200" {
  rest_api_id = aws_api_gateway_rest_api.main.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.method.http_method
  status_code = "200"
}

resource "aws_api_gateway_integration_response" "integration_response" {
  rest_api_id = aws_api_gateway_rest_api.main.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.method.http_method
  status_code = aws_api_gateway_method_response.response_200.status_code
}

resource "aws_api_gateway_deployment" "deployment" {
  depends_on  = [aws_api_gateway_integration.integration]
  rest_api_id = aws_api_gateway_rest_api.main.id
  stage_name  = var.environment
}

resource "aws_api_gateway_stage" "prod" {
  depends_on    = [aws_api_gateway_deployment.deployment]
  stage_name    = var.environment
  rest_api_id   = aws_api_gateway_rest_api.main.id
  deployment_id = aws_api_gateway_deployment.deployment.id

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
    format = jsonencode({
      requestId        = "$context.requestId",
      requestTime      = "$context.requestTime",
      requestTimeEpoch = "$context.requestTimeEpoch",
      path             = "$context.path",
      method           = "$context.httpMethod",
      status           = "$context.status",
      responseLength   = "$context.responseLength"
    })
  }
}

resource "aws_api_gateway_method_settings" "all" {
  rest_api_id = aws_api_gateway_rest_api.test_api_gateway.id
  stage_name  = aws_api_gateway_stage.prod.stage_name
  method_path = "*/*"

  settings {
    logging_level = "INFO"
  }
}

resource "aws_iam_policy" "api_gateway_sns_policy" {
  name        = "ApiGatewaySNSAccessPolicy"
  description = "IAM policy for API Gateway to invoke SNS"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
         Effect   = "Allow",
         Action   = ["sns:Publish"]
         Resource = aws_sns_topic.sns_topic_name.arn
      }
    ]
  })
}

resource "aws_iam_role" "api_gateway_role" {
  name = "ApiGatewaySNSAccessRole"
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
     Statement = [
       {
          Effect    = "Allow"
          Principal = { Service = "apigateway.amazonaws.com" }
          Action    = "sts:AssumeRole"
       }
     ]
  })
}

resource "aws_iam_role_policy_attachment" "api_gateway_role_policy_attachment" {
  role       = aws_iam_role.api_gateway_role.name
  policy_arn = aws_iam_policy.api_gateway_sns_policy.arn
}

output "api_gateway_endpoint_url" {
  value = aws_api_gateway_deployment.deployment.invoke_url
}

output "api_gateway_endpoint_arn" {
  value = aws_api_gateway_rest_api.main.arn
}

希望这有助于解决您的问题。

英文:

I am using Terraform to build my architecture, specifically an API Gateway with CloudWatch Logs integration. However, I am encountering an issue where I cannot see the API Gateway CloudWatch logs. I have integrated the apigateway-sns and can see the Lambda logs subscribed to the SNS topic, but I am unable to view the CloudWatch logs for the API Gateway methods.

From the console, I noticed that the "CloudWatch Logs" option is unchecked. I can manually check it from the console, but I want to configure it through Terraform. I have checked my Terraform code, but I can't figure out where I am making a mistake.

resource &quot;aws_api_gateway_rest_api&quot; &quot;main&quot; {
  name        = &quot;gitlab-slack-api-${var.environment}&quot;
  description = &quot;Gitlab Slack notifications integration&quot;

  endpoint_configuration {
    types = [&quot;REGIONAL&quot;]
  }
}

resource &quot;aws_cloudwatch_log_group&quot; &quot;main_api_gw&quot; {
  name              = &quot;/aws/api-gw/${aws_api_gateway_rest_api.main.name}&quot;
  retention_in_days = 14
}

resource &quot;aws_api_gateway_resource&quot; &quot;resource&quot; {
  path_part   = &quot;slack&quot;
  parent_id   = aws_api_gateway_rest_api.main.root_resource_id
  rest_api_id = aws_api_gateway_rest_api.main.id
}

resource &quot;aws_api_gateway_method&quot; &quot;method&quot; {
  rest_api_id   = aws_api_gateway_rest_api.main.id
  resource_id   = aws_api_gateway_resource.resource.id
  http_method   = &quot;POST&quot;
  authorization = &quot;NONE&quot;

  request_parameters = {
    &quot;method.request.querystring.TopicArn&quot; = false
    &quot;method.request.querystring.Message&quot;  = false
  }
}

resource &quot;aws_api_gateway_integration&quot; &quot;integration&quot; {
  rest_api_id             = aws_api_gateway_rest_api.main.id
  resource_id             = aws_api_gateway_resource.resource.id
  http_method             = aws_api_gateway_method.method.http_method
  type                    = &quot;AWS&quot;
  integration_http_method = &quot;POST&quot;
  uri                     = &quot;arn:aws:apigateway:${data.aws_region.region.name}:sns:action/Publish&quot;
  credentials             = aws_iam_role.api_gateway_role.arn

  request_parameters = {
    &quot;integration.request.querystring.TopicArn&quot; = &quot;&#39;${aws_sns_topic.sns_topic_name.arn}&#39;&quot;
    &quot;integration.request.querystring.Message&quot;  = &quot;method.request.body&quot;
  }

  request_templates = {
    &quot;application/json&quot; = &lt;&lt;EOF
   {
      &quot;Message&quot;: &quot;$input.body&quot;,
      &quot;TopicArn&quot;: &quot;$input.params(&#39;TopicArn&#39;)&quot;
   }
   EOF
  }
}

resource &quot;aws_api_gateway_method_response&quot; &quot;response_200&quot; {
  rest_api_id = aws_api_gateway_rest_api.main.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.method.http_method
  status_code = &quot;200&quot;
}

resource &quot;aws_api_gateway_integration_response&quot; &quot;integration_response&quot; {
  rest_api_id = aws_api_gateway_rest_api.main.id
  resource_id = aws_api_gateway_resource.resource.id
  http_method = aws_api_gateway_method.method.http_method
  status_code = aws_api_gateway_method_response.response_200.status_code
}

resource &quot;aws_api_gateway_deployment&quot; &quot;deployment&quot; {
  depends_on  = [aws_api_gateway_integration.integration]
  rest_api_id = aws_api_gateway_rest_api.main.id
  stage_name  = var.environment
}

resource &quot;aws_api_gateway_stage&quot; &quot;prod&quot; {
  depends_on    = [aws_api_gateway_deployment.deployment]
  stage_name    = var.environment
  rest_api_id   = aws_api_gateway_rest_api.main.id
  deployment_id = aws_api_gateway_deployment.deployment.id

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
    format = jsonencode({
      requestId        = &quot;$context.requestId&quot;
      requestTime      = &quot;$context.requestTime&quot;
      requestTimeEpoch = &quot;$context.requestTimeEpoch&quot;
      path             = &quot;$context.path&quot;
      method           = &quot;$context.httpMethod&quot;
      status           = &quot;$context.status&quot;
      responseLength   = &quot;$context.responseLength&quot;
    })
  }
}

resource &quot;aws_api_gateway_method_settings&quot; &quot;all&quot; {
  rest_api_id = aws_api_gateway_rest_api.test_api_gateway.id
  stage_name  = aws_api_gateway_stage.prod.stage_name
  method_path = &quot;*/*&quot;

  settings {
    logging_level = &quot;INFO&quot;
  }
}

resource &quot;aws_iam_policy&quot; &quot;api_gateway_sns_policy&quot; {
  name        = &quot;ApiGatewaySNSAccessPolicy&quot;
  description = &quot;IAM policy for API Gateway to invoke SNS&quot;

  policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
         Effect   = &quot;Allow&quot;
         Action   = [&quot;sns:Publish&quot;]
         Resource = aws_sns_topic.sns_topic_name.arn
      }
    ]
  })
}

resource &quot;aws_iam_role&quot; &quot;api_gateway_role&quot; {
  name = &quot;ApiGatewaySNSAccessRole&quot;
  assume_role_policy = jsonencode({
    Version = &quot;2012-10-17&quot;
     Statement = [
       {
          Effect    = &quot;Allow&quot;
          Principal = { Service = &quot;apigateway.amazonaws.com&quot; }
          Action    = &quot;sts:AssumeRole&quot;
       }
     ]
  })
}

resource &quot;aws_iam_role_policy_attachment&quot; &quot;api_gateway_role_policy_attachment&quot; {
  role       = aws_iam_role.api_gateway_role.name
  policy_arn = aws_iam_policy.api_gateway_sns_policy.arn
}

output &quot;api_gateway_endpoint_url&quot; {
  value = aws_api_gateway_deployment.deployment.invoke_url
}

output &quot;api_gateway_endpoint_arn&quot; {
  value = aws_api_gateway_rest_api.main.arn
}

答案1

得分: 1

您可以在阶段设置中为API Gateway启用日志记录,对应一个Terraform资源已存在。根据文档示例和您的代码,以下内容应该帮助您配置日志记录:

resource "aws_cloudwatch_log_group" "main_api_gw" {
  name              = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.main.id}/${var.environment}"
  retention_in_days = 14
}

resource "aws_api_gateway_stage" "main" {
  stage_name = var.environment

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
    format = "JSON" # 或 CLF/XML/CSV
  }
}

为了避免循环依赖,您需要将阶段名称定义为一个变量,而在您的情况下似乎是 var.environment。这是日志组 name 参数和API Gateway阶段资源的 stage_name 参数的一部分。另外,日志组的名称必须像这样,因为这是默认的API Gateway命名约定:

在启用此功能时管理CloudWatch日志组时,可以使用aws_cloudwatch_log_group资源,其中名称与API Gateway命名约定匹配。

format 参数是必需的,可以是 JSON/XML/CLF/CSV 中的一个,您可以在文档中了解更多信息。

编辑:由于原始帖子未显示所有代码,且阶段资源已存在并正确引用了日志组,需要更改的是日志组名称(如上所述)和IAM角色权限。除了在权限策略中允许SNS外,还需要添加CloudWatch日志:

resource "aws_iam_policy" "api_gateway_sns_policy" {
  name        = "ApiGatewaySNSAccessPolicy"
  description = "API Gateway调用SNS的IAM策略"

  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [
      {
         Effect   = "Allow",
         Action   = ["sns:Publish"],
         Resource = aws_sns_topic.sns_topic_name.arn
      },
      {
         Effect   = "Allow",
         Action   = [
           "logs:CreateLogGroup",
           "logs:CreateLogStream",
           "logs:DescribeLogGroups",
           "logs:DescribeLogStreams",
           "logs:PutLogEvents",
           "logs:GetLogEvents",
           "logs:FilterLogEvents"
         ],
         Resource = "*"
      }
    ]
  })
}
英文:

You can enable logging for API Gateway in the Stage settings, for which a terraform resource exists. Based on the example from the documentation and your code, the following should enable you to configure logging:

resource &quot;aws_cloudwatch_log_group&quot; &quot;main_api_gw&quot; {
  name              = &quot;API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.main.id}/${var.environment}&quot;
  retention_in_days = 14
}

resource &quot;aws_api_gateway_stage&quot; &quot;main&quot; {
  stage_name = var.environment

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
    format = &quot;JSON&quot; # or CLF/XML/CSV
  }
}

In order to avoid circular dependency, you would need to define the stage name as a variable, which in your case seems to be var.environment. This is a part of the log group name argument and stage_name argument of the API Gateway stage resource. Additionally, the name of the log group has to be like this, as this is the default API Gateway naming convention:

> To manage the CloudWatch Log Group when this feature is enabled, the aws_cloudwatch_log_group resource can be used where the name matches the API Gateway naming convention.

The format argument is required and can take in one of &quot;JSON&quot;/&quot;XML&quot;/&quot;CLF&quot;/&quot;CSV&quot;, which you can read more about in the documentation.


EDIT: Since the original post didn't show all the code, and the stage resource was present, referencing the log group properly, what would need to change is the log group name (as described above) and the IAM role permissions. Besides having SNS allowed in the permissions policy, CloudWatch logs also need to be added:

resource &quot;aws_iam_policy&quot; &quot;api_gateway_sns_policy&quot; {
  name        = &quot;ApiGatewaySNSAccessPolicy&quot;
  description = &quot;IAM policy for API Gateway to invoke SNS&quot;

  policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
         Effect   = &quot;Allow&quot;
         Action   = [&quot;sns:Publish&quot;]
         Resource = aws_sns_topic.sns_topic_name.arn
      },
      {
         Effect   = &quot;Allow&quot;
         Action   = [
           &quot;logs:CreateLogGroup&quot;,
           &quot;logs:CreateLogStream&quot;,
           &quot;logs:DescribeLogGroups&quot;,
           &quot;logs:DescribeLogStreams&quot;,
           &quot;logs:PutLogEvents&quot;,
           &quot;logs:GetLogEvents&quot;,
           &quot;logs:FilterLogEvents&quot;
         ]
         Resource = &quot;*&quot;
      }
    ]
  })
}

答案2

得分: 0

找到解决方案:
ApiGate 需要一个角色来允许 CloudWatch 发布日志,某种方式 Marko 的策略没有生效(不知道为什么)。我使用了 AWS 管理的角色 AmazonAPIGatewayPushToCloudWatchLogs 和资源 aws_api_gateway_method_settings 来启用日志记录(INFO、ERROR、OFF)。现在我可以在 CloudWatch 中看到 API Gateway 的日志。

谢谢 Marko,在这个问题上付出了一些努力。

以下是 Terraform 代码:

resource "aws_api_gateway_deployment" "deployment" {
  depends_on  = [aws_api_gateway_integration.integration]
  rest_api_id = aws_api_gateway_rest_api.main.id
  lifecycle {
    create_before_destroy = true
  }
}

resource "aws_api_gateway_stage" "prod" {
  depends_on    = [aws_cloudwatch_log_group.main_api_gw]
  stage_name    = var.environment
  rest_api_id   = aws_api_gateway_rest_api.main.id
  deployment_id = aws_api_gateway_deployment.deployment.id

  access_log_settings {
    destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
    format = jsonencode({
      requestId        = "$context.requestId"
      requestTime      = "$context.requestTime"
      requestTimeEpoch = "$context.requestTimeEpoch"
      path             = "$context.path"
      method           = "$context.httpMethod"
      status           = "$context.status"
      responseLength   = "$context.responseLength"
    })
  }
}

resource "aws_iam_role" "api_gateway_cloud_watch_role" {
  name = "AmazonAPIGatewayPushToCloudWatchLogs"

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

resource "aws_iam_role_policy_attachment" "api_gateway_cloudwatchlogs" {
  role       = aws_iam_role.api_gateway_cloud_watch_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}

resource "aws_api_gateway_account" "api_gateway_account" {
  cloudwatch_role_arn = aws_iam_role.api_gateway_cloud_watch_role.arn
}

resource "aws_api_gateway_method_settings" "api_gateway_log_settings" {
  rest_api_id = aws_api_gateway_rest_api.main.id
  stage_name  = var.environment
  method_path = "*/*"

  settings {
    metrics_enabled = true
    logging_level   = "INFO"
  }
}

参考链接:
这里获取了参考。

英文:

Found solution:
ApiGate needs a role to allow CloudWatch to publish logs, some how Marko's policy did not work(don't why). I have used the AWS managed role AmazonAPIGatewayPushToCloudWatchLogs and the resource aws_api_gateway_method_settings for enabling the logs(INFO, ERROR,OFF). Now I can see the API Gateway logs in CloudWatch.

Thank you Marko, put some effort on the issue.

Here is Terraform:

<!-- begin snippet: js hide: false console: true babel: false -->

<!-- language: lang-js -->

resource &quot;aws_api_gateway_deployment&quot; &quot;deployment&quot; {
depends_on  = [aws_api_gateway_integration.integration]
rest_api_id = aws_api_gateway_rest_api.main.id
lifecycle {
create_before_destroy = true
}
}
resource &quot;aws_api_gateway_stage&quot; &quot;prod&quot; {
depends_on    = [aws_cloudwatch_log_group.main_api_gw]
stage_name    = var.environment
rest_api_id   = aws_api_gateway_rest_api.main.id
deployment_id = aws_api_gateway_deployment.deployment.id
access_log_settings {
destination_arn = aws_cloudwatch_log_group.main_api_gw.arn
format = jsonencode({
requestId        = &quot;$context.requestId&quot;
requestTime      = &quot;$context.requestTime&quot;
requestTimeEpoch = &quot;$context.requestTimeEpoch&quot;
path             = &quot;$context.path&quot;
method           = &quot;$context.httpMethod&quot;
status           = &quot;$context.status&quot;
responseLength   = &quot;$context.responseLength&quot;
})
}
}
resource &quot;aws_iam_role&quot; &quot;api_gateway_cloud_watch_role&quot; {
name = &quot;AmazonAPIGatewayPushToCloudWatchLogs&quot;
assume_role_policy = &lt;&lt;-EOF
{
&quot;Version&quot;: &quot;2012-10-17&quot;,
&quot;Statement&quot;: [
{
&quot;Action&quot;: &quot;sts:AssumeRole&quot;,
&quot;Principal&quot;: {
&quot;Service&quot;: &quot;apigateway.amazonaws.com&quot;
},
&quot;Effect&quot;: &quot;Allow&quot;
}
]
}
EOF
}
resource &quot;aws_iam_role_policy_attachment&quot; &quot;api_gateway_cloudwatchlogs&quot; {
role       = aws_iam_role.api_gateway_cloud_watch_role.name
policy_arn = &quot;arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs&quot;
}
resource &quot;aws_api_gateway_account&quot; &quot;api_gateway_account&quot; {
cloudwatch_role_arn = aws_iam_role.api_gateway_cloud_watch_role.arn
}
resource &quot;aws_api_gateway_method_settings&quot; &quot;api_gateway_log_settings&quot; {
rest_api_id = aws_api_gateway_rest_api.main.id
stage_name  = var.environment
method_path = &quot;*/*&quot;
settings {
metrics_enabled = true
logging_level   = &quot;INFO&quot;
}
}

<!-- end snippet -->

Ref:

Took Ref from here as well

huangapple
  • 本文由 发表于 2023年7月11日 14:21:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/76659181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定