From affbd93614d297306f1e51e096d7d46e4bf352f2 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 30 Nov 2022 12:11:35 +0100 Subject: [PATCH 1/3] feat(cloudwatch): `Stats` factory class for metric strings The `Statistic` enum type was incorrectly publicly exposed (it should only have been visible internally to the package), and was enhanced in PR #23074 to have more enum values such as `P10`, `P50`, `P99_9`, etc. The stringification of this `Statistic` type would only have worked in TypeScript anyway (in JSII languages like Java and Python we cannot rely on the string values of enums), and the fact that enums cannot be parameterized made it so that we used to have a lot of redundant enum values. Introduce a new factory class, `Stats`, whose sole purpose is to produce formatted strings to use as CloudWatch `statistic` values, and advertise the use of this class. (We probably shouldn't have been using `string` as the type in the first place, but given that we are factories to produce them seems to be the next best thing). --- .../lib/certificate-base.ts | 4 +- packages/@aws-cdk/aws-cloudwatch/README.md | 17 +- packages/@aws-cdk/aws-cloudwatch/lib/index.ts | 1 + .../aws-cloudwatch/lib/metric-types.ts | 376 +----------------- .../@aws-cdk/aws-cloudwatch/lib/metric.ts | 2 + .../aws-cloudwatch/lib/private/statistic.ts | 34 +- packages/@aws-cdk/aws-cloudwatch/lib/stats.ts | 189 +++++++++ .../aws-cloudwatch/test/stats.test.ts | 20 + 8 files changed, 257 insertions(+), 386 deletions(-) create mode 100644 packages/@aws-cdk/aws-cloudwatch/lib/stats.ts create mode 100644 packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts diff --git a/packages/@aws-cdk/aws-certificatemanager/lib/certificate-base.ts b/packages/@aws-cdk/aws-certificatemanager/lib/certificate-base.ts index de36b51a4ef00..adaa2abe7ae2e 100644 --- a/packages/@aws-cdk/aws-certificatemanager/lib/certificate-base.ts +++ b/packages/@aws-cdk/aws-certificatemanager/lib/certificate-base.ts @@ -1,5 +1,5 @@ import * as cloudwatch from '@aws-cdk/aws-cloudwatch'; -import { Statistic } from '@aws-cdk/aws-cloudwatch'; +import { Stats } from '@aws-cdk/aws-cloudwatch'; import { Duration, Resource } from '@aws-cdk/core'; import { ICertificate } from './certificate'; @@ -26,7 +26,7 @@ export abstract class CertificateBase extends Resource implements ICertificate { metricName: 'DaysToExpiry', namespace: 'AWS/CertificateManager', region: this.region, - statistic: Statistic.MINIMUM, + statistic: Stats.MINIMUM, }); } } diff --git a/packages/@aws-cdk/aws-cloudwatch/README.md b/packages/@aws-cdk/aws-cloudwatch/README.md index a7e007a05df07..e12e54d9d9f5c 100644 --- a/packages/@aws-cdk/aws-cloudwatch/README.md +++ b/packages/@aws-cdk/aws-cloudwatch/README.md @@ -136,14 +136,17 @@ to the metric function call: declare const fn: lambda.Function; const minuteErrorRate = fn.metricErrors({ - statistic: 'avg', + statistic: cloudwatch.Stats.AVERAGE, period: Duration.minutes(1), label: 'Lambda failure rate' }); ``` -This function also allows changing the metric label or color (which will be -useful when embedding them in graphs, see below). +The `statistic` field accepts a `string`; the `cloudwatch.Stats` object has a +number of predefined factory functions that help you constructs strings that are +appropriate for CloudWatch. The `metricErrors` function also allows changing the +metric label or color, which will be useful when embedding them in graphs (see +below). > Rates versus Sums > @@ -175,7 +178,7 @@ in the legend. For example, if you use: declare const fn: lambda.Function; const minuteErrorRate = fn.metricErrors({ - statistic: 'sum', + statistic: cloudwatch.Stats.SUM, period: Duration.hours(1), // Show the maximum hourly error count in the legend @@ -363,7 +366,7 @@ dashboard.addWidgets(new cloudwatch.GraphWidget({ left: [executionCountMetric], right: [errorCountMetric.with({ - statistic: "average", + statistic: cloudwatch.Stats.AVERAGE, label: "Error rate", color: cloudwatch.Color.GREEN, })] @@ -611,7 +614,7 @@ you can use the following widgets to pack widgets together in different ways: ### Column widget -A column widget contains other widgets and they will be laid out in a +A column widget contains other widgets and they will be laid out in a vertical column. Widgets will be put one after another in order. ```ts @@ -626,7 +629,7 @@ You can add a widget after object instantiation with the method ### Row widget -A row widget contains other widgets and they will be laid out in a +A row widget contains other widgets and they will be laid out in a horizontal row. Widgets will be put one after another in order. If the total width of the row exceeds the max width of the grid of 24 columns, the row will wrap automatically and adapt its height. diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/index.ts b/packages/@aws-cdk/aws-cloudwatch/lib/index.ts index fbbf8c7bb8b69..3a27d6be77458 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/index.ts @@ -12,6 +12,7 @@ export * from './log-query'; export * from './text'; export * from './widget'; export * from './alarm-status-widget'; +export * from './stats'; // AWS::CloudWatch CloudFormation Resources: export * from './cloudwatch.generated'; diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts index f77b95f57430b..e68cabd3c9524 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts @@ -54,6 +54,7 @@ export interface Dimension { * Statistic to use over the aggregation period * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html + * @deprecated Use one of the factory methods on `Stats` to produce statistics strings */ export enum Statistic { /** @@ -80,381 +81,6 @@ export enum Statistic { * You can use this value to determine high volumes of activity for your application. */ MAXIMUM = 'Maximum', - - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p10 is the 10th percentile and means that 10% of the data within the period is lower than this value and 90% of the data is higher than this value. - */ - P10 = 'p10', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p50 is the 50th percentile and means that 50% of the data within the period is lower than this value and 50% of the data is higher than this value. - */ - P50 = 'p50', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p90 is the 90th percentile and means that 90% of the data within the period is lower than this value and 10% of the data is higher than this value. - */ - P90 = 'p90', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p95 is the 95th percentile and means that 95% of the data within the period is lower than this value and 5% of the data is higher than this value. - */ - P95 = 'p95', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p99 is the 99th percentile and means that 99% of the data within the period is lower than this value and 1% of the data is higher than this value. - */ - P99 = 'p99', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p99.9 is the 99.9th percentile and means that 99.9% of the data within the period is lower than this value and 0.1% of the data is higher than this value. - */ - P99_9 = 'p99.9', - /** - * Percentile (p) indicates the relative standing of a value in a dataset. - * Percentiles help you get a better understanding of the distribution of your metric data. - * - * p99.99 is the 99.99th percentile and means that 99.9% of the data within the period is lower than this value and 0.01% of the data is higher than this value. - */ - P99_99 = 'p99.99', - - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm10 calculates the average after removing the 90% of data points with the highest values. - */ - TM10 = 'tm10', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm50 calculates the average after removing the 50% of data points with the highest values. - */ - TM50 = 'tm50', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm90 calculates the average after removing the 10% of data points with the highest values. - */ - TM90 = 'tm90', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm95 calculates the average after removing the 5% of data points with the highest values. - */ - TM95 = 'tm95', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm99 calculates the average after removing the 1% of data points with the highest values. - */ - TM99 = 'tm99', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm99.9 calculates the average after removing the 0.1% of data points with the highest values. - */ - TM99_9 = 'tm99.9', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * tm99 calculates the average after removing the 0.01% of data points with the highest values. - */ - TM99_99 = 'tm99.99', - - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * TM(1%:99%) calculates the average after removing the 1% lowest data points and the 1% highest data points. - */ - TM_1P_99P = 'TM(1%:99%)', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * TM(2%:98%) calculates the average after removing the 2% lowest data points and the 2% highest data points. - */ - TM_2P_98P = 'TM(2%:98%)', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * TM(5%:95%) calculates the average after removing the 5% lowest data points and the 5% highest data points. - */ - TM_5P_95P = 'TM(5%:95%)', - /** - * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. Values outside of the boundaries are ignored when the mean is calculated. - * You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. The numbers can be absolute values or percentages. - * - * TM(10%:90%) calculates the average after removing the 10% lowest data points and the 10% highest data points. - */ - TM_10P_90P = 'TM(10%:90%)', - - /** - * Interquartile mean (IQM) is the trimmed mean of the interquartile range, or the middle 50% of values. It is equivalent to TM(25%:75%). - */ - IQM = 'IQM', - - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm10 calculates the average while treating the 90% of the highest values to be equal to the value at the 10th percentile. - */ - WM10 = 'wm10', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm50 calculates the average while treating the 50% of the highest values to be equal to the value at the 50th percentile. - */ - WM50 = 'wm50', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm90 calculates the average while treating the 10% of the highest values to be equal to the value at the 90th percentile. - */ - WM90 = 'wm90', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm95 calculates the average while treating the 5% of the highest values to be equal to the value at the 95th percentile. - */ - WM95 = 'wm95', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm99 calculates the average while treating the 1% of the highest values to be equal to the value at the 99th percentile. - */ - WM99 = 'wm99', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm99.9 calculates the average while treating the 0.1% of the highest values to be equal to the value at the 99.9th percentile. - */ - WM99_9 = 'wm99.9', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * wm99.99 calculates the average while treating the 0.01% of the highest values to be equal to the value at the 99.99th percentile. - */ - WM99_99 = 'wm99.99', - - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * WM(1%:99%) calculates the average while treating the highest 1% of data points to be the value of the 99% boundary, - * and treating the lowest 1% of data points to be the value of the 1% boundary. - */ - WM_1P_99P = 'WM(1%:99%)', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * WM(2%:98%) calculates the average while treating the highest 2% of data points to be the value of the 98% boundary, - * and treating the lowest 2% of data points to be the value of the 2% boundary. - */ - WM_2P_98P = 'WM(2%:98%)', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * WM(5%:95%) calculates the average while treating the highest 5% of data points to be the value of the 95% boundary, - * and treating the lowest 5% of data points to be the value of the 5% boundary. - */ - WM_5P_95P = 'WM(5%:95%)', - /** - * Winsorized mean (WM) is similar to trimmed mean. However, with winsorized mean, the values that are outside the boundary are not ignored, - * but instead are considered to be equal to the value at the edge of the appropriate boundary. - * After this normalization, the average is calculated. You define the boundaries as one or two numbers between 0 and 100, up to 10 decimal places. - * - * WM(10%:90%) calculates the average while treating the highest 10% of data points to be the value of the 90% boundary, - * and treating the lowest 10% of data points to be the value of the 10% boundary. - */ - WM_10P_90P = 'WM(10%:90%)', - - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc10 returns the number of data points not including any data points that fall in the highest 90% of the values. - */ - TC10 = 'tc10', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc50 returns the number of data points not including any data points that fall in the highest 50% of the values. - */ - TC50 = 'tc50', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc90 returns the number of data points not including any data points that fall in the highest 10% of the values. - */ - TC90 = 'tc90', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc95 returns the number of data points not including any data points that fall in the highest 5% of the values. - */ - TC95 = 'tc95', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc99 returns the number of data points not including any data points that fall in the highest 1% of the values. - */ - TC99 = 'tc99', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc99.9 returns the number of data points not including any data points that fall in the highest 0.1% of the values. - */ - TC99_9 = 'tc99.9', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * tc99.99 returns the number of data points not including any data points that fall in the highest 0.01% of the values. - */ - TC99_99 = 'tc99.99', - - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * TC(1%:99%) returns the number of data points not including any data points that fall in the lowest 1% of the values and the highest 99% of the values. - */ - TC_1P_99P = 'TC(1%:99%)', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * TC(2%:98%) returns the number of data points not including any data points that fall in the lowest 2% of the values and the highest 98% of the values. - */ - TC_2P_98P = 'TC(2%:98%)', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * TC(5%:95%) returns the number of data points not including any data points that fall in the lowest 5% of the values and the highest 95% of the values. - */ - TC_5P_95P = 'TC(5%:95%)', - /** - * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. - * - * TC(10%:90%) returns the number of data points not including any data points that fall in the lowest 10% of the values and the highest 90% of the values. - */ - TC_10P_90P = 'TC(10%:90%)', - - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts10 returns the sum of the data points not including any data points that fall in the highest 90% of the values. - */ - TS10 = 'ts10', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts50 returns the sum of the data points not including any data points that fall in the highest 50% of the values. - */ - TS50 = 'ts50', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts90 returns the sum of the data points not including any data points that fall in the highest 10% of the values. - */ - TS90 = 'ts90', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts95 returns the sum of the data points not including any data points that fall in the highest 5% of the values. - */ - TS95 = 'ts95', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts99 returns the sum of the data points not including any data points that fall in the highest 1% of the values. - */ - TS99 = 'ts99', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts99.9 returns the sum of the data points not including any data points that fall in the highest 0.1% of the values. - */ - TS99_9 = 'ts99.9', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * ts99.99 returns the sum of the data points not including any data points that fall in the highest 0.01% of the values. - */ - TS99_99 = 'ts99.99', - - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * TS(1%:99%) returns the sum of the data points not including any data points that fall in the lowest 1% of the values and the highest 99% of the values. - */ - TS_1P_99P = 'TS(1%:99%)', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * TS(2%:98%) returns the sum of the data points not including any data points that fall in the lowest 2% of the values and the highest 98% of the values. - */ - TS_2P_98P = 'TS(2%:98%)', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * TS(5%:95%) returns the sum of the data points not including any data points that fall in the lowest 5% of the values and the highest 95% of the values. - */ - TS_5P_95P = 'TS(5%:95%)', - /** - * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. - * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. - * - * TS(10%:90%) returns the sum of the data points not including any data points that fall in the lowest 10% of the values and the highest 90% of the values. - */ - TS_10P_90P = 'TS(10%:90%)', } /** diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts index 06ecae7ca8edc..f3f9400712f02 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -38,6 +38,8 @@ export interface CommonMetricOptions { * - "tcNN.NN" | "tc(NN.NN%:NN.NN%)" * - "tsNN.NN" | "ts(NN.NN%:NN.NN%)" * + * Use the factory functions on the `Stats` object to construct valid input strings. + * * @default Average */ readonly statistic?: string; diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts index a306c1d925817..738a8bc11a25a 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts @@ -1,5 +1,3 @@ -import { Statistic } from '../metric-types'; - export interface SimpleStatistic { type: 'simple'; statistic: Statistic; @@ -66,4 +64,36 @@ export function normalizeStatistic(stat: string): string { // floating point rounding issues, return as-is but lowercase the p. return stat.toLowerCase(); } +} + +/** + * Enum for simple statistics + * + * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html + */ +export enum Statistic { + /** + * The count (number) of data points used for the statistical calculation. + */ + SAMPLE_COUNT = 'SampleCount', + + /** + * The value of Sum / SampleCount during the specified period. + */ + AVERAGE = 'Average', + /** + * All values submitted for the matching metric added together. + * This statistic can be useful for determining the total volume of a metric. + */ + SUM = 'Sum', + /** + * The lowest value observed during the specified period. + * You can use this value to determine low volumes of activity for your application. + */ + MINIMUM = 'Minimum', + /** + * The highest value observed during the specified period. + * You can use this value to determine high volumes of activity for your application. + */ + MAXIMUM = 'Maximum', } \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts b/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts new file mode 100644 index 0000000000000..d837b123f9de1 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts @@ -0,0 +1,189 @@ + +/** + * Factory functions for standard statistics strings + */ +export abstract class Stats { + /** + * The count (number) of data points used for the statistical calculation. + */ + public static readonly SAMPLE_COUNT = 'SampleCount'; + + /** + * The value of Sum / SampleCount during the specified period. + */ + public static readonly AVERAGE = 'Average'; + /** + * All values submitted for the matching metric added together. + * This statistic can be useful for determining the total volume of a metric. + */ + public static readonly SUM = 'Sum'; + + /** + * The lowest value observed during the specified period. + * You can use this value to determine low volumes of activity for your application. + */ + public static readonly MINIMUM = 'Minimum'; + + /** + * The highest value observed during the specified period. + * You can use this value to determine high volumes of activity for your application. + */ + public static readonly MAXIMUM = 'Maximum'; + + /** + * Interquartile mean (IQM) is the trimmed mean of the interquartile range, or the middle 50% of values. + * + * It is equivalent to `trimmedMean(25, 75)`. + */ + public static readonly IQM = 'IQM'; + + /** + * Percentile indicates the relative standing of a value in a dataset. + * + * Percentiles help you get a better understanding of the distribution of your metric data. + * + * For example, `p(90)` is the 90th percentile and means that 90% of the data + * within the period is lower than this value and 10% of the data is higher + * than this value. + */ + public static percentile(percentile: number) { + assertPercentage(percentile); + return `p${percentile}`; + } + + /** + * A shorter alias for `percentile()`. + */ + public static p(percentile: number) { + return Stats.percentile(percentile); + } + + /** + * Trimmed mean (TM) is the mean of all values that are between two specified boundaries. + * + * Values outside of the boundaries are ignored when the mean is calculated. + * You define the boundaries as one or two numbers between 0 and 100, up to 10 + * decimal places. The numbers are percentages. + * + * - If two numbers are given, they define the lower and upper bounds in percentages, + * respectively. + * - If one number is given, it defines the upper bound (the lower bound is assumed to + * be 0). + * + * For example, `tm(90)` calculates the average after removing the 10% of data + * points with the highest values; `tm(10, 90)` calculates the average after removing the + * 10% with the lowest and 10% with the highest values. + */ + public static trimmedMean(p1: number, p2?: number) { + return boundaryPercentileStat('tm', 'TM', p1, p2); + } + + /** + * A shorter alias for `trimmedMean()`. + */ + public static tm(p1: number, p2?: number) { + return Stats.trimmedMean(p1, p2); + } + + /** + * Winsorized mean (WM) is similar to trimmed mean. + * + * However, with winsorized mean, the values that are outside the boundary are + * not ignored, but instead are considered to be equal to the value at the + * edge of the appropriate boundary. After this normalization, the average is + * calculated. You define the boundaries as one or two numbers between 0 and + * 100, up to 10 decimal places. + * + * - If two numbers are given, they define the lower and upper bounds in percentages, + * respectively. + * - If one number is given, it defines the upper bound (the lower bound is assumed to + * be 0). + * + * For example, `tm(90)` calculates the average after removing the 10% of data + * points with the highest values; `tm(10, 90)` calculates the average after removing the + * 10% with the lowest and 10% with the highest values. + * + * For example, `wm(90)` calculates the average while treating the 10% of the + * highest values to be equal to the value at the 90th percentile. + * `wm(10, 90)` calculates the average while treaing the bottom 10% and the + * top 10% of values to be equal to the boundary values. + */ + public static winsorizedMean(p1: number, p2?: number) { + return boundaryPercentileStat('wm', 'WM', p1, p2); + } + + /** + * A shorter alias for `winsorizedMean()`. + */ + public static wm(p1: number, p2?: number) { + return Stats.winsorizedMean(p1, p2); + } + + /** + * Trimmed count (TC) is the number of data points in the chosen range for a trimmed mean statistic. + * + * - If two numbers are given, they define the lower and upper bounds in percentages, + * respectively. + * - If one number is given, it defines the upper bound (the lower bound is assumed to + * be 0). + * + * For example, `tc(90)` returns the number of data points not including any + * data points that fall in the highest 10% of the values. `tc(10, 90)` + * returns the number of data points not including any data points that fall + * in the lowest 10% of the values and the highest 90% of the values. + */ + public static trimmedCount(p1: number, p2?: number) { + return boundaryPercentileStat('tc', 'TC', p1, p2); + } + + /** + * Shorter alias for `trimmedCount()`. + */ + public static tc(p1: number, p2?: number) { + return Stats.trimmedCount(p1, p2); + } + + /** + * Trimmed sum (TS) is the sum of the values of data points in a chosen range for a trimmed mean statistic. + * It is equivalent to `(Trimmed Mean) * (Trimmed count)`. + * + * - If two numbers are given, they define the lower and upper bounds in percentages, + * respectively. + * - If one number is given, it defines the upper bound (the lower bound is assumed to + * be 0). + * + * For example, `ts(90)` returns the sum of the data points not including any + * data points that fall in the highest 10% of the values. `ts(10, 90)` + * returns the sum of the data points not including any data points that fall + * in the lowest 10% of the values and the highest 90% of the values. + */ + public static trimmedSum(p1: number, p2?: number) { + return boundaryPercentileStat('ts', 'TS', p1, p2); + } + + /** + * Shorter alias for `trimmedSum()`. + */ + public static ts(p1: number, p2: number) { + return Stats.trimmedSum(p1, p2); + } +} + +function assertPercentage(x?: number) { + if (x !== undefined && (x < 0 || x > 100)) { + throw new Error(`Expecting a percentage, got: ${x}`); + } +} + +/** + * Formatting helper because all these stats look the same + */ +function boundaryPercentileStat(oneBoundaryStat: string, twoBoundaryStat: string, p1: number, p2: number | undefined) { + assertPercentage(p1); + assertPercentage(p2); + if (p2 !== undefined) { + return `${twoBoundaryStat}(${p1}%:${p2}%)`; + } else { + return `${oneBoundaryStat}${p1}`; + } +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts new file mode 100644 index 0000000000000..482112d4ed5f2 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts @@ -0,0 +1,20 @@ +import * as cloudwatch from '../lib'; + +test('spot check some constants', () => { + expect(cloudwatch.Stats.AVERAGE).toEqual('Average'); + expect(cloudwatch.Stats.IQM).toEqual('IQM'); + expect(cloudwatch.Stats.SAMPLE_COUNT).toEqual('SampleCount'); +}); + + +test('spot check percentiles', () => { + expect(cloudwatch.Stats.p(99)).toEqual('p99'); + expect(cloudwatch.Stats.p(99.9)).toEqual('p99.9'); + expect(cloudwatch.Stats.p(99.99)).toEqual('p99.99'); +}); + +test('spot check some trimmed means', () => { + expect(cloudwatch.Stats.tm(99)).toEqual('tm99'); + expect(cloudwatch.Stats.tm(99.9)).toEqual('tm99.9'); + expect(cloudwatch.Stats.tm(0.01, 99.99)).toEqual('TM(0.01%:99.99%)'); +}); \ No newline at end of file From 8c155d566110e9bf7a494f1d6efc0c90deb0647c Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 30 Nov 2022 12:19:04 +0100 Subject: [PATCH 2/3] Explain copied type definition --- packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts index 738a8bc11a25a..1d81a683da576 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts @@ -69,6 +69,9 @@ export function normalizeStatistic(stat: string): string { /** * Enum for simple statistics * + * (This is a private copy of the type in `metric-types.ts`; this type should always + * been private, the public one has been deprecated and isn't used anywhere). + * * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Statistics-definitions.html */ export enum Statistic { From 87e036c9cdbaca398f6e15a457ceb309d7843451 Mon Sep 17 00:00:00 2001 From: Rico Huijbers Date: Wed, 30 Nov 2022 12:29:55 +0100 Subject: [PATCH 3/3] Also add percentile rank, because why not --- packages/@aws-cdk/aws-cloudwatch/lib/stats.ts | 28 ++++++++++++++++++- .../aws-cloudwatch/test/stats.test.ts | 5 ++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts b/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts index d837b123f9de1..2a86d1837c642 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/stats.ts @@ -164,9 +164,35 @@ export abstract class Stats { /** * Shorter alias for `trimmedSum()`. */ - public static ts(p1: number, p2: number) { + public static ts(p1: number, p2?: number) { return Stats.trimmedSum(p1, p2); } + + /** + * Percentile rank (PR) is the percentage of values that meet a fixed threshold. + * + * - If two numbers are given, they define the lower and upper bounds in absolute values, + * respectively. + * - If one number is given, it defines the upper bound (the lower bound is assumed to + * be 0). + * + * For example, `percentileRank(300)` returns the percentage of data points that have a value of 300 or less. + * `percentileRank(100, 2000)` returns the percentage of data points that have a value between 100 and 2000. + */ + public static percentileRank(v1: number, v2?: number) { + if (v2 !== undefined) { + return `PR(${v1}:${v2})`; + } else { + return `PR(:${v1})`; + } + } + + /** + * Shorter alias for `percentileRank()`. + */ + public static pr(v1: number, v2?: number) { + return this.percentileRank(v1, v2); + } } function assertPercentage(x?: number) { diff --git a/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts b/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts index 482112d4ed5f2..a7739fac1739e 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/stats.test.ts @@ -17,4 +17,9 @@ test('spot check some trimmed means', () => { expect(cloudwatch.Stats.tm(99)).toEqual('tm99'); expect(cloudwatch.Stats.tm(99.9)).toEqual('tm99.9'); expect(cloudwatch.Stats.tm(0.01, 99.99)).toEqual('TM(0.01%:99.99%)'); +}); + +test('percentile rank', () => { + expect(cloudwatch.Stats.pr(300)).toEqual('PR(:300)'); + expect(cloudwatch.Stats.pr(100, 500)).toEqual('PR(100:500)'); }); \ No newline at end of file