LoginSignup
4
3

More than 3 years have passed since last update.

Amazon API Gateway アクセスログ出力設定とアクセスログステータスコード監視設定

Last updated at Posted at 2020-11-19

個人的備忘録

1. 概要

  • 設定内容

  • Amazon API Gateway のアクセスログを出力する設定

    • API名: hogehoge
    • CloudWatch Logs ロググループ名: /aws/apigateway/hogehoge-accesslog
  • アクセスログのステータスコード監視設定 - 方法1

    • CloudWatch Logs メトリックフィルター + CloudWatch メトリックアラーム + Amazon SNS (AWS Chatbot) + Slack
  • アクセスログのステータスコード監視設定 - 方法2

  • 監視方法

  • 方法1

    • 監視間隔
      • 1分おき
    • 監視ワード
      • status = 4* || status = 5*
    • Slackへの通知条件
      • 上記監視ワードにマッチしたアクセスログのカウント数が閾値を超えたときと、下回ったとき
        (閾値を超えている間はSlackへの通知はない)
    • Slackへの通知内容
      • カウント数
  • 方法2

    • 監視間隔
      • 5分おき
    • 監視ワード
      • status >= 400
    • Slackへの通知条件
      • 直近5分前のアクセスログが上記監視ワードにマッチしたとき
    • Slackへの通知内容
      • マッチしたアクセスログの行
  • 監視例

    • 方法1で第一報と収束を通知、方法2でエラー中の実際のログを1分毎に通知する

2. 設定

2.1. API Gateway アクセスログ出力

[参考] https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/http-api-logging.html

  1. API Gateway が CloudWatch Logs ロググループへアクセスログを書き込むことを許可するIAMロールを作成
  2. API Gateway のアクセスログを記録する CloudWatch Logs ロググループ を作成
  3. API Gateway のアクセスログ出力を有効にする設定

1. API Gateway が CloudWatch Logs ロググループへアクセスログを書き込むことを許可するIAMロールを作成

  • IAMロール を作成する terraform サンプル
data "aws_iam_policy_document" "apigw-assume-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["apigateway.amazonaws.com"]
    }
  }
}
resource "aws_iam_role" "role_AmazonAPIGatewayPushToCloudWatchLogs" {
  name               = "AmazonAPIGatewayPushToCloudWatchLogs"
  assume_role_policy = data.aws_iam_policy_document.apigw-assume-role-policy.json
  path               = "/service-role/"
}
resource "aws_iam_role_policy_attachment" "AmazonAPIGatewayPushToCloudWatchLogs" {
  role       = aws_iam_role.role_AmazonAPIGatewayPushToCloudWatchLogs.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
}
  • 作成したIAMロール
    20201119-01.png
    20201119-02.png

  • 作成したIAMロールを設定する

    20201119-03.png


2. API Gateway のアクセスログを記録する CloudWatch Logs ロググループ を作成

  • ロググループ を作成する terraform サンプル
resource "aws_cloudwatch_log_group" "apigateway-hogehoge-accesslog" {
  name              = "/aws/apigateway/hogehoge-accesslog"
  retention_in_days = "90"
}

3. API Gateway のアクセスログ出力を有効にする設定

[参考]https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/set-up-logging.html

  • AWS マネジメントコンソール

    20201119-04.png

    • 「Access Log Destination ARN」は、 2. で作成したロググループの ARN を設定
    • ログの形式は JSON を選択
  • アクセスログ例 (JSON)

{
    "requestId": "※※※※※※※※-※※※※-※※※※-※※※※-※※※※※※※※※※※※",
    "ip": "***.***.***.***",
    "caller": "-",
    "user": "-",
    "requestTime": "18/Nov/2020:05:28:56 +0000",
    "httpMethod": "POST",
    "resourcePath": "/v1",
    "status": "403",
    "protocol": "HTTP/1.1",
    "responseLength": "42"
}

2.2. アクセスログのステータスコード監視設定 - 方法1

  • ディレクトリ構成

└── apigateway-accesslog-check.tf
  • CloudWatch Logs メトリックフィルター を作成する terraform のサンプル
apigateway-accesslog-check.tf
resource "aws_cloudwatch_log_metric_filter" "APIGateway_hogehoge_accesslog_4xx5xxError" {
  name           = "APIGateway_hogehoge_accesslog_4xx5xxError"
  pattern        = "{ ($.status = 4*) || ($.status = 5*) }"
  log_group_name = aws_cloudwatch_log_group.apigateway-hogehoge-accesslog.name
  metric_transformation {
    name      = "APIGateway_hogehoge_accesslog_4xx5xxError"
    namespace = "LogMetrics"
    value     = "1"
  }
}
  • CloudWatch メトリックアラーム を作成する terraform のサンプル
apigateway-accesslog-check.tf
resource "aws_cloudwatch_metric_alarm" "APIGateway_hogehoge_accesslog_4xx5xxError" {
  alarm_name          = "APIGateway hogehoge accesslog 4xx5xxError"
  threshold           = "1"
  alarm_description   = ""
  alarm_actions       = [※※※※※]
  ok_actions          = [※※※※※]
  comparison_operator = "GreaterThanOrEqualToThreshold"
  namespace           = "LogMetrics"
  metric_name         = "APIGateway_hogehoge_accesslog_4xx5xxError"
  period              = "60"
  evaluation_periods  = "1"
  statistic           = "Sum"
  treat_missing_data  = "notBreaching"
}

2.3. アクセスログのステータスコード監視設定 - 方法2

  • ディレクトリ構成

├── apigateway-accesslog-check.tf
└── source_code
    └── apigateway-accesslog-check
        └── main.py
  • Lambda にアタッチする IAM ロール を作成する terraform のサンプル
apigateway-accesslog-check.tf
data "aws_iam_policy_document" "lambda-assume-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}
resource "aws_iam_role" "role_lambda-apigateway-accesslog-check" {
  name               = "lambda-apigateway-accesslog-check"
  assume_role_policy = data.aws_iam_policy_document.lambda-assume-role-policy.json
  path               = "/service-role/"
}
resource "aws_iam_role_policy_attachment" "lambda-apigateway-accesslog-check_CloudWatchLogsReadOnlyAccess" {
  role       = aws_iam_role.role_lambda-apigateway-accesslog-check.name
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchLogsReadOnlyAccess"
}
resource "aws_iam_role_policy_attachment" "lambda-apigateway-accesslog-check_AWSLambdaBasicExecutionRole" {
  role       = aws_iam_role.role_lambda-apigateway-accesslog-check.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
  • Lambda を作成する terraform のサンプル
apigateway-accesslog-check.tf
data "archive_file" "apigateway-accesslog-check" {
  type        = "zip"
  source_dir  = "./source_code/apigateway-accesslog-check"
  output_path = "./source_code/apigateway-accesslog-check.zip"
}
resource "aws_lambda_function" "apigateway-accesslog-check_hogehoge" {
  filename         = data.archive_file.apigateway-accesslog-check.output_path
  function_name    = "apigateway-accesslog-check_hogehoge"
  role             = aws_iam_role.role_lambda-apigateway-accesslog-check.arn
  handler          = "main.lambda_handler"
  source_code_hash = data.archive_file.apigateway-accesslog-check.output_base64sha256
  runtime          = "python3.8"
  memory_size      = 128
  timeout          = 300
  environment {
    variables = {
      LOGGROUP_NAMES    = "/aws/apigateway/hogehoge-accesslog"
      SLACK_CHANNEL     = "#※※※※※"
      SLACK_TEXT        = "Slack通知テスト"
      SLACK_USER_NAME   = "名無し"
      SLACK_ICON_EMOJI  = ":fearful:"
      SLACK_COLOR       = "warning"
      SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/※※※※※/※※※※※/※※※※※"
    }
  }
}
source_code/apigateway-accesslog-check/main.py
import os
import datetime
import boto3
client = boto3.client('logs')
import time
import ast
import json
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

logGroupName    = os.environ['LOGGROUP_NAMES']
slackWebhookURL = os.environ['SLACK_WEBHOOK_URL']
slackChannel    = os.environ['SLACK_CHANNEL']
slackUserNmae   = os.environ['SLACK_USER_NAME']
slackText       = os.environ['SLACK_TEXT']
slackColor      = os.environ['SLACK_COLOR']
slackIconEmoji  = os.environ['SLACK_ICON_EMOJI']

def lambda_handler(event, context):
    five_minutes_ago = datetime.datetime.now() - datetime.timedelta(minutes = 5)
    startTime = five_minutes_ago.replace(second = 0, microsecond = 0)
    endTime = startTime + datetime.timedelta(minutes = 5) - datetime.timedelta(milliseconds = 1)
    queryString = 'fields @timestamp, @message | filter (status >= 400)'

    error_logs = results(logGroupName, startTime, endTime, queryString)
    if len(error_logs) > 0:
        for error_log in error_logs:
            dict_message = ast.literal_eval(error_log[1]['value'])
            text = str(logGroupName) + '\n' + str(dict_message)
            print(text)
            post_slack(text)

def results(logGroupName, startTime, endTime, queryString):
    start_query_res = client.start_query(
        logGroupName=logGroupName,
        startTime=int(startTime.timestamp()),
        endTime=int(endTime.timestamp()),
        queryString=queryString
    )
    queryId = start_query_res['queryId']

    get_results_res = client.get_query_results(
        queryId=queryId
    )

    while get_results_res['status'] == 'Running':
        time.sleep(5)
        get_results_res = client.get_query_results(
           queryId=queryId
        )
    return get_results_res['results']

def post_slack(text):
    print(slackWebhookURL)

    slack_message = {
        'channel': slackChannel,
        'username': slackUserNmae,
        'text': slackText,
        'icon_emoji': slackIconEmoji,
        'attachments': [
            {
                "color": slackColor,
                "text": text
            }
        ]
    }
    req = Request(slackWebhookURL, json.dumps(slack_message).encode('utf-8'))
    response = urlopen(req)
    response.read()
  • CloudWatch イベントルール を作成する terraform のサンプル
apigateway-accesslog-check.tf
resource "aws_cloudwatch_event_rule" "apigateway-accesslog-check" {
  name                = "alarm_apigateway-accesslog-check"
  is_enabled          = true
  schedule_expression = "rate(5 minutes)"
}
resource "aws_cloudwatch_event_target" "apigateway-accesslog-check_hogehoge" {
  rule      = aws_cloudwatch_event_rule.apigateway-accesslog-check.name
  target_id = "apigateway-accesslog-check_hogehoge"
  arn       = aws_lambda_function.apigateway-accesslog-check_hogehoge.arn
}

※ terraform で CloudWatch イベントルール を作成した場合、スケジュールが来ても lambda が実行されない時がある。
※ そのときは、AWSマネジメントコンソールから 対象のイベントルールを無編集で更新する。

3. Slack通知例

  • 方法1

    20201119-08.png
    20201119-09.png

  • 方法2

    20201119-10.png

4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3