はじめに
API Gateway+Terraform記事第3弾。
前回の記事でカスタムアクセスログについてさらっと触れたが、今回はログフォーマット関連でもう少し掘り下げてみる。
アクセスログに出せるもの
以下のドキュメントを参照しよう。
【AWS公式】API Gateway での REST API の CloudWatch ログ記録の設定
【AWS公式】API Gateway マッピングテンプレートとアクセスのログ記録の変数リファレンス
API Gatewayのドキュメントは結構散らかっていて、後者のページではあたかも色々なものをログに出せそうに見えるが、実際には前者のページに記載の通り、$context 変数のものだけしか出力できない。
例えば、ヘッダやパスパラメータやBodyで指定されるようなパラメータについては、出力できないため、あくまでもHTTPサーバとしてのアクセスログといった位置づけだろう。
※統合リクエストでAWSサービスとも統合できるのに、アプリケーションサーバ的なログが出せないのはちょっといただけないのだが……
ログ出力の方法
前回の記事にも書いた通り、カスタムアクセスログについては、Terraformの aws_api_gateway_stage
で access_log_settings
を指定してあげることで出力可能だ。CloudWatch Logsの作成や、ログ書き込みのためのロール設定等は前回を参照してほしい。
なお、ログフォーマットには、CLF、JSON、XML、CSVが設定可能だが、CloudWatch Logs Insightsで使えることを考えると、JSONが良いだろうということで、今回はJSONで書いている(後でJSON→CSV変換であれば色々な手段で簡単にできるし)。
今回の記事では、簡単な Mock 統合を作成し、そこの prod ステージに対して以下の設定をした。
resource "aws_api_gateway_stage" "prod" {
stage_name = "prod"
rest_api_id = aws_api_gateway_rest_api.my.id
deployment_id = aws_api_gateway_deployment.dev_to_prod.id
access_log_settings {
destination_arn = aws_cloudwatch_log_group.apigateway_accesslog.arn
format = replace(file("${path.module}/logformat.json"), "\n", "")
}
}
format には直接書き込んでも良いが、色々と書きだしたい場合はファイルを分けた方が良いだろう。
replace() 関数を入れているのは、JSONを複数行にするとエラーになるためだ(API Gatewayの仕様)。
ファイルに '' を入れても良いのだが、可読性を上げるために、ファイルは普通のJSON形式にして、Terraformに食わせるときに変換している。
実際の出力
実際に設定した際の出力を確認していってみよう。
なお、全部一気にまとめて出力しようとしたら、ログフォーマット設定は最大で 3,000Byte までしか指定できないらしいので、まとめて出力することはできないようだ。実運用する際は、必要なものをピックアップしよう。
なお、値が "-"
のものは、今回の単純な Mock のAPIでは使っていない機能によるものなので、実際には利用シーンに合わせた値が設定されるはずである。
例に記載されているもの
フォーマットでの指定
{
"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"
}
ログ出力内容
{
"requestId": "feee42ea-1b14-4f99-ad2a-5c0e17b01d5c",
"ip": "xxx.xxx.xxx.xxx",
"caller": "-",
"user": "-",
"requestTime": "26/Sep/2020:13:10:33 +0000",
"httpMethod": "GET",
"resourcePath": "/employee",
"status": "200",
"protocol": "HTTP/1.1",
"responseLength": "0",
共通的なコンテキスト変数
フォーマットでの指定
{
"account_id": "$context.accountId",
"api_id": "$context.apiId",
"authorizer_claims_property": "$context.authorizer.claims.property",
"authorizer_principal_id": "$context.authorizer.principalId",
"authorizer_property": "$context.authorizer.property",
"aws_endpoint_request_id": "$context.awsEndpointRequestId",
"domain_name": "$context.domainName",
"domain_prefix": "$context.domainPrefix",
"error_message": "$context.error.message",
"error_message_string": "$context.error.messageString",
"error_response_type": "$context.error.responseType",
"error_validation_error_string": "$context.error.validationErrorString",
"extended_request_id": "$context.extendedRequestId",
"identity_account_id": "$context.identity.accountId",
"identity_api_key": "$context.identity.apiKey",
"identity_api_key_id": "$context.identity.apiKeyId",
"identity_cognito_authentication_provider": "$context.identity.cognitoAuthenticationProvider",
"identity_cognito_authentication_type": "$context.identity.cognitoAuthenticationType",
"identity_cognito_identity_id": "$context.identity.cognitoIdentityId",
"identity_cognito_identity_pool_id": "$context.identity.cognitoIdentityPoolId",
"identity_principal_org_id": "$context.identity.principalOrgId",
"identity_client_cert_client_cert_pem": "$context.identity.clientCert.clientCertPem",
"identity_client_cert_subject_d_n": "$context.identity.clientCert.subjectDN",
"identity_client_cert_issuer_d_n": "$context.identity.clientCert.issuerDN",
"identity_client_cert_serial_number": "$context.identity.clientCert.serialNumber",
"identity_client_cert_validity_not_before": "$context.identity.clientCert.validity.notBefore",
"identity_client_cert_validity_not_after": "$context.identity.clientCert.validity.notAfter",
"identity_user_agent": "$context.identity.userAgent",
"identity_user_arn": "$context.identity.userArn",
"path": "$context.path",
"request_override_header_header_name": "$context.requestOverride.header.header_name",
"request_override_path_path_name": "$context.requestOverride.path.path_name",
"request_override_querystring_querystring_name": "$context.requestOverride.querystring.querystring_name",
"response_override_header_header_name": "$context.responseOverride.header.header_name",
"response_override_status": "$context.responseOverride.status",
"request_time_epoch": "$context.requestTimeEpoch",
"resource_id": "$context.resourceId",
"stage": "$context.stage",
"waf_response_code": "$context.wafResponseCode",
"webacl_arn": "$context.webaclArn",
"xray_trace_id": "$context.xrayTraceId"
}
ログ出力内容
{
"account_id": "xxxxxxxxxxxx",
"api_id": "xxxxxxxxxx",
"authorizer_claims_property": "-",
"authorizer_principal_id": "-",
"authorizer_property": "-",
"aws_endpoint_request_id": "-",
"domain_name": "xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com",
"domain_prefix": "xxxxxxxxxx",
"error_message": "-",
"error_message_string": "-",
"error_response_type": "-",
"error_validation_error_string": "-",
"extended_request_id": "TeaXkGWhNjMFVjw=",
"identity_account_id": "-",
"identity_api_key": "-",
"identity_api_key_id": "-",
"identity_cognito_authentication_provider": "-",
"identity_cognito_authentication_type": "-",
"identity_cognito_identity_id": "-",
"identity_cognito_identity_pool_id": "-",
"identity_principal_org_id": "-",
"identity_client_cert_client_cert_pem": "-",
"identity_client_cert_subject_d_n": "-",
"identity_client_cert_issuer_d_n": "-",
"identity_client_cert_serial_number": "-",
"identity_client_cert_validity_not_before": "-",
"identity_client_cert_validity_not_after": "-",
"identity_user_agent": "curl/7.61.1",
"identity_user_arn": "-",
"path": "/prod/employee",
"request_override_header_header_name": "-",
"request_override_path_path_name": "-",
"request_override_querystring_querystring_name": "-",
"response_override_header_header_name": "-",
"response_override_status": "-",
"request_time_epoch": "1601126133567",
"resource_id": "xxxxxx",
"stage": "prod",
"waf_response_code": "-",
"webacl_arn": "-",
"xray_trace_id": "-"
}
アクセスログのみのコンテキスト変数
フォーマットでの指定
{
"authorize_error": "$context.authorize.error",
"authorize_latency": "$context.authorize.latency",
"authorize_status": "$context.authorize.status",
"authorizer_error": "$context.authorizer.error",
"authorizer_integration_latency": "$context.authorizer.integrationLatency",
"authorizer_integration_status": "$context.authorizer.integrationStatus",
"authorizer_latency": "$context.authorizer.latency",
"authorizer_request_id": "$context.authorizer.requestId",
"authorizer_status": "$context.authorizer.status",
"authenticate_error": "$context.authenticate.error",
"authenticate_latency": "$context.authenticate.latency",
"authenticate_status": "$context.authenticate.status",
"integration_error": "$context.integration.error",
"integration_integration_status": "$context.integration.integrationStatus",
"integration_latency1": "$context.integration.latency",
"integration_request_id": "$context.integration.requestId",
"integration_status1": "$context.integration.status",
"integration_error_message": "$context.integrationErrorMessage",
"integration_latency2": "$context.integrationLatency",
"integration_status2": "$context.integrationStatus",
"response_latency": "$context.responseLatency",
"waf_error": "$context.waf.error",
"waf_latency": "$context.waf.latency",
"waf_status": "$context.waf.status"
}
ログ出力内容
{
"authorize_error": "-",
"authorize_latency": "-",
"authorize_status": "-",
"authorizer_error": "-",
"authorizer_integration_latency": "-",
"authorizer_integration_status": "-",
"authorizer_latency": "-",
"authorizer_request_id": "-",
"authorizer_status": "-",
"authenticate_error": "-",
"authenticate_latency": "-",
"authenticate_status": "-",
"integration_error": "-",
"integration_integration_status": "200",
"integration_latency1": "0",
"integration_request_id": "-",
"integration_status1": "-",
"integration_error_message": "-",
"integration_latency2": "0",
"integration_status2": "200",
"response_latency": "3",
"waf_error": "-",
"waf_latency": "-",
"waf_status": "-"
}