Skip to content

Commit 7460d4e

Browse files
authored
feat: adding monitoring support for CloudWatch Canary (#75)
1 parent a14bc1a commit 7460d4e

File tree

13 files changed

+1844
-4
lines changed

13 files changed

+1844
-4
lines changed

API.md

Lines changed: 664 additions & 1 deletion
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ You can also browse the documentation at https://constructs.dev/packages/cdk-mon
9090
| AWS Billing (`.monitorBilling()`) | AWS account cost | | [Requires enabling](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/gs_monitor_estimated_charges_with_cloudwatch.html#gs_turning_on_billing_metrics) the **Receive Billing Alerts** option in AWS Console / Billing Preferences |
9191
| AWS Certificate Manager (`.monitorCertificate()`) | Certificate expiration | Days until expiration | |
9292
| AWS CloudFront (`.monitorCloudFrontDistribution()`) | TPS, traffic, latency, errors | Error rate, low/high TPS | |
93+
| AWS CloudWatch Synthetics Canary (`.monitorSyntheticsCanary()`) | Latency, error count/rate | Error count/rate, latency | |
9394
| AWS CodeBuild (`.monitorCodeBuildProject()`) | Build counts (total, successful, failed), failed rate, duration | Failed build count/rate, duration | |
9495
| AWS DynamoDB (`.monitorDynamoTable()`) | Read and write capacity provisioned / used | Consumed capacity, throttling, latency, errors | |
9596
| AWS DynamoDB Global Secondary Index (`.monitorDynamoTableGlobalSecondaryIndex()`) | Read and write capacity, indexing progress, throttled events | | |

lib/facade/MonitoringAspect.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import * as secretsmanager from "monocdk/aws-secretsmanager";
2121
import * as sns from "monocdk/aws-sns";
2222
import * as sqs from "monocdk/aws-sqs";
2323
import * as stepfunctions from "monocdk/aws-stepfunctions";
24+
import * as synthetics from "monocdk/aws-synthetics";
2425

2526
import { ElastiCacheClusterType } from "../monitoring";
2627
import { MonitoringAspectProps, MonitoringAspectType } from "./aspect-types";
@@ -61,7 +62,8 @@ export class MonitoringAspect implements IAspect {
6162
this.monitorSecretsManager(node);
6263
this.monitorSns(node);
6364
this.monitorSqs(node);
64-
this.monitorStepFuntions(node);
65+
this.monitorStepFunctions(node);
66+
this.monitorSyntheticsCanaries(node);
6567

6668
if (!this.addedNodeIndependentMonitoringToScope) {
6769
this.addedNodeIndependentMonitoringToScope = true;
@@ -340,7 +342,7 @@ export class MonitoringAspect implements IAspect {
340342
}
341343
}
342344

343-
private monitorStepFuntions(node: IConstruct) {
345+
private monitorStepFunctions(node: IConstruct) {
344346
const [isEnabled, props] = this.getMonitoringDetails(
345347
this.props.stepFunctions
346348
);
@@ -351,6 +353,18 @@ export class MonitoringAspect implements IAspect {
351353
});
352354
}
353355
}
356+
357+
private monitorSyntheticsCanaries(node: IConstruct) {
358+
const [isEnabled, props] = this.getMonitoringDetails(
359+
this.props.syntheticsCanaries
360+
);
361+
if (isEnabled && node instanceof synthetics.Canary) {
362+
this.monitoringFacade.monitorSyntheticsCanary({
363+
canary: node,
364+
...props,
365+
});
366+
}
367+
}
354368
}
355369

356370
export * from "./aspect-types";

lib/facade/MonitoringFacade.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ import {
101101
StepFunctionServiceIntegrationMonitoringProps,
102102
getQueueProcessingEc2ServiceMonitoring,
103103
getQueueProcessingFargateServiceMonitoring,
104+
SyntheticsCanaryMonitoringProps,
105+
SyntheticsCanaryMonitoring,
104106
} from "../monitoring";
105107
import { MonitoringAspect, MonitoringAspectProps } from "./MonitoringAspect";
106108

@@ -605,6 +607,12 @@ export class MonitoringFacade extends MonitoringScope {
605607
return this;
606608
}
607609

610+
monitorSyntheticsCanary(props: SyntheticsCanaryMonitoringProps) {
611+
const segment = new SyntheticsCanaryMonitoring(this, props);
612+
this.addSegment(segment, props);
613+
return this;
614+
}
615+
608616
monitorBilling(props?: BillingMonitoringProps) {
609617
const segment = new BillingMonitoring(this, props ?? {});
610618
this.addSegment(segment, props);

lib/facade/aspect-types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
SnsTopicMonitoringOptions,
2424
SqsQueueMonitoringOptions,
2525
StepFunctionMonitoringOptions,
26+
SyntheticsCanaryMonitoringOptions,
2627
} from "../monitoring";
2728

2829
export interface MonitoringAspectType<T> {
@@ -64,4 +65,5 @@ export interface MonitoringAspectProps {
6465
readonly sns?: MonitoringAspectType<SnsTopicMonitoringOptions>;
6566
readonly sqs?: MonitoringAspectType<SqsQueueMonitoringOptions>;
6667
readonly stepFunctions?: MonitoringAspectType<StepFunctionMonitoringOptions>;
68+
readonly syntheticsCanaries?: MonitoringAspectType<SyntheticsCanaryMonitoringOptions>;
6769
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { DimensionHash } from "monocdk/aws-cloudwatch";
2+
import { Canary } from "monocdk/aws-synthetics";
3+
import {
4+
MetricFactory,
5+
MetricStatistic,
6+
RateComputationMethod,
7+
} from "../../common/index";
8+
9+
const MetricNamespace = "CloudWatchSynthetics";
10+
11+
export interface SyntheticsCanaryMetricFactoryProps {
12+
/**
13+
* CloudWatch Canary to monitor
14+
*/
15+
readonly canary: Canary;
16+
/**
17+
* Method used to calculate relative rates
18+
* @default average
19+
*/
20+
readonly rateComputationMethod?: RateComputationMethod;
21+
}
22+
23+
export class SyntheticsCanaryMetricFactory {
24+
protected readonly canary: Canary;
25+
protected readonly metricFactory: MetricFactory;
26+
protected readonly rateComputationMethod: RateComputationMethod;
27+
protected readonly dimensions: DimensionHash;
28+
29+
constructor(
30+
metricFactory: MetricFactory,
31+
props: SyntheticsCanaryMetricFactoryProps
32+
) {
33+
this.canary = props.canary;
34+
this.metricFactory = metricFactory;
35+
this.rateComputationMethod =
36+
props.rateComputationMethod ?? RateComputationMethod.AVERAGE;
37+
this.dimensions = {
38+
CanaryName: props.canary.canaryName,
39+
};
40+
}
41+
42+
metricLatencyAverageInMillis() {
43+
return this.metricFactory.adaptMetric(
44+
this.canary.metricDuration({
45+
label: "Average",
46+
statistic: MetricStatistic.AVERAGE,
47+
})
48+
);
49+
}
50+
51+
metricSuccessInPercent() {
52+
return this.metricFactory.adaptMetric(
53+
this.canary.metricSuccessPercent({
54+
label: "Success Rate",
55+
statistic: MetricStatistic.AVERAGE,
56+
})
57+
);
58+
}
59+
60+
metric4xxErrorCount() {
61+
return this.metricFactory.createMetric(
62+
"4xx",
63+
MetricStatistic.SUM,
64+
"4xx",
65+
this.dimensions,
66+
undefined,
67+
MetricNamespace
68+
);
69+
}
70+
71+
metric4xxErrorRate() {
72+
const metric = this.metric4xxErrorCount();
73+
return this.metricFactory.toRate(
74+
metric,
75+
this.rateComputationMethod,
76+
false,
77+
"errors"
78+
);
79+
}
80+
81+
metric5xxFaultCount() {
82+
return this.metricFactory.createMetric(
83+
"5xx",
84+
MetricStatistic.SUM,
85+
"5xx",
86+
this.dimensions,
87+
undefined,
88+
MetricNamespace
89+
);
90+
}
91+
92+
metric5xxFaultRate() {
93+
const metric = this.metric5xxFaultCount();
94+
return this.metricFactory.toRate(
95+
metric,
96+
this.rateComputationMethod,
97+
false,
98+
"faults"
99+
);
100+
}
101+
}

0 commit comments

Comments
 (0)