From 95d187e4aabf69b7210ffdf53e53eda077b46d6e Mon Sep 17 00:00:00 2001 From: kazuho cryer-shinozuka Date: Fri, 26 Jan 2024 03:44:45 +0900 Subject: [PATCH] feat(cloudfront): additional cloudfront distribution metrics (#28777) This PR supports the activation of [additional cloudfront distribution metrics](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html#monitoring-console.distributions-additional). ```ts new cloudfront.Distribution(this, 'myDist', { defaultBehavior: { origin: new origins.HttpOrigin('www.example.com') }, publishAdditionalMetrics: true, // added }); ``` Closes #22922. ---- *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 | 236 +++++++++ .../cdk.out | 1 + .../integ.json | 12 + .../manifest.json | 167 ++++++ .../tree.json | 478 ++++++++++++++++++ .../integ.distribution-additional-metrics.ts | 44 ++ packages/aws-cdk-lib/aws-cloudfront/README.md | 25 + .../aws-cloudfront/lib/distribution.ts | 154 +++++- .../aws-cloudfront/test/distribution.test.ts | 83 +++ 12 files changed, 1273 insertions(+), 1 deletion(-) create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.assets.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.template.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/cdk.out create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/integ.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/manifest.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/tree.json create mode 100644 packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.ts diff --git a/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-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-additional-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-additional-metrics.js.snapshot/MyTestDefaultTestDeployAssertB8AA280E.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-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-additional-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-additional-metrics.js.snapshot/MyTestStack.assets.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.assets.json new file mode 100644 index 0000000000000..fb427a5b9c20a --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.assets.json @@ -0,0 +1,19 @@ +{ + "version": "36.0.0", + "files": { + "a25a4f1a41d39c58512c39095bc8123af135549e7725115f685dd0ea3cd1b1c6": { + "source": { + "path": "MyTestStack.template.json", + "packaging": "file" + }, + "destinations": { + "current_account-current_region": { + "bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}", + "objectKey": "a25a4f1a41d39c58512c39095bc8123af135549e7725115f685dd0ea3cd1b1c6.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-additional-metrics.js.snapshot/MyTestStack.template.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.template.json new file mode 100644 index 0000000000000..bb2ed1f1a2dae --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/MyTestStack.template.json @@ -0,0 +1,236 @@ +{ + "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" + } + ] + } + } + }, + "DistMonitoringSubscriptionDD7607C2": { + "Type": "AWS::CloudFront::MonitoringSubscription", + "Properties": { + "DistributionId": { + "Ref": "DistB3B78991" + }, + "MonitoringSubscription": { + "RealtimeMetricsSubscriptionConfig": { + "RealtimeMetricsSubscriptionStatus": "Enabled" + } + } + } + }, + "Alarm1F9009D71": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "OriginLatency", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm2A7122E13": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "CacheHitRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm32341D8D9": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "401ErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm4671832C8": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "403ErrorRate", + "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": "404ErrorRate", + "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": "502ErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm77B1024B6": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "503ErrorRate", + "Namespace": "AWS/CloudFront", + "Period": 300, + "Statistic": "Average", + "Threshold": 1 + } + }, + "Alarm8AEE8034F": { + "Type": "AWS::CloudWatch::Alarm", + "Properties": { + "ComparisonOperator": "GreaterThanThreshold", + "Dimensions": [ + { + "Name": "DistributionId", + "Value": { + "Ref": "DistB3B78991" + } + } + ], + "EvaluationPeriods": 1, + "MetricName": "504ErrorRate", + "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-additional-metrics.js.snapshot/cdk.out b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-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-additional-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-additional-metrics.js.snapshot/integ.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-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-additional-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-additional-metrics.js.snapshot/manifest.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/manifest.json new file mode 100644 index 0000000000000..8c52583d3ef1c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/manifest.json @@ -0,0 +1,167 @@ +{ + "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}/a25a4f1a41d39c58512c39095bc8123af135549e7725115f685dd0ea3cd1b1c6.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/Dist/MonitoringSubscription": [ + { + "type": "aws:cdk:logicalId", + "data": "DistMonitoringSubscriptionDD7607C2" + } + ], + "/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/Alarm7/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm77B1024B6" + } + ], + "/MyTestStack/Alarm8/Resource": [ + { + "type": "aws:cdk:logicalId", + "data": "Alarm8AEE8034F" + } + ], + "/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-additional-metrics.js.snapshot/tree.json b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/tree.json new file mode 100644 index 0000000000000..61703754ffd8c --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.js.snapshot/tree.json @@ -0,0 +1,478 @@ +{ + "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" + } + }, + "MonitoringSubscription": { + "id": "MonitoringSubscription", + "path": "MyTestStack/Dist/MonitoringSubscription", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudFront::MonitoringSubscription", + "aws:cdk:cloudformation:props": { + "distributionId": { + "Ref": "DistB3B78991" + }, + "monitoringSubscription": { + "realtimeMetricsSubscriptionConfig": { + "realtimeMetricsSubscriptionStatus": "Enabled" + } + } + } + }, + "constructInfo": { + "fqn": "aws-cdk-lib.aws_cloudfront.CfnMonitoringSubscription", + "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": "OriginLatency", + "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" + } + }, + "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": "CacheHitRate", + "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" + } + }, + "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": "401ErrorRate", + "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" + } + }, + "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": "403ErrorRate", + "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": "404ErrorRate", + "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": "502ErrorRate", + "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" + } + }, + "Alarm7": { + "id": "Alarm7", + "path": "MyTestStack/Alarm7", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm7/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "503ErrorRate", + "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" + } + }, + "Alarm8": { + "id": "Alarm8", + "path": "MyTestStack/Alarm8", + "children": { + "Resource": { + "id": "Resource", + "path": "MyTestStack/Alarm8/Resource", + "attributes": { + "aws:cdk:cloudformation:type": "AWS::CloudWatch::Alarm", + "aws:cdk:cloudformation:props": { + "comparisonOperator": "GreaterThanThreshold", + "dimensions": [ + { + "name": "DistributionId", + "value": { + "Ref": "DistB3B78991" + } + } + ], + "evaluationPeriods": 1, + "metricName": "504ErrorRate", + "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-additional-metrics.ts b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.ts new file mode 100644 index 0000000000000..0bbe24007c94d --- /dev/null +++ b/packages/@aws-cdk-testing/framework-integ/test/aws-cloudfront/test/integ.distribution-additional-metrics.ts @@ -0,0 +1,44 @@ +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') }, + publishAdditionalMetrics: true, + }); + + // 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.metricOriginLatency()); + createAlarm('Alarm2', distribution.metricCacheHitRate()); + createAlarm('Alarm3', distribution.metric401ErrorRate()); + createAlarm('Alarm4', distribution.metric403ErrorRate()); + createAlarm('Alarm5', distribution.metric404ErrorRate()); + createAlarm('Alarm6', distribution.metric502ErrorRate()); + createAlarm('Alarm7', distribution.metric503ErrorRate()); + createAlarm('Alarm8', distribution.metric504ErrorRate()); + } +} + +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 6c9aa2d3a461c..79a055ba89a75 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/README.md +++ b/packages/aws-cdk-lib/aws-cloudfront/README.md @@ -597,6 +597,31 @@ new cloudfront.Distribution(this, 'myDist', { }); ``` +### 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: + +- 4xx and 5xx error rates: View 4xx and 5xx error rates by the specific HTTP status code, as a percentage of total requests. +- Origin latency: See the total time spent from when CloudFront receives a request to when it provides a response to the network (not the viewer), for responses that are served from the origin, not the CloudFront cache. +- Cache hit rate: View cache hits as a percentage of total cacheable requests, excluding errors. + +```ts +const dist = new cloudfront.Distribution(this, 'myDist', { + defaultBehavior: { origin: new origins.HttpOrigin('www.example.com') }, + publishAdditionalMetrics: true, +}); + +// Retrieving additional distribution metrics +const latencyMetric = dist.metricOriginLatency(); +const cacheHitRateMetric = dist.metricCacheHitRate(); +const http401ErrorRateMetric = dist.metric401ErrorRate(); +const http403ErrorRateMetric = dist.metric403ErrorRate(); +const http404ErrorRateMetric = dist.metric404ErrorRate(); +const http502ErrorRateMetric = dist.metric502ErrorRate(); +const http503ErrorRateMetric = dist.metric503ErrorRate(); +const http504ErrorRateMetric = dist.metric504ErrorRate(); +``` + ### HTTP Versions You can configure CloudFront to use a particular version of the HTTP protocol. By default, diff --git a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts index bc0e8d58c8d96..ce4d64d02ef24 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/lib/distribution.ts @@ -1,6 +1,6 @@ import { Construct } from 'constructs'; import { ICachePolicy } from './cache-policy'; -import { CfnDistribution } from './cloudfront.generated'; +import { CfnDistribution, CfnMonitoringSubscription } from './cloudfront.generated'; import { FunctionAssociation } from './function'; import { GeoRestriction } from './geo-restriction'; import { IKeyGroup } from './key-group'; @@ -11,6 +11,7 @@ import { formatDistributionArn } from './private/utils'; import { IRealtimeLogConfig } from './realtime-log-config'; import { IResponseHeadersPolicy } from './response-headers-policy'; import * as acm from '../../aws-certificatemanager'; +import * as cloudwatch from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; import * as lambda from '../../aws-lambda'; import * as s3 from '../../aws-s3'; @@ -255,6 +256,15 @@ export interface DistributionProps { * @default SSLMethod.SNI */ readonly sslSupportMethod?: SSLMethod; + + /** + * Whether to enable additional CloudWatch metrics. + * + * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/viewing-cloudfront-metrics.html + * + * @default false + */ + readonly publishAdditionalMetrics?: boolean; } /** @@ -298,6 +308,7 @@ export class Distribution extends Resource implements IDistribution { private readonly errorResponses: ErrorResponse[]; private readonly certificate?: acm.ICertificate; + private readonly publishAdditionalMetrics?: boolean; constructor(scope: Construct, id: string, props: DistributionProps) { super(scope, id); @@ -323,6 +334,7 @@ export class Distribution extends Resource implements IDistribution { this.certificate = props.certificate; this.errorResponses = props.errorResponses ?? []; + this.publishAdditionalMetrics = props.publishAdditionalMetrics; // Comments have an undocumented limit of 128 characters const trimmedComment = @@ -355,6 +367,146 @@ export class Distribution extends Resource implements IDistribution { this.domainName = distribution.attrDomainName; this.distributionDomainName = distribution.attrDomainName; this.distributionId = distribution.ref; + + if (props.publishAdditionalMetrics) { + new CfnMonitoringSubscription(this, 'MonitoringSubscription', { + distributionId: this.distributionId, + monitoringSubscription: { + realtimeMetricsSubscriptionConfig: { + realtimeMetricsSubscriptionStatus: 'Enabled', + }, + }, + }); + } + } + + /** + * Return the given named metric for this Distribution + */ + public metric(metricName: string, props?: cloudwatch.MetricOptions): cloudwatch.Metric { + return new cloudwatch.Metric({ + namespace: 'AWS/CloudFront', + metricName, + dimensionsMap: { DistributionId: this.distributionId }, + ...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. + * + * This is also known as first byte latency, or time-to-first-byte. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metricOriginLatency(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('Origin latency metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('OriginLatency', props); + } + + /** + * Metric for the percentage of all cacheable requests for which CloudFront served the content from its cache. + * + * HTTP POST and PUT requests, and errors, are not considered cacheable requests. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metricCacheHitRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('Cache hit rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('CacheHitRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 401. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric401ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('401 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('401ErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 403. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric403ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('403 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('403ErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 404. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric404ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('404 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('404ErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 502. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric502ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('502 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('502ErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 503. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric503ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('503 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('503ErrorRate', props); + } + + /** + * Metric for the percentage of all viewer requests for which the response's HTTP status code is 504. + * + * To obtain this metric, you need to set `publishAdditionalMetrics` to `true`. + * + * @default - average over 5 minutes + */ + public metric504ErrorRate(props?: cloudwatch.MetricOptions): cloudwatch.Metric { + if (this.publishAdditionalMetrics !== true) { + throw new Error('504 error rate metric is only available if \'publishAdditionalMetrics\' is set \'true\''); + } + return this.metric('504ErrorRate', props); } /** 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 bedd391865792..8efb67d82ccc8 100644 --- a/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts +++ b/packages/aws-cdk-lib/aws-cloudfront/test/distribution.test.ts @@ -1,6 +1,7 @@ import { defaultOrigin, defaultOriginGroup } from './test-origin'; import { Match, Template } from '../../assertions'; import * as acm from '../../aws-certificatemanager'; +import * as cloudwatch from '../../aws-cloudwatch'; import * as iam from '../../aws-iam'; import * as kinesis from '../../aws-kinesis'; import * as lambda from '../../aws-lambda'; @@ -1241,3 +1242,85 @@ test('render distribution behavior with realtime log config - multiple behaviors }, })); }); + +test('with publish additional metrics', () => { + const origin = defaultOrigin(); + new Distribution(stack, 'MyDist', { + defaultBehavior: { origin }, + publishAdditionalMetrics: true, + }); + + Template.fromStack(stack).hasResourceProperties('AWS::CloudFront::Distribution', { + DistributionConfig: { + DefaultCacheBehavior: { + CachePolicyId: '658327ea-f89d-4fab-a63d-7e88639e58f6', + Compress: true, + TargetOriginId: 'StackMyDistOrigin1D6D5E535', + ViewerProtocolPolicy: 'allow-all', + }, + Enabled: true, + HttpVersion: 'http2', + IPV6Enabled: true, + Origins: [{ + DomainName: 'www.example.com', + Id: 'StackMyDistOrigin1D6D5E535', + CustomOriginConfig: { + OriginProtocolPolicy: 'https-only', + }, + }], + }, + }); + Template.fromStack(stack).hasResourceProperties('AWS::CloudFront::MonitoringSubscription', { + DistributionId: { + Ref: 'MyDistDB88FD9A', + }, + MonitoringSubscription: { + RealtimeMetricsSubscriptionConfig: { + RealtimeMetricsSubscriptionStatus: 'Enabled', + }, + }, + }); +}); + +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' }, + ...['401', '403', '404', '502', '503', '504'].map(errorCode => ({ + name: `${errorCode}ErrorRate`, + method: `metric${errorCode}ErrorRate`, + additionalMetricsRequired: true, + errorMetricName: `${errorCode} error rate`, + })), + ]; + + test.each(metrics)('get %s metric', (metric) => { + const origin = defaultOrigin(); + const dist = new Distribution(stack, 'MyDist', { + defaultBehavior: { origin }, + publishAdditionalMetrics: metric.additionalMetricsRequired, + }); + + const metricObj = dist[metric.method](); + + expect(metricObj).toEqual(new cloudwatch.Metric({ + namespace: 'AWS/CloudFront', + metricName: metric.name, + dimensions: { DistributionId: dist.distributionId }, + statistic: 'Average', + period: Duration.minutes(5), + })); + }); + + test.each(metrics)('throw error when trying to get %s metric without publishing additional metrics', (metric) => { + const origin = defaultOrigin(); + const dist = new Distribution(stack, 'MyDist', { + defaultBehavior: { origin }, + publishAdditionalMetrics: false, + }); + + expect(() => { + dist[metric.method](); + }).toThrow(new RegExp(`${metric.errorMetricName} metric is only available if 'publishAdditionalMetrics' is set 'true'`)); + }); +});