From 133e39938f63484f3d2e3bcc066f6d2a4277642f Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Thu, 1 Feb 2024 04:42:48 +0900 Subject: [PATCH] feat(cloudfront): retrieve default distribution metrics (#28894) This PR implements functions to retrieve [default distribution metrics](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html). ```ts declare const dist: cloudfront.Distribution const metric = dist.metricRequests() ``` Closes #28893 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- ...efaultTestDeployAssertB8AA280E.assets.json | 19 + ...aultTestDeployAssertB8AA280E.template.json | 36 ++ .../MyTestStack.assets.json | 19 + .../MyTestStack.template.json | 183 +++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 149 +++++++ .../tree.json | 381 ++++++++++++++++++ .../integ.distribution-default-metrics.ts | 41 ++ packages/aws-cdk-lib/aws-cloudfront/README.md | 31 +- .../aws-cloudfront/lib/distribution.ts | 54 +++ .../aws-cloudfront/test/distribution.test.ts | 22 +- 12 files changed, 941 insertions(+), 7 deletions(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json new file mode 100644 index 0000000000000..5a407d062b58e --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": { + "source": { + "path": "MyTestDefaultTestDeployAssertB8AA280E.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json new file mode 100644 index 0000000000000..ad9d0fb73d1dd --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json @@ -0,0 +1,36 @@ +{ + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.assets.json new file mode 100644 index 0000000000000..07bff9951aed2 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "6925d90c1b36859755bae5e304ac217d16e02eadd6c581f9d1cc501a2a40a52a": { + "source": { + "path": "MyTestStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "6925d90c1b36859755bae5e304ac217d16e02eadd6c581f9d1cc501a2a40a52a.json", + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}" + } + } + } + }, + "dockerImages": {} +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.template.json new file mode 100644 index 0000000000000..9978af28618d9 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/MyTestStack.template.json @@ -0,0 +1,183 @@ +{ + "Resources": { + "DistB3B78991": { + "Type": "AWS::CloudFront::Distribution", + "Properties": { + "DistributionConfig": { + "DefaultCacheBehavior": { + "CachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "Compress": true, + "TargetOriginId": "MyTestStackDistOrigin1CBA48A7B", + "ViewerProtocolPolicy": "allow-all" + }, + "Enabled": true, + "HttpVersion": "http2", + "IPV6Enabled": true, + "Origins": [ + { + "CustomOriginConfig": { + "OriginProtocolPolicy": "https-only" + }, + "DomainName": "www.example.com", + "Id": "MyTestStackDistOrigin1CBA48A7B" + } + ] + } + } + }, + "Alarm1F9009D71": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "Requests", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Sum", + "Threshold": 1 + } + }, + "Alarm2A7122E13": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "BytesUploaded", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Sum", + "Threshold": 1 + } + }, + "Alarm32341D8D9": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "BytesDownloaded", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Sum", + "Threshold": 1 + } + }, + "Alarm4671832C8": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "4xxErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm548383B2F": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "5xxErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm65738D89F": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "TotalErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + } + }, + "Parameters": { + "BootstrapVersion": { + "Type": "AWS::SSM::Parameter::Value", + "Default": "/cdk-bootstrap/hnb659fds/version", + "Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]" + } + }, + "Rules": { + "CheckBootstrapVersion": { + "Assertions": [ + { + "Assert": { + "Fn::Not": [ + { + "Fn::Contains": [ + [ + "1", + "2", + "3", + "4", + "5" + ], + { + "Ref": "BootstrapVersion" + } + ] + } + ] + }, + "AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI." + } + ] + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/cdk.out new file mode 100644 index 0000000000000..1f0068d32659a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/cdk.out @@ -0,0 +1 @@ +{"version":"36.0.0"} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/integ.json new file mode 100644 index 0000000000000..50b188c9d1840 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/integ.json @@ -0,0 +1,12 @@ +{ + "version": "36.0.0", + "testCases": { + "MyTest/DefaultTest": { + "stacks": [ + "MyTestStack" + ], + "assertionStack": "MyTest/DefaultTest/DeployAssert", + "assertionStackName": "MyTestDefaultTestDeployAssertB8AA280E" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/manifest.json new file mode 100644 index 0000000000000..f7d5cccf0a416 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/manifest.json @@ -0,0 +1,149 @@ +{ + "version": "36.0.0", + "artifacts": { + "MyTestStack.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "MyTestStack.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "MyTestStack": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "MyTestStack.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/6925d90c1b36859755bae5e304ac217d16e02eadd6c581f9d1cc501a2a40a52a.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "MyTestStack.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "MyTestStack.assets" + ], + "metadata": { + "/MyTestStack/Dist/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "DistB3B78991" + } + ], + "/MyTestStack/Alarm1/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm1F9009D71" + } + ], + "/MyTestStack/Alarm2/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm2A7122E13" + } + ], + "/MyTestStack/Alarm3/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm32341D8D9" + } + ], + "/MyTestStack/Alarm4/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm4671832C8" + } + ], + "/MyTestStack/Alarm5/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm548383B2F" + } + ], + "/MyTestStack/Alarm6/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm65738D89F" + } + ], + "/MyTestStack/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/MyTestStack/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "MyTestStack" + }, + "MyTestDefaultTestDeployAssertB8AA280E.assets": { + "type": "cdk:asset-manifest", + "properties": { + "file": "MyTestDefaultTestDeployAssertB8AA280E.assets.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "MyTestDefaultTestDeployAssertB8AA280E": { + "type": "aws:cloudformation:stack", + "environment": "aws://unknown-account/unknown-region", + "properties": { + "templateFile": "MyTestDefaultTestDeployAssertB8AA280E.template.json", + "terminationProtection": false, + "validateOnSynth": false, + "assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}", + "cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}", + "stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json", + "requiresBootstrapStackVersion": 6, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version", + "additionalDependencies": [ + "MyTestDefaultTestDeployAssertB8AA280E.assets" + ], + "lookupRole": { + "arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}", + "requiresBootstrapStackVersion": 8, + "bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version" + } + }, + "dependencies": [ + "MyTestDefaultTestDeployAssertB8AA280E.assets" + ], + "metadata": { + "/MyTest/DefaultTest/DeployAssert/BootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "BootstrapVersion" + } + ], + "/MyTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [ + { + "type": "aws:cdk:logicalId", + "data": "CheckBootstrapVersion" + } + ] + }, + "displayName": "MyTest/DefaultTest/DeployAssert" + }, + "Tree": { + "type": "cdk:tree", + "properties": { + "file": "tree.json" + } + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/tree.json new file mode 100644 index 0000000000000..ce99142367438 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.js.snapshot/tree.json @@ -0,0 +1,381 @@ +{ + "version": "tree-0.1", + "tree": { + "id": "App", + "path": "", + "children": { + "MyTestStack": { + "id": "MyTestStack", + "path": "MyTestStack", + "children": { + "Dist": { + "id": "Dist", + "path": "MyTestStack/Dist", + "children": { + "Origin1": { + "id": "Origin1", + "path": "MyTestStack/Dist/Origin1", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "Resource": { + "id": "Resource", + "path": "MyTestStack/Dist/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudFront::Distribution", + "aws:cdk:cloudformation:props": { + "distributionConfig": { + "enabled": true, + "origins": [ + { + "domainName": "www.example.com", + "id": "MyTestStackDistOrigin1CBA48A7B", + "customOriginConfig": { + "originProtocolPolicy": "https-only" + } + } + ], + "defaultCacheBehavior": { + "pathPattern": "*", + "targetOriginId": "MyTestStackDistOrigin1CBA48A7B", + "cachePolicyId": "658327ea-f89d-4fab-a63d-7e88639e58f6", + "compress": true, + "viewerProtocolPolicy": "allow-all" + }, + "httpVersion": "http2", + "ipv6Enabled": true + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudfront.CfnDistribution", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudfront.Distribution", + "version": "0.0.0" + } + }, + "Alarm1": { + "id": "Alarm1", + "path": "MyTestStack/Alarm1", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm1/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "Requests", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Sum", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "Alarm2": { + "id": "Alarm2", + "path": "MyTestStack/Alarm2", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm2/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "BytesUploaded", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Sum", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "Alarm3": { + "id": "Alarm3", + "path": "MyTestStack/Alarm3", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm3/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "BytesDownloaded", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Sum", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "Alarm4": { + "id": "Alarm4", + "path": "MyTestStack/Alarm4", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm4/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "4xxErrorRate", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Average", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "Alarm5": { + "id": "Alarm5", + "path": "MyTestStack/Alarm5", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm5/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "5xxErrorRate", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Average", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "Alarm6": { + "id": "Alarm6", + "path": "MyTestStack/Alarm6", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm6/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "TotalErrorRate", + "namespace": "AWS/CloudFront", + "period": 300, + "statistic": "Average", + "threshold": 1 + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.CfnAlarm", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudwatch.Alarm", + "version": "0.0.0" + } + }, + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "MyTestStack/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "MyTestStack/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + }, + "MyTest": { + "id": "MyTest", + "path": "MyTest", + "children": { + "DefaultTest": { + "id": "DefaultTest", + "path": "MyTest/DefaultTest", + "children": { + "Default": { + "id": "Default", + "path": "MyTest/DefaultTest/Default", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + }, + "DeployAssert": { + "id": "DeployAssert", + "path": "MyTest/DefaultTest/DeployAssert", + "children": { + "BootstrapVersion": { + "id": "BootstrapVersion", + "path": "MyTest/DefaultTest/DeployAssert/BootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnParameter", + "version": "0.0.0" + } + }, + "CheckBootstrapVersion": { + "id": "CheckBootstrapVersion", + "path": "MyTest/DefaultTest/DeployAssert/CheckBootstrapVersion", + "constructInfo": { + "fqn": "aws-cdk-lib.CfnRule", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.Stack", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTestCase", + "version": "0.0.0" + } + } + }, + "constructInfo": { + "fqn": "@aws-cdk/integ-tests-alpha.IntegTest", + "version": "0.0.0" + } + }, + "Tree": { + "id": "Tree", + "path": "Tree", + "constructInfo": { + "fqn": "constructs.Construct", + "version": "10.3.0" + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.App", + "version": "0.0.0" + } + } +} \ No newline at end of file diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.ts new file mode 100644 index 0000000000000..b77c6a410c5f6 --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-default-metrics.ts @@ -0,0 +1,41 @@ +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import { TestOrigin } from './test-origin'; +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch'; +import { IntegTest } from '@aws-cdk/integ-tests-alpha'; + +class DistributionMetricsTestStack extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + // CloudFront distribution setup + const distribution = new cloudfront.Distribution(this, 'Dist', { + defaultBehavior: { origin: new TestOrigin('www.example.com') }, + }); + + // Utility function to create alarms + const createAlarm = (alarmName: string, metric: cloudwatch.Metric) => { + return new cloudwatch.Alarm(this, alarmName, { + evaluationPeriods: 1, + threshold: 1, + comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_THRESHOLD, + metric: metric, + }); + }; + + createAlarm('Alarm1', distribution.metricRequests()); + createAlarm('Alarm2', distribution.metricBytesUploaded()); + createAlarm('Alarm3', distribution.metricBytesDownloaded()); + createAlarm('Alarm4', distribution.metric4xxErrorRate()); + createAlarm('Alarm5', distribution.metric5xxErrorRate()); + createAlarm('Alarm6', distribution.metricTotalErrorRate()); + } +} + +const app = new cdk.App(); +const stack = new DistributionMetricsTestStack(app, 'MyTestStack'); + +new IntegTest(app, 'MyTest', { + testCases: [stack], +}); diff --git a/packages/aws-cdk-lib/aws-cloudfront/README.md b/packages/aws-cdk-lib/aws-cloudfront/README.md index 79a055ba89a75..7eb960d4dcee4 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/README.md +++ b/packages/aws-cdk-lib/aws-cloudfront/README.md @@ -597,7 +597,36 @@ new cloudfront.Distribution(this, 'myDist', { }); ``` -### Additional CloudFront distribution metrics +### CloudFront Distribution Metrics + +You can view operational metrics about your CloudFront distributions. + +#### Default CloudFront Distribution Metrics + +The [following metrics are available by default](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html#monitoring-console.distributions) for all CloudFront distributions: + +- Total requests: The total number of viewer requests received by CloudFront for all HTTP methods and for both HTTP and HTTPS requests. +- Total bytes uploaded: The total number of bytes that viewers uploaded to your origin with CloudFront, using POST and PUT requests. +- Total bytes downloaded: The total number of bytes downloaded by viewers for GET, HEAD, and OPTIONS requests. +- Total error rate: The percentage of all viewer requests for which the response's HTTP status code was 4xx or 5xx. +- 4xx error rate: The percentage of all viewer requests for which the response's HTTP status code was 4xx. +- 5xx error rate: The percentage of all viewer requests for which the response's HTTP status code was 5xx. + +```ts +const dist = new cloudfront.Distribution(this, 'myDist', { + defaultBehavior: { origin: new origins.HttpOrigin('www.example.com') }, +}); + +// Retrieving default distribution metrics +const requestsMetric = dist.metricRequests(); +const bytesUploadedMetric = dist.metricBytesUploaded(); +const bytesDownloadedMetric = dist.metricBytesDownloaded(); +const totalErrorRateMetric = dist.metricTotalErrorRate(); +const http4xxErrorRateMetric = dist.metric4xxErrorRate(); +const http5xxErrorRateMetric = dist.metric5xxErrorRate(); +``` + +#### Additional CloudFront Distribution Metrics You can enable [additional CloudFront distribution metrics](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html#monitoring-console.distributions-additional), which include the following metrics: diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts index ce4d64d02ef24..f0747997c956f 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts @@ -392,6 +392,60 @@ export class Distribution extends Resource implements IDistribution { }); } + /** + * Metric for the total number of viewer requests received by CloudFront, for all HTTP methods and for both HTTP and HTTPS requests. + * + * @default - sum over 5 minutes + */ + public metricRequests(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('Requests', { statistic: 'sum', ...props }); + } + + /** + * Metric for the total number of bytes that viewers uploaded to your origin with CloudFront, using POST and PUT requests. + * + * @default - sum over 5 minutes + */ + public metricBytesUploaded(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('BytesUploaded', { statistic: 'sum', ...props }); + } + + /** + * Metric for the total number of bytes downloaded by viewers for GET, HEAD, and OPTIONS requests. + * + * @default - sum over 5 minutes + */ + public metricBytesDownloaded(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('BytesDownloaded', { statistic: 'sum', ...props }); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 4xx or 5xx. + * + * @default - average over 5 minutes + */ + public metricTotalErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('TotalErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 4xx. + * + * @default - average over 5 minutes + */ + public metric4xxErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('4xxErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 5xx. + * + * @default - average over 5 minutes + */ + public metric5xxErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return this.metric('5xxErrorRate', props); + } + /** * Metric for the total time spent from when CloudFront receives a request to when it starts providing a response to the network (not the viewer), * for requests that are served from the origin, not the CloudFront cache. diff --git a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts index 8efb67d82ccc8..abd135a418632 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts @@ -1283,18 +1283,28 @@ test('with publish additional metrics', () => { }); describe('Distribution metrics tests', () => { - const metrics = [ - { name: 'OriginLatency', method: 'metricOriginLatency', additionalMetricsRequired: true, errorMetricName: 'Origin latency' }, - { name: 'CacheHitRate', method: 'metricCacheHitRate', additionalMetricsRequired: true, errorMetricName: 'Cache hit rate' }, + const additionalMetrics = [ + { name: 'OriginLatency', method: 'metricOriginLatency', statistic: 'Average', additionalMetricsRequired: true, errorMetricName: 'Origin latency' }, + { name: 'CacheHitRate', method: 'metricCacheHitRate', statistic: 'Average', additionalMetricsRequired: true, errorMetricName: 'Cache hit rate' }, ...['401', '403', '404', '502', '503', '504'].map(errorCode => ({ name: `${errorCode}ErrorRate`, method: `metric${errorCode}ErrorRate`, + statistic: 'Average', additionalMetricsRequired: true, errorMetricName: `${errorCode} error rate`, })), ]; - test.each(metrics)('get %s metric', (metric) => { + const defaultMetrics = [ + { name: 'Requests', method: 'metricRequests', statistic: 'Sum', additionalMetricsRequired: false, errorMetricName: '' }, + { name: 'BytesDownloaded', method: 'metricBytesDownloaded', statistic: 'Sum', additionalMetricsRequired: false, errorMetricName: '' }, + { name: 'BytesUploaded', method: 'metricBytesUploaded', statistic: 'Sum', additionalMetricsRequired: false, errorMetricName: '' }, + { name: 'TotalErrorRate', method: 'metricTotalErrorRate', statistic: 'Average', additionalMetricsRequired: false, errorMetricName: '' }, + { name: '4xxErrorRate', method: 'metric4xxErrorRate', statistic: 'Average', additionalMetricsRequired: false, errorMetricName: '' }, + { name: '5xxErrorRate', method: 'metric5xxErrorRate', statistic: 'Average', additionalMetricsRequired: false, errorMetricName: '' }, + ]; + + test.each(additionalMetrics.concat(defaultMetrics))('get %s metric', (metric) => { const origin = defaultOrigin(); const dist = new Distribution(stack, 'MyDist', { defaultBehavior: { origin }, @@ -1307,12 +1317,12 @@ describe('Distribution metrics tests', () => { namespace: 'AWS/CloudFront', metricName: metric.name, dimensions: { DistributionId: dist.distributionId }, - statistic: 'Average', + statistic: metric.statistic, period: Duration.minutes(5), })); }); - test.each(metrics)('throw error when trying to get %s metric without publishing additional metrics', (metric) => { + test.each(additionalMetrics)('throw error when trying to get %s metric without publishing additional metrics', (metric) => { const origin = defaultOrigin(); const dist = new Distribution(stack, 'MyDist', { defaultBehavior: { origin },