Skip to content

Commit 34a5684

Browse files
authored
feat(apigateway): support trimmed stats and latency graph configuration (#81)
Introduces latency factory methods with latency type parameter in Api Gateway. It will be useful for creating trimmed-mean alarms, etc. Closes #65 Closes #47 --- _By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license_
1 parent 78a9fe8 commit 34a5684

File tree

11 files changed

+2495
-641
lines changed

11 files changed

+2495
-641
lines changed

API.md

Lines changed: 877 additions & 9 deletions
Large diffs are not rendered by default.

lib/common/monitoring/alarms/LatencyAlarmFactory.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,33 @@ export class LatencyAlarmFactory {
148148
});
149149
}
150150

151+
addIntegrationLatencyAlarm(
152+
metric: MetricWithAlarmSupport,
153+
latencyType: LatencyType,
154+
props: LatencyThreshold,
155+
disambiguator?: string
156+
) {
157+
const alarmNameSuffix = `IntegrationLatency-${latencyType}`;
158+
159+
return this.alarmFactory.addAlarm(metric, {
160+
treatMissingData:
161+
props.treatMissingDataOverride ?? TreatMissingData.NOT_BREACHING,
162+
comparisonOperator:
163+
props.comparisonOperatorOverride ??
164+
ComparisonOperator.GREATER_THAN_THRESHOLD,
165+
...props,
166+
disambiguator,
167+
threshold: props.maxLatency.toMilliseconds(),
168+
alarmNameSuffix,
169+
// we will dedupe any kind of latency issue to the same alarm
170+
alarmDedupeStringSuffix: this.alarmFactory
171+
.shouldUseDefaultDedupeForLatency
172+
? "AnyLatency"
173+
: alarmNameSuffix,
174+
alarmDescription: `${latencyType} integration latency is too high.`,
175+
});
176+
}
177+
151178
addDurationAlarm(
152179
metric: MetricWithAlarmSupport,
153180
latencyType: LatencyType,

lib/monitoring/aws-apigateway/ApiGatewayMetricFactory.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { RestApiBase } from "monocdk/aws-apigateway";
22
import { DimensionHash } from "monocdk/aws-cloudwatch";
33

44
import {
5+
getLatencyTypeLabel,
6+
getLatencyTypeStatistic,
7+
LatencyType,
58
MetricFactory,
69
MetricStatistic,
710
RateComputationMethod,
@@ -122,33 +125,33 @@ export class ApiGatewayMetricFactory {
122125
);
123126
}
124127

128+
/**
129+
* @deprecated use metricLatencyInMillis instead
130+
*/
125131
metricLatencyP99InMillis() {
126-
return this.metricFactory.createMetric(
127-
"Latency",
128-
MetricStatistic.P99,
129-
"P99",
130-
this.dimensions,
131-
undefined,
132-
ApiGatewayNamespace
133-
);
132+
return this.metricLatencyInMillis(LatencyType.P99);
134133
}
135134

135+
/**
136+
* @deprecated use metricLatencyInMillis instead
137+
*/
136138
metricLatencyP90InMillis() {
137-
return this.metricFactory.createMetric(
138-
"Latency",
139-
MetricStatistic.P90,
140-
"P90",
141-
this.dimensions,
142-
undefined,
143-
ApiGatewayNamespace
144-
);
139+
return this.metricLatencyInMillis(LatencyType.P90);
145140
}
146141

142+
/**
143+
* @deprecated use metricLatencyInMillis instead
144+
*/
147145
metricLatencyP50InMillis() {
146+
return this.metricLatencyInMillis(LatencyType.P50);
147+
}
148+
149+
metricLatencyInMillis(latencyType: LatencyType) {
150+
const label = getLatencyTypeLabel(latencyType);
148151
return this.metricFactory.createMetric(
149152
"Latency",
150-
MetricStatistic.P50,
151-
"P50",
153+
getLatencyTypeStatistic(latencyType),
154+
label,
152155
this.dimensions,
153156
undefined,
154157
ApiGatewayNamespace

lib/monitoring/aws-apigateway/ApiGatewayMonitoring.ts

Lines changed: 82 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,42 @@ import {
3737
ApiGatewayMetricFactoryProps,
3838
} from "./ApiGatewayMetricFactory";
3939

40+
const DefaultLatencyTypesToRender = [
41+
LatencyType.P50,
42+
LatencyType.P90,
43+
LatencyType.P99,
44+
];
45+
4046
export interface ApiGatewayMonitoringOptions extends BaseMonitoringProps {
4147
readonly addLatencyP50Alarm?: Record<string, LatencyThreshold>;
48+
readonly addLatencyP70Alarm?: Record<string, LatencyThreshold>;
4249
readonly addLatencyP90Alarm?: Record<string, LatencyThreshold>;
4350
readonly addLatencyP99Alarm?: Record<string, LatencyThreshold>;
51+
readonly addLatencyP999Alarm?: Record<string, LatencyThreshold>;
52+
readonly addLatencyP9999Alarm?: Record<string, LatencyThreshold>;
53+
readonly addLatencyP100Alarm?: Record<string, LatencyThreshold>;
54+
readonly addLatencyTM50Alarm?: Record<string, LatencyThreshold>;
55+
readonly addLatencyTM70Alarm?: Record<string, LatencyThreshold>;
56+
readonly addLatencyTM90Alarm?: Record<string, LatencyThreshold>;
57+
readonly addLatencyTM99Alarm?: Record<string, LatencyThreshold>;
58+
readonly addLatencyTM999Alarm?: Record<string, LatencyThreshold>;
59+
readonly addLatencyTM9999Alarm?: Record<string, LatencyThreshold>;
60+
readonly addLatencyAverageAlarm?: Record<string, LatencyThreshold>;
4461
readonly add4XXErrorCountAlarm?: Record<string, ErrorCountThreshold>;
4562
readonly add4XXErrorRateAlarm?: Record<string, ErrorRateThreshold>;
4663
readonly add5XXFaultCountAlarm?: Record<string, ErrorCountThreshold>;
4764
readonly add5XXFaultRateAlarm?: Record<string, ErrorRateThreshold>;
4865
readonly addLowTpsAlarm?: Record<string, LowTpsThreshold>;
4966
readonly addHighTpsAlarm?: Record<string, HighTpsThreshold>;
67+
68+
/**
69+
* You can specify what latency types you want to be rendered in the dashboards.
70+
* Note: any latency type with an alarm will be also added automatically.
71+
* If the list is undefined, default values will be shown.
72+
* If the list is empty, only the latency types with an alarm will be shown (if any).
73+
* @default p50, p90, p99 (@see DefaultLatencyTypesToRender)
74+
*/
75+
readonly latencyTypesToRender?: LatencyType[];
5076
}
5177

5278
export interface ApiGatewayMonitoringProps
@@ -67,14 +93,15 @@ export class ApiGatewayMonitoring extends Monitoring {
6793
protected readonly errorRateAnnotations: HorizontalAnnotation[];
6894

6995
protected readonly tpsMetric: MetricWithAlarmSupport;
70-
protected readonly p50LatencyMetric: MetricWithAlarmSupport;
71-
protected readonly p90LatencyMetric: MetricWithAlarmSupport;
72-
protected readonly p99LatencyMetric: MetricWithAlarmSupport;
7396
protected readonly error4XXCountMetric: MetricWithAlarmSupport;
7497
protected readonly error4XXRateMetric: MetricWithAlarmSupport;
7598
protected readonly fault5XXCountMetric: MetricWithAlarmSupport;
7699
protected readonly fault5XXRateMetric: MetricWithAlarmSupport;
77100

101+
// keys are LatencyType, but JSII doesn't like non-string types
102+
protected readonly latencyMetrics: Record<string, MetricWithAlarmSupport>;
103+
protected readonly latencyTypesToRender: string[];
104+
78105
constructor(scope: MonitoringScope, props: ApiGatewayMonitoringProps) {
79106
super(scope, props);
80107

@@ -115,48 +142,60 @@ export class ApiGatewayMonitoring extends Monitoring {
115142
scope.createMetricFactory(),
116143
props
117144
);
145+
118146
this.tpsMetric = metricFactory.metricTps();
119-
this.p50LatencyMetric = metricFactory.metricLatencyP50InMillis();
120-
this.p90LatencyMetric = metricFactory.metricLatencyP90InMillis();
121-
this.p99LatencyMetric = metricFactory.metricLatencyP99InMillis();
147+
148+
this.latencyMetrics = {};
149+
this.latencyTypesToRender = [
150+
...(props.latencyTypesToRender ?? DefaultLatencyTypesToRender),
151+
];
152+
122153
this.error4XXCountMetric = metricFactory.metric4XXErrorCount();
123154
this.error4XXRateMetric = metricFactory.metric4XXErrorRate();
124155
this.fault5XXCountMetric = metricFactory.metric5XXFaultCount();
125156
this.fault5XXRateMetric = metricFactory.metric5XXFaultRate();
126157

127-
for (const disambiguator in props.addLatencyP50Alarm) {
128-
const alarmProps = props.addLatencyP50Alarm[disambiguator];
129-
const createdAlarm = this.latencyAlarmFactory.addLatencyAlarm(
130-
this.p50LatencyMetric,
131-
LatencyType.P50,
132-
alarmProps,
133-
disambiguator
134-
);
135-
this.latencyAnnotations.push(createdAlarm.annotation);
136-
this.addAlarm(createdAlarm);
137-
}
138-
for (const disambiguator in props.addLatencyP90Alarm) {
139-
const alarmProps = props.addLatencyP90Alarm[disambiguator];
140-
const createdAlarm = this.latencyAlarmFactory.addLatencyAlarm(
141-
this.p90LatencyMetric,
142-
LatencyType.P90,
143-
alarmProps,
144-
disambiguator
145-
);
146-
this.latencyAnnotations.push(createdAlarm.annotation);
147-
this.addAlarm(createdAlarm);
148-
}
149-
for (const disambiguator in props.addLatencyP99Alarm) {
150-
const alarmProps = props.addLatencyP99Alarm[disambiguator];
151-
const createdAlarm = this.latencyAlarmFactory.addLatencyAlarm(
152-
this.p99LatencyMetric,
153-
LatencyType.P99,
154-
alarmProps,
155-
disambiguator
156-
);
157-
this.latencyAnnotations.push(createdAlarm.annotation);
158-
this.addAlarm(createdAlarm);
158+
const latencyAlarmDefinitions = {
159+
[LatencyType.P50]: props.addLatencyP50Alarm,
160+
[LatencyType.P70]: props.addLatencyP70Alarm,
161+
[LatencyType.P90]: props.addLatencyP90Alarm,
162+
[LatencyType.P99]: props.addLatencyP99Alarm,
163+
[LatencyType.P999]: props.addLatencyP999Alarm,
164+
[LatencyType.P9999]: props.addLatencyP9999Alarm,
165+
[LatencyType.P100]: props.addLatencyP100Alarm,
166+
[LatencyType.TM50]: props.addLatencyTM50Alarm,
167+
[LatencyType.TM70]: props.addLatencyTM70Alarm,
168+
[LatencyType.TM90]: props.addLatencyTM90Alarm,
169+
[LatencyType.TM99]: props.addLatencyTM99Alarm,
170+
[LatencyType.TM999]: props.addLatencyTM999Alarm,
171+
[LatencyType.TM9999]: props.addLatencyTM9999Alarm,
172+
[LatencyType.AVERAGE]: props.addLatencyAverageAlarm,
173+
};
174+
175+
Object.values(LatencyType).forEach((latencyType) => {
176+
this.latencyMetrics[latencyType] =
177+
metricFactory.metricLatencyInMillis(latencyType);
178+
});
179+
180+
for (const [latencyType, alarmDefinition] of Object.entries(
181+
latencyAlarmDefinitions
182+
)) {
183+
for (const disambiguator in alarmDefinition) {
184+
const alarmProps = alarmDefinition[disambiguator];
185+
const latencyTypeEnum = latencyType as LatencyType;
186+
const metric = this.latencyMetrics[latencyTypeEnum];
187+
const createdAlarm = this.latencyAlarmFactory.addLatencyAlarm(
188+
metric,
189+
latencyTypeEnum,
190+
alarmProps,
191+
disambiguator
192+
);
193+
this.latencyAnnotations.push(createdAlarm.annotation);
194+
this.latencyTypesToRender.push(latencyTypeEnum);
195+
this.addAlarm(createdAlarm);
196+
}
159197
}
198+
160199
for (const disambiguator in props.add5XXFaultCountAlarm) {
161200
const alarmProps = props.add5XXFaultCountAlarm[disambiguator];
162201
const createdAlarm = this.errorAlarmFactory.addErrorCountAlarm(
@@ -262,15 +301,15 @@ export class ApiGatewayMonitoring extends Monitoring {
262301
}
263302

264303
protected createLatencyWidget(width: number, height: number) {
304+
const left = Array.from(new Set(this.latencyTypesToRender))
305+
.sort()
306+
.map((type) => this.latencyMetrics[type]);
307+
265308
return new GraphWidget({
266309
width,
267310
height,
268311
title: "Latency",
269-
left: [
270-
this.p50LatencyMetric,
271-
this.p90LatencyMetric,
272-
this.p99LatencyMetric,
273-
],
312+
left,
274313
leftYAxis: TimeAxisMillisFromZero,
275314
leftAnnotations: this.latencyAnnotations,
276315
});

lib/monitoring/aws-apigatewayv2/ApiGatewayV2HttpApiMetricFactory.ts

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { IHttpApi } from "monocdk/aws-apigatewayv2";
22
import { DimensionHash } from "monocdk/aws-cloudwatch";
33

44
import {
5+
getLatencyTypeLabel,
6+
getLatencyTypeStatistic,
7+
LatencyType,
58
MetricFactory,
69
MetricStatistic,
710
RateComputationMethod,
@@ -118,66 +121,66 @@ export class ApiGatewayV2HttpApiMetricFactory {
118121
);
119122
}
120123

124+
/**
125+
* @deprecated use metricLatencyInMillis instead
126+
*/
121127
metricLatencyP50InMillis() {
122-
return this.metricFactory.createMetric(
123-
"Latency",
124-
MetricStatistic.P50,
125-
"P50 Latency",
126-
this.dimensions,
127-
undefined,
128-
ApiGatewayNamespace
129-
);
128+
return this.metricLatencyInMillis(LatencyType.P50);
130129
}
131130

131+
/**
132+
* @deprecated use metricLatencyInMillis instead
133+
*/
132134
metricLatencyP90InMillis() {
133-
return this.metricFactory.createMetric(
134-
"Latency",
135-
MetricStatistic.P90,
136-
"P90 Latency",
137-
this.dimensions,
138-
undefined,
139-
ApiGatewayNamespace
140-
);
135+
return this.metricLatencyInMillis(LatencyType.P90);
141136
}
142137

138+
/**
139+
* @deprecated use metricLatencyInMillis instead
140+
*/
143141
metricLatencyP99InMillis() {
144-
return this.metricFactory.createMetric(
145-
"Latency",
146-
MetricStatistic.P99,
147-
"P99 Latency",
148-
this.dimensions,
149-
undefined,
150-
ApiGatewayNamespace
151-
);
142+
return this.metricLatencyInMillis(LatencyType.P99);
152143
}
153144

145+
/**
146+
* @deprecated use metricIntegrationLatencyInMillis instead
147+
*/
154148
metricIntegrationLatencyP50InMillis() {
155-
return this.metricFactory.createMetric(
156-
"IntegrationLatency",
157-
MetricStatistic.P50,
158-
"P50 Integration Latency",
159-
this.dimensions,
160-
undefined,
161-
ApiGatewayNamespace
162-
);
149+
return this.metricIntegrationLatencyInMillis(LatencyType.P50);
163150
}
164151

152+
/**
153+
* @deprecated use metricIntegrationLatencyInMillis instead
154+
*/
165155
metricIntegrationLatencyP90InMillis() {
156+
return this.metricIntegrationLatencyInMillis(LatencyType.P90);
157+
}
158+
159+
/**
160+
* @deprecated use metricIntegrationLatencyInMillis instead
161+
*/
162+
metricIntegrationLatencyP99InMillis() {
163+
return this.metricIntegrationLatencyInMillis(LatencyType.P99);
164+
}
165+
166+
metricIntegrationLatencyInMillis(latencyType: LatencyType) {
167+
const label = getLatencyTypeLabel(latencyType);
166168
return this.metricFactory.createMetric(
167169
"IntegrationLatency",
168-
MetricStatistic.P90,
169-
"P90 Integration Latency",
170+
getLatencyTypeStatistic(latencyType),
171+
label,
170172
this.dimensions,
171173
undefined,
172174
ApiGatewayNamespace
173175
);
174176
}
175177

176-
metricIntegrationLatencyP99InMillis() {
178+
metricLatencyInMillis(latencyType: LatencyType) {
179+
const label = getLatencyTypeLabel(latencyType);
177180
return this.metricFactory.createMetric(
178-
"IntegrationLatency",
179-
MetricStatistic.P99,
180-
"P99 Integration Latency",
181+
"Latency",
182+
getLatencyTypeStatistic(latencyType),
183+
label,
181184
this.dimensions,
182185
undefined,
183186
ApiGatewayNamespace

0 commit comments

Comments
 (0)