Skip to content

Commit

Permalink
feat(cloudwatch): trimmed mean stat in graph widgets (#15316)
Browse files Browse the repository at this point in the history
Cloudwatch supports trimmed mean as a statistic in graph widgets.

Trimmed mean: https://en.wikipedia.org/wiki/Truncated_mean

Graph widgets can now take `tmNN.NN` to represent various
trimmed mean statistical ranges (similar to the existing percentile
ranges `pNN.NN`).

For instance, `tm90` represents the mean (or average) of all data
points after trimming out data points of the last 10 percentiles.

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
Niranjan Jayakar authored Jun 29, 2021
1 parent d2c76aa commit 60f6d82
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 6 deletions.
3 changes: 3 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,9 @@ function renderIfExtendedStatistic(statistic?: string): string | undefined {
// floating point rounding issues, return as-is but lowercase the p.
return statistic.toLowerCase();
}
if (parsed.type === 'trimmedMean') {
throw new Error('tmNN.NN stat is not supported in CloudWatch alarms');
}
return undefined;
}

Expand Down
1 change: 1 addition & 0 deletions packages/@aws-cdk/aws-cloudwatch/lib/metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface CommonMetricOptions {
* - "Sum" | "sum"
* - "SampleCount | "n"
* - "pNN.NN"
* - "tmNN.NN"
*
* @default Average
*/
Expand Down
25 changes: 19 additions & 6 deletions packages/@aws-cdk/aws-cloudwatch/lib/private/statistic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ export interface PercentileStatistic {
type: 'percentile';
percentile: number;
}

export interface TrimmedMeanStatistic {
type: 'trimmedMean';
trimmedMean: number;
}

/**
* Parse a statistic, returning the type of metric that was used (simple or percentile)
*/
export function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic {
export function parseStatistic(stat: string): SimpleStatistic | PercentileStatistic | TrimmedMeanStatistic {
const lowerStat = stat.toLowerCase();

// Simple statistics
Expand All @@ -35,17 +41,24 @@ export function parseStatistic(stat: string): SimpleStatistic | PercentileStatis
};
}

// Percentile statistics
const re = /^p([\d.]+)$/;
// Percentile or Trimmed Mean statistics
const re = /^(p|tm)([\d.]+)$/;
const m = re.exec(lowerStat);
if (m) {
if (m && m[1] === 'p') {
return {
type: 'percentile',
percentile: parseFloat(m[1]),
percentile: parseFloat(m[2]),
};
}
if (m && m[1] === 'tm') {
return {
type: 'trimmedMean',
trimmedMean: parseFloat(m[2]),
};
}

throw new Error(`Not a valid statistic: '${stat}', must be one of Average | Minimum | Maximum | SampleCount | Sum | pNN.NN`);
const supportedStats = ['Average', 'Minimum', 'Maximum', 'SampleCount', 'Sum', 'pNN.NN', 'tmNN.NN'];
throw new Error(`Not a valid statistic: '${stat}', must be one of ${supportedStats.join(' | ')}`);
}

export function normalizeStatistic(stat: string): string {
Expand Down
13 changes: 13 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ export = {
test.done();
},

'trimmedMean not supported'(test: Test) {
const stack = new Stack();

test.throws(() => new Alarm(stack, 'Alarm', {
metric: testMetric,
statistic: 'TM99',
threshold: 1000,
evaluationPeriods: 3,
}), /tmNN.NN stat is not supported/);

test.done();
},

'can set DatapointsToAlarm'(test: Test) {
// GIVEN
const stack = new Stack();
Expand Down
23 changes: 23 additions & 0 deletions packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,27 @@ export = {

test.done();
},

'trimmedMean stat is rendered'(test: Test) {
// GIVEN
const stack = new Stack();
const widget = new GraphWidget({
statistic: 'tm90',
});

// THEN
test.deepEqual(stack.resolve(widget.toJson()), [{
type: 'metric',
width: 6,
height: 6,
properties: {
view: 'timeSeries',
region: { Ref: 'AWS::Region' },
yAxis: {},
stat: 'tm90',
},
}]);

test.done();
},
};

0 comments on commit 60f6d82

Please sign in to comment.