diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts index 3b178dca42bab..6c314e036ff5f 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/step-scaling-policy.ts @@ -8,7 +8,7 @@ export interface BasicStepScalingPolicyProps { /** * Metric to scale on. */ - readonly metric: cloudwatch.Metric; + readonly metric: cloudwatch.IMetric; /** * The intervals for scaling. @@ -101,8 +101,8 @@ export class StepScalingPolicy extends cdk.Construct { } this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { - // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric, + periodSec: 60, // Recommended by AutoScaling alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LessThanOrEqualToThreshold, evaluationPeriods: 1, @@ -131,8 +131,8 @@ export class StepScalingPolicy extends cdk.Construct { } this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { - // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric, + periodSec: 60, // Recommended by AutoScaling alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanOrEqualToThreshold, evaluationPeriods: 1, @@ -180,8 +180,9 @@ export interface ScalingInterval { readonly change: number; } -function aggregationTypeFromMetric(metric: cloudwatch.Metric): MetricAggregationType { - switch (metric.statistic) { +function aggregationTypeFromMetric(metric: cloudwatch.IMetric): MetricAggregationType { + const statistic = metric.toAlarmConfig().statistic; + switch (statistic) { case 'Average': return MetricAggregationType.Average; case 'Minimum': @@ -189,7 +190,7 @@ function aggregationTypeFromMetric(metric: cloudwatch.Metric): MetricAggregation case 'Maximum': return MetricAggregationType.Maximum; default: - throw new Error(`Cannot only scale on 'Minimum', 'Maximum', 'Average' metrics, got ${metric.statistic}`); + throw new Error(`Cannot only scale on 'Minimum', 'Maximum', 'Average' metrics, got ${statistic}`); } } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts index d4c8487474592..158f4eda1a59e 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/lib/target-tracking-scaling-policy.ts @@ -89,7 +89,7 @@ export interface BasicTargetTrackingScalingPolicyProps extends BaseTargetTrackin * * @default - No custom metric. */ - readonly customMetric?: cloudwatch.Metric; + readonly customMetric?: cloudwatch.IMetric; } /** @@ -145,14 +145,20 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { } } -function renderCustomMetric(metric?: cloudwatch.Metric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined { +function renderCustomMetric(metric?: cloudwatch.IMetric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined { if (!metric) { return undefined; } + const c = metric.toAlarmConfig(); + + if (!c.statistic) { + throw new Error('Can only use Average, Minimum, Maximum, SampleCount, Sum statistic for target tracking'); + } + return { - dimensions: metric.dimensionsAsList(), - metricName: metric.metricName, - namespace: metric.namespace, - statistic: metric.statistic, - unit: metric.unit + dimensions: c.dimensions, + metricName: c.metricName, + namespace: c.namespace, + statistic: c.statistic, + unit: c.unit }; } diff --git a/packages/@aws-cdk/aws-applicationautoscaling/test/test.target-tracking.ts b/packages/@aws-cdk/aws-applicationautoscaling/test/test.target-tracking.ts index 213736e95fb11..3d7cdff38ee2a 100644 --- a/packages/@aws-cdk/aws-applicationautoscaling/test/test.target-tracking.ts +++ b/packages/@aws-cdk/aws-applicationautoscaling/test/test.target-tracking.ts @@ -46,7 +46,6 @@ export = { PolicyType: "TargetTrackingScaling", TargetTrackingScalingPolicyConfiguration: { CustomizedMetricSpecification: { - Dimensions: [], MetricName: "Metric", Namespace: "Test", Statistic: "Average" diff --git a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts index e2ad9f9437fbd..55c723bf8393d 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts @@ -806,7 +806,7 @@ export interface MetricTargetTrackingProps extends BaseTargetTrackingProps { * target value, your ASG should scale out, and if it's lower it should * scale in. */ - readonly metric: cloudwatch.Metric; + readonly metric: cloudwatch.IMetric; /** * Value to keep the metric around diff --git a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts index 3e3a126a4391d..c91e1098f6cdd 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/step-scaling-policy.ts @@ -8,7 +8,7 @@ export interface BasicStepScalingPolicyProps { /** * Metric to scale on. */ - readonly metric: cloudwatch.Metric; + readonly metric: cloudwatch.IMetric; /** * The intervals for scaling. @@ -102,8 +102,8 @@ export class StepScalingPolicy extends cdk.Construct { } this.lowerAlarm = new cloudwatch.Alarm(this, 'LowerAlarm', { - // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric, + periodSec: 60, // Recommended by AutoScaling alarmDescription: 'Lower threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.LessThanOrEqualToThreshold, evaluationPeriods: 1, @@ -133,7 +133,8 @@ export class StepScalingPolicy extends cdk.Construct { this.upperAlarm = new cloudwatch.Alarm(this, 'UpperAlarm', { // Recommended by AutoScaling - metric: props.metric.with({ periodSec: 60 }), + metric: props.metric, + periodSec: 60, // Recommended by AutoScaling alarmDescription: 'Upper threshold scaling alarm', comparisonOperator: cloudwatch.ComparisonOperator.GreaterThanOrEqualToThreshold, evaluationPeriods: 1, @@ -144,8 +145,9 @@ export class StepScalingPolicy extends cdk.Construct { } } -function aggregationTypeFromMetric(metric: cloudwatch.Metric): MetricAggregationType { - switch (metric.statistic) { +function aggregationTypeFromMetric(metric: cloudwatch.IMetric): MetricAggregationType { + const statistic = metric.toAlarmConfig().statistic; + switch (statistic) { case 'Average': return MetricAggregationType.Average; case 'Minimum': @@ -153,7 +155,7 @@ function aggregationTypeFromMetric(metric: cloudwatch.Metric): MetricAggregation case 'Maximum': return MetricAggregationType.Maximum; default: - throw new Error(`Cannot only scale on 'Minimum', 'Maximum', 'Average' metrics, got ${metric.statistic}`); + throw new Error(`Cannot only scale on 'Minimum', 'Maximum', 'Average' metrics, got ${statistic}`); } } diff --git a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts index 2bf1193c2e352..1f2ed373787b6 100644 --- a/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts +++ b/packages/@aws-cdk/aws-autoscaling/lib/target-tracking-scaling-policy.ts @@ -70,7 +70,7 @@ export interface BasicTargetTrackingScalingPolicyProps extends BaseTargetTrackin * * @default - No custom metric. */ - readonly customMetric?: cloudwatch.Metric; + readonly customMetric?: cloudwatch.IMetric; /** * The resource label associated with the predefined metric @@ -147,14 +147,20 @@ export class TargetTrackingScalingPolicy extends cdk.Construct { } } -function renderCustomMetric(metric?: cloudwatch.Metric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined { +function renderCustomMetric(metric?: cloudwatch.IMetric): CfnScalingPolicy.CustomizedMetricSpecificationProperty | undefined { if (!metric) { return undefined; } + const c = metric.toAlarmConfig(); + + if (!c.statistic) { + throw new Error('Can only use Average, Minimum, Maximum, SampleCount, Sum statistic for target tracking'); + } + return { - dimensions: metric.dimensionsAsList(), - metricName: metric.metricName, - namespace: metric.namespace, - statistic: metric.statistic, - unit: metric.unit + dimensions: c.dimensions, + metricName: c.metricName, + namespace: c.namespace, + statistic: c.statistic, + unit: c.unit }; } diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts index 249d4bc81f16e..b065867a8408e 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/alarm.ts @@ -2,8 +2,9 @@ import { Construct, IResource, Lazy, Resource, Stack } from '@aws-cdk/cdk'; import { IAlarmAction } from './alarm-action'; import { CfnAlarm } from './cloudwatch.generated'; import { HorizontalAnnotation } from './graph'; -import { Dimension, Metric, MetricAlarmProps, Statistic, Unit } from './metric'; -import { parseStatistic } from './util.statistic'; +import { CreateAlarmOptions } from './metric'; +import { IMetric } from './metric-types'; +import { normalizeStatistic } from './util.statistic'; export interface IAlarm extends IResource { /** @@ -20,14 +21,14 @@ export interface IAlarm extends IResource { /** * Properties for Alarms */ -export interface AlarmProps extends MetricAlarmProps { +export interface AlarmProps extends CreateAlarmOptions { /** * The metric to add the alarm on * * Metric objects can be obtained from most resources, or you can construct * custom Metric objects by instantiating one. */ - readonly metric: Metric; + readonly metric: IMetric; } /** @@ -102,7 +103,7 @@ export class Alarm extends Resource implements IAlarm { /** * The metric object this alarm was based on */ - public readonly metric: Metric; + public readonly metric: IMetric; private alarmActionArns?: string[]; private insufficientDataActionArns?: string[]; @@ -118,6 +119,8 @@ export class Alarm extends Resource implements IAlarm { const comparisonOperator = props.comparisonOperator || ComparisonOperator.GreaterThanOrEqualToThreshold; + const config = props.metric.toAlarmConfig(); + const alarm = new CfnAlarm(this, 'Resource', { // Meta alarmDescription: props.alarmDescription, @@ -138,7 +141,12 @@ export class Alarm extends Resource implements IAlarm { okActions: Lazy.listValue({ produce: () => this.okActionArns }), // Metric - ...metricJson(props.metric) + ...dropUndef(config), + ...dropUndef({ + // Alarm overrides + period: props.periodSec, + statistic: props.statistic && normalizeStatistic(props.statistic), + }) }); this.alarmArn = alarm.attrArn; @@ -146,7 +154,7 @@ export class Alarm extends Resource implements IAlarm { this.metric = props.metric; this.annotation = { // tslint:disable-next-line:max-line-length - label: `${this.metric.label || this.metric.metricName} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${props.evaluationPeriods} datapoints within ${describePeriod(props.evaluationPeriods * props.metric.periodSec)}`, + label: `${this.metric} ${OPERATOR_SYMBOLS[comparisonOperator]} ${props.threshold} for ${props.evaluationPeriods} datapoints within ${describePeriod(props.evaluationPeriods * config.period)}`, value: props.threshold, }; } @@ -223,61 +231,12 @@ function describePeriod(seconds: number) { return seconds + ' seconds'; } -/** - * Return the JSON structure which represents the given metric in an alarm. - */ -function metricJson(metric: Metric): AlarmMetricJson { - const stat = parseStatistic(metric.statistic); - - const dims = metric.dimensionsAsList(); - - return { - dimensions: dims.length > 0 ? dims : undefined, - namespace: metric.namespace, - metricName: metric.metricName, - period: metric.periodSec, - statistic: stat.type === 'simple' ? stat.statistic : undefined, - extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, - unit: metric.unit - }; -} - -/** - * Properties used to construct the Metric identifying part of an Alarm - */ -export interface AlarmMetricJson { - /** - * The dimensions to apply to the alarm - */ - readonly dimensions?: Dimension[]; - - /** - * Namespace of the metric - */ - readonly namespace: string; - - /** - * Name of the metric - */ - readonly metricName: string; - - /** - * How many seconds to aggregate over - */ - readonly period: number; - - /** - * Simple aggregation function to use - */ - readonly statistic?: Statistic; - - /** - * Percentile aggregation function to use - */ - readonly extendedStatistic?: string; - - /** - * The unit of the alarm - */ - readonly unit?: Unit; -} +function dropUndef(x: T): T { + const ret: any = {}; + for (const [key, value] of Object.entries(x)) { + if (value !== undefined) { + ret[key] = value; + } + } + return ret; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts index 4d7aae6e3f4b7..45bb76981c49f 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/dashboard.ts @@ -45,6 +45,15 @@ export interface DashboardProps { * @default Auto */ readonly periodOverride?: PeriodOverride; + + /** + * Initial set of widgets on the dashboard + * + * One array represents a row of widgets. + * + * @default - No widgets + */ + readonly widgets?: IWidget[][] } /** @@ -53,22 +62,26 @@ export interface DashboardProps { export class Dashboard extends Resource { private readonly rows: IWidget[] = []; - constructor(scope: Construct, id: string, props?: DashboardProps) { + constructor(scope: Construct, id: string, props: DashboardProps = {}) { super(scope, id); new CfnDashboard(this, 'Resource', { - dashboardName: (props && props.dashboardName) || undefined, + dashboardName: props.dashboardName, dashboardBody: Lazy.stringValue({ produce: () => { const column = new Column(...this.rows); column.position(0, 0); return Stack.of(this).toJsonString({ - start: props ? props.start : undefined, - end: props ? props.end : undefined, - periodOverride: props ? props.periodOverride : undefined, + start: props.start, + end: props.end, + periodOverride: props.periodOverride, widgets: column.toJson(), }); }}) }); + + (props.widgets || []).forEach(row => { + this.addWidgets(...row); + }); } /** @@ -80,7 +93,7 @@ export class Dashboard extends Resource { * Multiple widgets added in the same call to add() will be laid out next * to each other. */ - public add(...widgets: IWidget[]) { + public addWidgets(...widgets: IWidget[]) { if (widgets.length === 0) { return; } diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts index 249d0c0a739ed..aea30abd66927 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/graph.ts @@ -1,7 +1,6 @@ import cdk = require('@aws-cdk/cdk'); import { Alarm } from "./alarm"; -import { Metric } from "./metric"; -import { parseStatistic } from './util.statistic'; +import { IMetric } from "./metric-types"; import { ConcreteWidget } from "./widget"; /** @@ -123,12 +122,12 @@ export interface GraphWidgetProps extends MetricWidgetProps { /** * Metrics to display on left Y axis */ - readonly left?: Metric[]; + readonly left?: IMetric[]; /** * Metrics to display on right Y axis */ - readonly right?: Metric[]; + readonly right?: IMetric[]; /** * Annotations for the left Y axis @@ -200,7 +199,7 @@ export interface SingleValueWidgetProps extends MetricWidgetProps { /** * Metrics to display */ - readonly metrics: Metric[]; + readonly metrics: IMetric[]; } /** @@ -297,26 +296,27 @@ function mapAnnotation(yAxis: string): ((x: HorizontalAnnotation) => any) { * * This will be called by GraphWidget, no need for clients to call this. */ -function metricJson(metric: Metric, yAxis: string): any[] { +function metricJson(metric: IMetric, yAxis: string): any[] { + const config = metric.toGraphConfig(); + // Namespace and metric Name const ret: any[] = [ - metric.namespace, - metric.metricName, + config.namespace, + config.metricName, ]; // Dimensions - for (const dim of metric.dimensionsAsList()) { + for (const dim of (config.dimensions || [])) { ret.push(dim.name, dim.value); } // Options - const stat = parseStatistic(metric.statistic); ret.push({ yAxis, - label: metric.label, - color: metric.color, - period: metric.periodSec, - stat: stat.type === 'simple' ? stat.statistic : 'p' + stat.percentile.toString(), + label: config.label, + color: config.color, + period: config.period, + stat: config.statistic, }); return ret; diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/index.ts b/packages/@aws-cdk/aws-cloudwatch/lib/index.ts index 3f621c27f2519..93b206b3af83d 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/index.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/index.ts @@ -4,6 +4,7 @@ export * from './dashboard'; export * from './graph'; export * from './layout'; export * from './metric'; +export * from './metric-types'; export * from './text'; export * from './widget'; diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts new file mode 100644 index 0000000000000..d813a7dfbe8a9 --- /dev/null +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric-types.ts @@ -0,0 +1,159 @@ + +/** + * Interface for metrics + */ +export interface IMetric { + /** + * Turn this metric object into an alarm configuration + */ + toAlarmConfig(): MetricAlarmConfig; + + /** + * Turn this metric object into a graph configuration + */ + toGraphConfig(): MetricGraphConfig; +} + +/** + * Metric dimension + */ +export interface Dimension { + /** + * Name of the dimension + */ + readonly name: string; + + /** + * Value of the dimension + */ + readonly value: any; +} + +/** + * Statistic to use over the aggregation period + */ +export enum Statistic { + SampleCount = 'SampleCount', + Average = 'Average', + Sum = 'Sum', + Minimum = 'Minimum', + Maximum = 'Maximum', +} + +/** + * Unit for metric + */ +export enum Unit { + Seconds = 'Seconds', + Microseconds = 'Microseconds', + Milliseconds = 'Milliseconds', + Bytes_ = 'Bytes', + Kilobytes = 'Kilobytes', + Megabytes = 'Megabytes', + Gigabytes = 'Gigabytes', + Terabytes = 'Terabytes', + Bits = 'Bits', + Kilobits = 'Kilobits', + Megabits = 'Megabits', + Gigabits = 'Gigabits', + Terabits = 'Terabits', + Percent = 'Percent', + Count = 'Count', + BytesPerSecond = 'Bytes/Second', + KilobytesPerSecond = 'Kilobytes/Second', + MegabytesPerSecond = 'Megabytes/Second', + GigabytesPerSecond = 'Gigabytes/Second', + TerabytesPerSecond = 'Terabytes/Second', + BitsPerSecond = 'Bits/Second', + KilobitsPerSecond = 'Kilobits/Second', + MegabitsPerSecond = 'Megabits/Second', + GigabitsPerSecond = 'Gigabits/Second', + TerabitsPerSecond = 'Terabits/Second', + CountPerSecond = 'Count/Second', + None = 'None' +} + +/** + * Properties used to construct the Metric identifying part of an Alarm + */ +export interface MetricAlarmConfig { + /** + * The dimensions to apply to the alarm + */ + readonly dimensions?: Dimension[]; + + /** + * Namespace of the metric + */ + readonly namespace: string; + + /** + * Name of the metric + */ + readonly metricName: string; + + /** + * How many seconds to aggregate over + */ + readonly period: number; + + /** + * Simple aggregation function to use + */ + readonly statistic?: Statistic; + + /** + * Percentile aggregation function to use + */ + readonly extendedStatistic?: string; + + /** + * The unit of the alarm + */ + readonly unit?: Unit; +} + +/** + * Properties used to construct the Metric identifying part of a Graph + */ +export interface MetricGraphConfig { + /** + * The dimensions to apply to the alarm + */ + readonly dimensions?: Dimension[]; + + /** + * Namespace of the metric + */ + readonly namespace: string; + + /** + * Name of the metric + */ + readonly metricName: string; + + /** + * How many seconds to aggregate over + */ + readonly period: number; + + /** + * Label for the metric + */ + readonly label?: string; + + /** + * Color for the graph line + */ + readonly color?: string; + + /** + * Aggregation function to use (can be either simple or a percentile) + */ + readonly statistic?: string; + + /** + * The unit of the alarm + */ + readonly unit?: Unit; +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts index da81e2022beda..2c0384ddf9d16 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/metric.ts @@ -1,31 +1,15 @@ import iam = require('@aws-cdk/aws-iam'); import cdk = require('@aws-cdk/cdk'); import { Alarm, ComparisonOperator, TreatMissingData } from './alarm'; -import { normalizeStatistic } from './util.statistic'; +import { Dimension, IMetric, MetricAlarmConfig, MetricGraphConfig, Unit } from './metric-types'; +import { normalizeStatistic, parseStatistic } from './util.statistic'; export type DimensionHash = {[dim: string]: any}; /** - * Properties for a metric + * Options shared by most methods accepting metric options */ -export interface MetricProps { - /** - * Dimensions of the metric - * - * @default - No dimensions. - */ - readonly dimensions?: DimensionHash; - - /** - * Namespace of the metric. - */ - readonly namespace: string; - - /** - * Name of the metric. - */ - readonly metricName: string; - +export interface CommonMetricOptions { /** * The period over which the specified statistic is applied. * @@ -38,19 +22,26 @@ export interface MetricProps { /** * What function to use for aggregating. * - * Can be one of the following (case insensitive) + * Can be one of the following: * - * - "minimum" | "min" - * - "maximum" | "max" - * - "average" | "avg" - * - "sum" - * - "samplecount | "n" + * - "Minimum" | "min" + * - "Maximum" | "max" + * - "Average" | "avg" + * - "Sum" | "sum" + * - "SampleCount | "n" * - "pNN.NN" * * @default Average */ readonly statistic?: string; + /** + * Dimensions of the metric + * + * @default - No dimensions. + */ + readonly dimensions?: DimensionHash; + /** * Unit for the metric that is associated with the alarm */ @@ -67,6 +58,27 @@ export interface MetricProps { readonly color?: string; } +/** + * Properties for a metric + */ +export interface MetricProps extends CommonMetricOptions { + /** + * Namespace of the metric. + */ + readonly namespace: string; + + /** + * Name of the metric. + */ + readonly metricName: string; +} + +/** + * Properties of a metric that can be changed + */ +export interface MetricOptions extends CommonMetricOptions { +} + /** * A metric emitted by a service * @@ -81,7 +93,7 @@ export interface MetricProps { * Metric is an abstraction that makes it easy to specify metrics for use in both * alarms and graphs. */ -export class Metric { +export class Metric implements IMetric { /** * Grant permissions to the given identity to write metrics. * @@ -148,7 +160,7 @@ export class Metric { * Combines both properties that may adjust the metric (aggregation) as well * as alarm properties. */ - public newAlarm(scope: cdk.Construct, id: string, props: MetricAlarmProps): Alarm { + public createAlarm(scope: cdk.Construct, id: string, props: CreateAlarmOptions): Alarm { return new Alarm(scope, id, { metric: this.with({ statistic: props.statistic, @@ -166,10 +178,42 @@ export class Metric { }); } + public toAlarmConfig(): MetricAlarmConfig { + const stat = parseStatistic(this.statistic); + const dims = this.dimensionsAsList(); + + return { + dimensions: dims.length > 0 ? dims : undefined, + namespace: this.namespace, + metricName: this.metricName, + period: this.periodSec, + statistic: stat.type === 'simple' ? stat.statistic : undefined, + extendedStatistic: stat.type === 'percentile' ? 'p' + stat.percentile : undefined, + unit: this.unit + }; + } + + public toGraphConfig(): MetricGraphConfig { + return { + dimensions: this.dimensionsAsList(), + namespace: this.namespace, + metricName: this.metricName, + period: this.periodSec, + statistic: this.statistic, + unit: this.unit, + color: this.color, + label: this.label, + }; + } + + public toString() { + return this.label || this.metricName; + } + /** * Return the dimensions of this Metric as a list of Dimension. */ - public dimensionsAsList(): Dimension[] { + private dimensionsAsList(): Dimension[] { const dims = this.dimensions; if (dims === undefined) { @@ -182,121 +226,10 @@ export class Metric { } } -/** - * Metric dimension - */ -export interface Dimension { - /** - * Name of the dimension - */ - readonly name: string; - - /** - * Value of the dimension - */ - readonly value: any; -} - -/** - * Statistic to use over the aggregation period - */ -export enum Statistic { - SampleCount = 'SampleCount', - Average = 'Average', - Sum = 'Sum', - Minimum = 'Minimum', - Maximum = 'Maximum', -} - -/** - * Unit for metric - */ -export enum Unit { - Seconds = 'Seconds', - Microseconds = 'Microseconds', - Milliseconds = 'Milliseconds', - Bytes_ = 'Bytes', - Kilobytes = 'Kilobytes', - Megabytes = 'Megabytes', - Gigabytes = 'Gigabytes', - Terabytes = 'Terabytes', - Bits = 'Bits', - Kilobits = 'Kilobits', - Megabits = 'Megabits', - Gigabits = 'Gigabits', - Terabits = 'Terabits', - Percent = 'Percent', - Count = 'Count', - BytesPerSecond = 'Bytes/Second', - KilobytesPerSecond = 'Kilobytes/Second', - MegabytesPerSecond = 'Megabytes/Second', - GigabytesPerSecond = 'Gigabytes/Second', - TerabytesPerSecond = 'Terabytes/Second', - BitsPerSecond = 'Bits/Second', - KilobitsPerSecond = 'Kilobits/Second', - MegabitsPerSecond = 'Megabits/Second', - GigabitsPerSecond = 'Gigabits/Second', - TerabitsPerSecond = 'Terabits/Second', - CountPerSecond = 'Count/Second', - None = 'None' -} - -/** - * Properties of a metric that can be changed - */ -export interface MetricOptions { - /** - * Dimensions of the metric - * - * @default - No dimensions. - */ - readonly dimensions?: DimensionHash; - - /** - * The period over which the specified statistic is applied. - * - * Specify time in seconds, in multiples of 60. - * - * @default 300 - */ - readonly periodSec?: number; - - /** - * What function to use for aggregating. - * - * Can be one of the following: - * - * - "Minimum" | "min" - * - "Maximum" | "max" - * - "Average" | "avg" - * - "Sum" | "sum" - * - "SampleCount | "n" - * - "pNN.NN" - * - * @default Average - */ - readonly statistic?: string; - - /** - * Unit for the metric that is associated with the alarm - */ - readonly unit?: Unit; - - /** - * Label for this metric when added to a Graph in a Dashboard - */ - readonly label?: string; - - /** - * Color for this metric when added to a Graph in a Dashboard - */ - readonly color?: string; -} - /** * Properties needed to make an alarm from a metric */ -export interface MetricAlarmProps { +export interface CreateAlarmOptions { /** * The period over which the specified statistic is applied. * diff --git a/packages/@aws-cdk/aws-cloudwatch/lib/util.statistic.ts b/packages/@aws-cdk/aws-cloudwatch/lib/util.statistic.ts index d21910c5ea9bd..6f6c590287fdf 100644 --- a/packages/@aws-cdk/aws-cloudwatch/lib/util.statistic.ts +++ b/packages/@aws-cdk/aws-cloudwatch/lib/util.statistic.ts @@ -1,4 +1,4 @@ -import { Statistic } from "./metric"; +import { Statistic } from "./metric-types"; export interface SimpleStatistic { type: 'simple'; diff --git a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts index 8f62afdb4bd9e..a1dbacc5af0f9 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/integ.alarm-and-dashboard.ts @@ -20,7 +20,7 @@ const metric = new cloudwatch.Metric({ dimensions: { QueueName: queue.getAtt('QueueName') } }); -const alarm = metric.newAlarm(stack, 'Alarm', { +const alarm = metric.createAlarm(stack, 'Alarm', { threshold: 100, evaluationPeriods: 3, datapointsToAlarm: 2, @@ -32,20 +32,20 @@ const dashboard = new cloudwatch.Dashboard(stack, 'Dash', { end: '2018-12-17T06:00:00.000Z', periodOverride: PeriodOverride.Inherit }); -dashboard.add( +dashboard.addWidgets( new cloudwatch.TextWidget({ markdown: '# This is my dashboard' }), new cloudwatch.TextWidget({ markdown: 'you like?' }), ); -dashboard.add(new cloudwatch.AlarmWidget({ +dashboard.addWidgets(new cloudwatch.AlarmWidget({ title: 'Messages in queue', alarm, })); -dashboard.add(new cloudwatch.GraphWidget({ +dashboard.addWidgets(new cloudwatch.GraphWidget({ title: 'More messages in queue with alarm annotation', left: [metric], leftAnnotations: [alarm.toAnnotation()] })); -dashboard.add(new cloudwatch.SingleValueWidget({ +dashboard.addWidgets(new cloudwatch.SingleValueWidget({ title: 'Current messages in queue', metrics: [metric] })); diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts index bb219e2b76f77..dfa9f4d51eaf7 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.alarm.ts @@ -34,6 +34,58 @@ export = { test.done(); }, + 'override metric period in Alarm'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + new Alarm(stack, 'Alarm', { + metric: testMetric, + periodSec: 600, + threshold: 1000, + evaluationPeriods: 3, + }); + + // THEN + expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + ComparisonOperator: "GreaterThanOrEqualToThreshold", + EvaluationPeriods: 3, + MetricName: "Metric", + Namespace: "CDK/Test", + Period: 600, + Statistic: 'Average', + Threshold: 1000, + })); + + test.done(); + }, + + 'override statistic Alarm'(test: Test) { + // GIVEN + const stack = new Stack(); + + // WHEN + new Alarm(stack, 'Alarm', { + metric: testMetric, + statistic: 'max', + threshold: 1000, + evaluationPeriods: 3, + }); + + // THEN + expect(stack).to(haveResource('AWS::CloudWatch::Alarm', { + ComparisonOperator: "GreaterThanOrEqualToThreshold", + EvaluationPeriods: 3, + MetricName: "Metric", + Namespace: "CDK/Test", + Period: 300, + Statistic: 'Maximum', + Threshold: 1000, + })); + + test.done(); + }, + 'can set DatapointsToAlarm'(test: Test) { // GIVEN const stack = new Stack(); @@ -91,7 +143,7 @@ export = { const stack = new Stack(); // WHEN - testMetric.newAlarm(stack, 'Alarm', { + testMetric.createAlarm(stack, 'Alarm', { threshold: 1000, evaluationPeriods: 2, statistic: 'min', @@ -117,7 +169,7 @@ export = { const stack = new Stack(); // WHEN - testMetric.newAlarm(stack, 'Alarm', { + testMetric.createAlarm(stack, 'Alarm', { threshold: 1000, evaluationPeriods: 2, statistic: 'p99.9' diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts index ca2c46f0b018d..73fc6fa55288e 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.dashboard.ts @@ -10,17 +10,17 @@ export = { const dashboard = new Dashboard(stack, 'Dash'); // WHEN - dashboard.add(new TextWidget({ + dashboard.addWidgets(new TextWidget({ width: 10, height: 2, markdown: "first" })); - dashboard.add(new TextWidget({ + dashboard.addWidgets(new TextWidget({ width: 1, height: 4, markdown: "second" })); - dashboard.add(new TextWidget({ + dashboard.addWidgets(new TextWidget({ width: 4, height: 1, markdown: "third" @@ -42,7 +42,7 @@ export = { const dashboard = new Dashboard(stack, 'Dash'); // WHEN - dashboard.add( + dashboard.addWidgets( new TextWidget({ width: 10, height: 2, @@ -76,7 +76,7 @@ export = { const dashboard = new Dashboard(stack, 'Dash'); // WHEN - dashboard.add( + dashboard.addWidgets( new GraphWidget({ width: 1, height: 1 }) // GraphWidget has internal reference to current region ); @@ -103,7 +103,7 @@ export = { }); // WHEN - dashboard.add( + dashboard.addWidgets( new GraphWidget({ width: 1, height: 1 }) // GraphWidget has internal reference to current region ); diff --git a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts index 72641b3e4f6f9..64d731b8b4e8d 100644 --- a/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts +++ b/packages/@aws-cdk/aws-cloudwatch/test/test.graphs.ts @@ -94,7 +94,7 @@ export = { // GIVEN const stack = new Stack(); - const alarm = new Metric({ namespace: 'CDK', metricName: 'Test' }).newAlarm(stack, 'Alarm', { + const alarm = new Metric({ namespace: 'CDK', metricName: 'Test' }).createAlarm(stack, 'Alarm', { evaluationPeriods: 2, threshold: 1000 }); @@ -170,7 +170,7 @@ export = { const metric = new Metric({ namespace: 'CDK', metricName: 'Test' }); - const alarm = metric.newAlarm(stack, 'Alarm', { + const alarm = metric.createAlarm(stack, 'Alarm', { evaluationPeriods: 2, threshold: 1000 }); diff --git a/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts b/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts index 54a7c5e80fa2e..1b495a57b00ae 100644 --- a/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts +++ b/packages/@aws-cdk/aws-ecs/lib/base/scalable-task-count.ts @@ -139,7 +139,7 @@ export interface TrackCustomMetricProps extends appscaling.BaseTargetTrackingPro * - metric > targetValue => scale out * - metric < targetValue => scale in */ - readonly metric: cloudwatch.Metric; + readonly metric: cloudwatch.IMetric; /** * The target value to achieve for the metric diff --git a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.ts b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.ts index db93b586f3d57..a6b11691c8364 100644 --- a/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.ts +++ b/packages/@aws-cdk/aws-elasticloadbalancingv2/test/integ.alb.ts @@ -31,12 +31,12 @@ const group2 = listener.addTargets('ConditionalTarget', { targets: [new elbv2.IpTarget('10.0.1.2')] }); -group1.metricTargetResponseTime().newAlarm(stack, 'ResponseTimeHigh1', { +group1.metricTargetResponseTime().createAlarm(stack, 'ResponseTimeHigh1', { threshold: 5, evaluationPeriods: 2, }); -group2.metricTargetResponseTime().newAlarm(stack, 'ResponseTimeHigh2', { +group2.metricTargetResponseTime().createAlarm(stack, 'ResponseTimeHigh2', { threshold: 5, evaluationPeriods: 2, });