CloudWatchのWebアプリケーションログ(その1 )
39390 ワード
このポストでは、LambdaSharpアプリAPIをCloudWatch Logs REST APIを使用してログに記録する方法を説明しています.
可観測性は開発者にとって重要なビルディングブロックである.したがって、それはLambdSharp開発者の経験の不可欠な部分です.参考のために、これはどのようにAmazon API Gatewayアプリが作成され、自動的にLambdashharpでCloudWatchのログリングのために配線されています.
additional capabilities
概要
この実装ではラムダ関数は使用しません.代わりに、APIゲートウェイREST APIを テンプレートを使用してCloudWatchログAPIと直接統合することでCloudWatchにログを有効にします.この設計は、最小限のコードが含まれる、ラムダコールドスタートの遅延、およびラムダ呼び出しコストなしであることを意味します.
実装はYAML記法を使用しているCloudFormationリソースに関して記述されます、しかし、同じ結果は代わりにAWSコンソールを使用することによって達成されることができます.
Apache Velocity
APIのログ記録
まず、新しいAPIゲートウェイリソースを作成する必要があります.APIをアンカーするトップレベル
ロググループ
CloudWatchロググループは、アプリケーション間で区別するのは簡単にするために、各アプリケーションのために明示的に作成する必要があります.加えて、ログ保持ポリシーはロググループがそれのために無期限に請求されるのを避けるために使用する記憶の量を制限するように設定されるべきです.
ゲートウェイ
APIゲートウェイはロググループでログストリームを作成し、それらに書き込む許可を必要とします.これは、以下のIAMロール定義によって達成されます.
API検証
APIゲートウェイREST APIのきちんとした機能は、JSONスキーマモデルに対する要求を検証できることです.この機能は、受信ペイロードが有効でないときにAPIの不要な呼び出しを防止します.妥当性検査は、各APIゲートウェイメソッドを次のバリデータ宣言に関連付けて有効になります.
REST API
この次のセクションは少し重いです.APIゲートウェイリソース、メソッド、および統合がどのように構築されるか.
まず、APIメソッドに関連する
アプリケーションは、新しいログストリームを作成する責任があることに注意してください.シングルページアプリ(SPA)の場合は、新しいログストリームは、アプリケーションの負荷たびに作成する必要があります.また、これはLambdasharpで構築されたBlazor WBabassemblyアプリの動作です.
を作成します.アプリケーションログ
Postメソッドは、関連するロググループに新しいログストリームを作成します.応答処理のいくつかは、呼び出し元アプリケーションにエラーがどのように返されるかに関するものです.強調はあまりにも多くの内部の詳細を明らかにせずに有用なフィードバックを提供する上です.
オプションのメソッドと同様に、この構成は
ログを追加します.アプリケーションログ
POSTメソッドと同様に、PUTメソッドは、受信要求を検証し、エラーが発生したときにどのような内部詳細が公開されるかを制限します.
オプションのメソッドと同様に、この構成は
APIと使用計画
次のリソースは、APIキーと使用計画を宣言します.APIキーは、デフォルトでCloudFormation Stack GuidのBase 64値に設定されます.フロントエンドアプリケーションがログ記録REST APIを使用するためにそれにアクセスする必要があるので、明示的にAPIキーを設定することをお勧めします.APIキーはさらにアプリケーションの内部値と組み合わせることによって難読化することができます.LambdSharpでは、APIキーは、CloudFormation Stack Guidとコンパイル済みの組み合わせを組み合わせて生成されます.NETコアアセンブリ識別子guid.
API展開
最後に、展開リソースによって使用される
結論-継続する.
このポストでは、フロントエンドのアプリを直接CloudWatchにログオンするために必要なリソースを作成しました.次のポストでは、このREST API経由でログを記録するプロトコルをカバーします.
ハッピーハッキング!
可観測性は開発者にとって重要なビルディングブロックである.したがって、それはLambdSharp開発者の経験の不可欠な部分です.参考のために、これはどのようにAmazon API Gatewayアプリが作成され、自動的にLambdashharpでCloudWatchのログリングのために配線されています.
Module: Sample.BlazorWebAssembly
Items:
- App: MyBlazorApp
はい、それは本当にです!何も追加は必要ありませんが、Blazor WebAssemblyの多くがあります.しかし、非LambsSharp開発者は、彼らのアプリが彼らの好ましいフレームワークを使用するために、同じ能力を達成したいかもしれません.それがこのポストの目的です.どのフレームワークを使用してフロントエンドアプリケーションのCloudWatchのログ機能を構築する方法を示します.additional capabilities
概要
この実装ではラムダ関数は使用しません.代わりに、APIゲートウェイREST APIを テンプレートを使用してCloudWatchログAPIと直接統合することでCloudWatchにログを有効にします.この設計は、最小限のコードが含まれる、ラムダコールドスタートの遅延、およびラムダ呼び出しコストなしであることを意味します.
実装はYAML記法を使用しているCloudFormationリソースに関して記述されます、しかし、同じ結果は代わりにAWSコンソールを使用することによって達成されることができます.
Apache Velocity
APIのログ記録
まず、新しいAPIゲートウェイリソースを作成する必要があります.APIをアンカーするトップレベル
.app
リソースを定義します.LambdSharpでは、この最上位のリソース名はCloudFormationパラメーターを使用して設定可能です.RestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: !Sub "${AWS::StackName} App API"
RestApiAppResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !GetAtt RestApi.RootResourceId
PathPart: .app
ロググループ
CloudWatchロググループは、アプリケーション間で区別するのは簡単にするために、各アプリケーションのために明示的に作成する必要があります.加えて、ログ保持ポリシーはロググループがそれのために無期限に請求されるのを避けるために使用する記憶の量を制限するように設定されるべきです.
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
RetentionInDays: 90
ゲートウェイ
APIゲートウェイはロググループでログストリームを作成し、それらに書き込む許可を必要とします.これは、以下のIAMロール定義によって達成されます.
RestApiRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Sid: ApiGatewayPrincipal
Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ApiLogsPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: LogGroupPermission
Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroup}"
- !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:${LogGroup}:log-stream:*"
API検証
APIゲートウェイREST APIのきちんとした機能は、JSONスキーマモデルに対する要求を検証できることです.この機能は、受信ペイロードが有効でないときにAPIの不要な呼び出しを防止します.妥当性検査は、各APIゲートウェイメソッドを次のバリデータ宣言に関連付けて有効になります.
RestApiValidator:
Type: AWS::ApiGateway::RequestValidator
Properties:
RestApiId: !Ref RestApi
ValidateRequestBody: true
ValidateRequestParameters: true
REST API
この次のセクションは少し重いです.APIゲートウェイリソース、メソッド、および統合がどのように構築されるか.
まず、APIメソッドに関連する
logs
リソースを作成します.RestApiAppLogsResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId: !Ref RestApi
ParentId: !Ref RestApiAppResource
PathPart: "logs"
次に、CORSリクエストを処理するためのオプションメソッドを作成する必要があります.この実装はAllow-Origin: '*'
を使用します.これは実際のホストスキームとアプリケーションが提供される名前に置き換えられなければなりません.Lambdasharpは設定可能にするためにCloudFormationパラメータを使用しますが、これらは簡潔に省略されました.RestApiAppLogsResourceOPTIONS:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
RestApiId: !Ref RestApi
ResourceId: !Ref RestApiAppLogsResource
HttpMethod: OPTIONS
Integration:
IntegrationResponses:
- StatusCode: 204
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'"
method.response.header.Access-Control-Allow-Methods: "'OPTIONS,POST,PUT'"
method.response.header.Access-Control-Allow-Origin: "'*'"
method.response.header.Access-Control-Max-Age: "'600'"
ResponseTemplates:
application/json: ''
PassthroughBehavior: WHEN_NO_MATCH
RequestTemplates:
application/json: '{"statusCode": 200}'
Type: MOCK
MethodResponses:
- StatusCode: 204
ResponseModels:
application/json: 'Empty'
ResponseParameters:
method.response.header.Access-Control-Allow-Headers: false
method.response.header.Access-Control-Allow-Methods: false
method.response.header.Access-Control-Allow-Origin: false
method.response.header.Access-Control-Max-Age: false
オプションのリクエストを使用してブラウザが承認されると、POSTを使用して新しいログストリームを作成するための1つ、およびPUTを使用してログストリームに書き込むための別の2つの追加のエンドポイントを提供する必要があります.また、各エンドポイントのJSONスキーマモデルを定義して、実行前に要求を検証します.アプリケーションは、新しいログストリームを作成する責任があることに注意してください.シングルページアプリ(SPA)の場合は、新しいログストリームは、アプリケーションの負荷たびに作成する必要があります.また、これはLambdasharpで構築されたBlazor WBabassemblyアプリの動作です.
を作成します.アプリケーションログ
Postメソッドは、関連するロググループに新しいログストリームを作成します.応答処理のいくつかは、呼び出し元アプリケーションにエラーがどのように返されるかに関するものです.強調はあまりにも多くの内部の詳細を明らかにせずに有用なフィードバックを提供する上です.
オプションのメソッドと同様に、この構成は
Allow-Origin: '*'
を使用します.これは実際のホストスキームとアプリケーションが提供される名前に置き換えられます.Lambdasharpは設定可能にするためにCloudFormationパラメータを使用しますが、これらは簡潔に省略されました.RestApiAppLogsResourcePOST:
Type: AWS::ApiGateway::Method
Properties:
OperationName: CreateLogStream
ApiKeyRequired: true
RestApiId: !Ref RestApi
ResourceId: !Ref RestApiAppLogsResource
AuthorizationType: NONE
HttpMethod: POST
RequestModels:
application/json: !Ref RestApiAppLogsResourcePOSTRequestModel
RequestValidatorId: !Ref RestApiValidator
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:logs:action/CreateLogStream"
Credentials: !GetAtt RestApiRole.Arn
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestParameters:
integration.request.header.Content-Type: "'application/x-amz-json-1.1'"
integration.request.header.X-Amz-Target: "'Logs_20140328.CreateLogStream'"
RequestTemplates:
application/json: !Sub |-
#set($body = $input.path('$'))
{
"logGroupName": "${LogGroup}",
"logStreamName": "$body.logStreamName"
}
IntegrationResponses:
- SelectionPattern: "200"
StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
{ }
- SelectionPattern: "400"
StatusCode: 400
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
#set($body = $input.path('$'))
{
#if($body.message.isEmpty())
"error": "Unknown error"
#else
"error": "$util.escapeJavaScript($body.message).replaceAll("\\'","'")"
#end
}
- StatusCode: 500
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
{
"error": "Unexpected response from service."
}
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
- StatusCode: 400
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
- StatusCode: 500
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
RestApiAppLogsResourcePOSTRequestModel:
Type: AWS::ApiGateway::Model
Properties:
Description: CreateLogStream
ContentType: application/json
RestApiId: !Ref RestApi
Schema:
$schema: http://json-schema.org/draft-04/schema#
type: object
properties:
logStreamName:
type: string
required:
- logStreamName
ログを追加します.アプリケーションログ
POSTメソッドと同様に、PUTメソッドは、受信要求を検証し、エラーが発生したときにどのような内部詳細が公開されるかを制限します.
オプションのメソッドと同様に、この構成は
Allow-Origin: '*'
を使用します.これは実際のホストスキームとアプリケーションが提供される名前に置き換えられます.Lambdasharpは設定可能にするためにCloudFormationパラメータを使用しますが、これらは簡潔に省略されました.RestApiAppLogsResourcePUT:
Type: AWS::ApiGateway::Method
Properties:
OperationName: PutLogEvents
ApiKeyRequired: true
RestApiId: !Ref RestApi
ResourceId: !Ref RestApiAppLogsResource
AuthorizationType: NONE
HttpMethod: PUT
RequestModels:
application/json: !Ref RestApiAppLogsResourcePUTRequestModel
RequestValidatorId: !Ref RestApiValidator
Integration:
Type: AWS
IntegrationHttpMethod: POST
Uri: !Sub "arn:${AWS::Partition}:apigateway:${AWS::Region}:logs:action/PutLogEvents"
Credentials: !GetAtt RestApiRole.Arn
PassthroughBehavior: WHEN_NO_TEMPLATES
RequestParameters:
integration.request.header.Content-Type: "'application/x-amz-json-1.1'"
integration.request.header.X-Amz-Target: "'Logs_20140328.PutLogEvents'"
integration.request.header.X-Amzn-Logs-Format: "'json/emf'"
RequestTemplates:
application/json: !Sub |-
#set($body = $input.path('$'))
{
"logEvents": [
#foreach($logEvent in $body.logEvents)
{
"message": "$util.escapeJavaScript($logEvent.message).replaceAll("\\'","'")",
"timestamp": $logEvent.timestamp
}#if($foreach.hasNext),#end
#end
],
"logGroupName": "${LogGroup}",
"logStreamName": "$body.logStreamName",
"sequenceToken": #if($body.sequenceToken.isEmpty()) null#else "$body.sequenceToken"#end
}
IntegrationResponses:
- SelectionPattern: "200"
StatusCode: 200
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
{
"nextSequenceToken": "$input.path('$.nextSequenceToken')"
}
- SelectionPattern: "400"
StatusCode: 400
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
#set($body = $input.path('$'))
#if($body.expectedSequenceToken.isEmpty())
{
#if($body.message.isEmpty())
"error": "Unknown error"
#else
"error": "$util.escapeJavaScript($body.message).replaceAll("\\'","'")"
#end
}
#else
{
#if($body.message.isEmpty())
"error": "unknown error",
#else
"error": "$util.escapeJavaScript($body.message).replaceAll("\\'","'")",
#end
"nextSequenceToken": "$body.expectedSequenceToken"
}
#end
- StatusCode: 500
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: "'*'"
ResponseTemplates:
application/x-amz-json-1.1: |-
{
"error": "Unexpected response from service."
}
MethodResponses:
- StatusCode: 200
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
- StatusCode: 400
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
- StatusCode: 500
ResponseModels:
application/json: Empty
ResponseParameters:
method.response.header.Access-Control-Allow-Origin: false
RestApiAppLogsResourcePUTRequestModel:
Type: AWS::ApiGateway::Model
Properties:
Description: PutLogEvents
ContentType: application/json
RestApiId: !Ref RestApi
Schema:
$schema: http://json-schema.org/draft-04/schema#
type: object
properties:
logEvents:
type: array
items:
- type: object
properties:
message:
type: string
timestamp:
type: integer
required:
- message
- timestamp
logStreamName:
type: string
sequenceToken:
type:
- string
- "null"
required:
- logEvents
- logStreamName
APIと使用計画
次のリソースは、APIキーと使用計画を宣言します.APIキーは、デフォルトでCloudFormation Stack GuidのBase 64値に設定されます.フロントエンドアプリケーションがログ記録REST APIを使用するためにそれにアクセスする必要があるので、明示的にAPIキーを設定することをお勧めします.APIキーはさらにアプリケーションの内部値と組み合わせることによって難読化することができます.LambdSharpでは、APIキーは、CloudFormation Stack Guidとコンパイル済みの組み合わせを組み合わせて生成されます.NETコアアセンブリ識別子guid.
RestApiKey:
Type: AWS::ApiGateway::ApiKey
Properties:
Description: !Sub "${AWS::StackName} App API Key"
Enabled: true
StageKeys:
- RestApiId: !Ref RestApi
StageName: !Ref RestApiStage
Value:
Fn::Base64: !Select [ 2, !Split [ "/", !Ref AWS::StackId ]]
RestApiUsagePlan:
Type: AWS::ApiGateway::UsagePlan
Properties:
ApiStages:
- ApiId: !Ref RestApi
Stage: !Ref RestApiStage
Description: !Sub "${AWS::StackName} App API Usage Plan"
Throttle:
BurstLimit: 200
RateLimit: 100
RestApiUsagePlanKey:
Type: AWS::ApiGateway::UsagePlanKey
Properties:
KeyId: !Ref RestApiKey
KeyType: API_KEY
UsagePlanId: !Ref RestApiUsagePlan
API展開
最後に、展開リソースによって使用される
LATEST
というステージを定義します.CloudFormationは一度だけ配備を実行することに注意してください.その後のCloudFormationスタック更新は、残りのAPIが変更されたときに手動で配置する必要があります.Lambdasharpは、設定変更を常に自動的に適用できるように
を使用します.RestApiStage:
Type: AWS::ApiGateway::Stage
Properties:
DeploymentId: !Ref RestApiDeployment
Description: App API LATEST Stage
RestApiId: !Ref RestApi
StageName: LATEST
RestApiDeployment:
Type: AWS::ApiGateway::Deployment
Properties:
Description: !Sub "${AWS::StackName} App API"
RestApiId: !Ref RestApi
DependsOn:
- RestApiAppLogsResource
- RestApiAppLogsResourcePOST
- RestApiAppLogsResourcePOSTRequestModel
- RestApiAppLogsResourcePUT
- RestApiAppLogsResourcePUTRequestModel
Finalizer結論-継続する.
このポストでは、フロントエンドのアプリを直接CloudWatchにログオンするために必要なリソースを作成しました.次のポストでは、このREST API経由でログを記録するプロトコルをカバーします.
ハッピーハッキング!
Reference
この問題について(CloudWatchのWebアプリケーションログ(その1 )), 我々は、より多くの情報をここで見つけました https://dev.to/lambdasharp/cloudwatch-logging-for-web-apps-part-1-5935テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol