Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
655 changes: 650 additions & 5 deletions API.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ You can also browse the documentation at https://constructs.dev/packages/cdk-mon
| AWS CloudFront (`.monitorCloudFrontDistribution()`) | TPS, traffic, latency, errors | Error rate, low/high TPS | |
| AWS CloudWatch Synthetics Canary (`.monitorSyntheticsCanary()`) | Latency, error count/rate | Error count/rate, latency | |
| AWS CodeBuild (`.monitorCodeBuildProject()`) | Build counts (total, successful, failed), failed rate, duration | Failed build count/rate, duration | |
| AWS DocumentDB (`.monitorDocumentDbCluster()`) | CPU, throttling, read/write latency, transactions, cursors | CPU | |
| AWS DynamoDB (`.monitorDynamoTable()`) | Read and write capacity provisioned / used | Consumed capacity, throttling, latency, errors | |
| AWS DynamoDB Global Secondary Index (`.monitorDynamoTableGlobalSecondaryIndex()`) | Read and write capacity, indexing progress, throttled events | | |
| AWS EC2 (`.monitorEC2Instances()`) | CPU, disk operations, network | | |
Expand Down
6 changes: 6 additions & 0 deletions lib/common/url/AwsConsoleUrlFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,12 @@ export class AwsConsoleUrlFactory {
}
}

getDocumentDbClusterUrl(clusterId: string): string | undefined {
const region = this.awsAccountRegion;
const destinationUrl = `https://${region}.console.aws.amazon.com/docdb/home?region=${region}#cluster-details/${clusterId}`;
return this.getAwsConsoleUrl(destinationUrl);
}

/**
* Resolves a destination URL within a resolution context.
* @param context The resolution context.
Expand Down
15 changes: 14 additions & 1 deletion lib/facade/MonitoringAspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as autoscaling from "aws-cdk-lib/aws-autoscaling";
import * as acm from "aws-cdk-lib/aws-certificatemanager";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as codebuild from "aws-cdk-lib/aws-codebuild";
import * as docdb from "aws-cdk-lib/aws-docdb";
import * as dynamodb from "aws-cdk-lib/aws-dynamodb";
import * as elasticsearch from "aws-cdk-lib/aws-elasticsearch";
import * as glue from "aws-cdk-lib/aws-glue";
Expand Down Expand Up @@ -51,6 +52,7 @@ export class MonitoringAspect implements IAspect {
this.monitorAutoScalingGroup(node);
this.monitorCloudFront(node);
this.monitorCodeBuild(node);
this.monitorDocumentDb(node);
this.monitorDynamoDb(node);
this.monitorGlue(node);
this.monitorKinesisAnalytics(node);
Expand Down Expand Up @@ -171,6 +173,17 @@ export class MonitoringAspect implements IAspect {
}
}

private monitorDocumentDb(node: IConstruct) {
const [isEnabled, props] = this.getMonitoringDetails(this.props.documentDb);
if (isEnabled && node instanceof docdb.DatabaseCluster) {
this.monitoringFacade.monitorDocumentDbCluster({
cluster: node,
alarmFriendlyName: node.node.path,
...props,
});
}
}

private monitorDynamoDb(node: IConstruct) {
const [isEnabled, props] = this.getMonitoringDetails(this.props.dynamoDB);
if (isEnabled && node instanceof dynamodb.Table) {
Expand Down Expand Up @@ -285,7 +298,7 @@ export class MonitoringAspect implements IAspect {
const [isEnabled, props] = this.getMonitoringDetails(this.props.rds);
if (isEnabled && node instanceof rds.DatabaseCluster) {
this.monitoringFacade.monitorRdsCluster({
clusterIdentifier: node.clusterIdentifier,
cluster: node,
alarmFriendlyName: node.node.path,
...props,
});
Expand Down
8 changes: 8 additions & 0 deletions lib/facade/MonitoringFacade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ import {
CodeBuildProjectMonitoringProps,
CustomMonitoring,
CustomMonitoringProps,
DocumentDbMonitoring,
DocumentDbMonitoringProps,
DynamoTableGlobalSecondaryIndexMonitoring,
DynamoTableGlobalSecondaryIndexMonitoringProps,
DynamoTableMonitoring,
Expand Down Expand Up @@ -390,6 +392,12 @@ export class MonitoringFacade extends MonitoringScope {
return this;
}

monitorDocumentDbCluster(props: DocumentDbMonitoringProps) {
const segment = new DocumentDbMonitoring(this, props);
this.addSegment(segment, props);
return this;
}

monitorDynamoTable(props: DynamoTableMonitoringProps) {
const segment = new DynamoTableMonitoring(this, props);
this.addSegment(segment, props);
Expand Down
2 changes: 2 additions & 0 deletions lib/facade/aspect-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
CertificateManagerMonitoringOptions,
CloudFrontDistributionMonitoringOptions,
CodeBuildProjectMonitoringOptions,
DocumentDbMonitoringOptions,
DynamoTableMonitoringOptions,
EC2MonitoringOptions,
ElastiCacheClusterMonitoringOptions,
Expand Down Expand Up @@ -50,6 +51,7 @@ export interface MonitoringAspectProps {
readonly billing?: MonitoringAspectType<BillingMonitoringOptions>;
readonly cloudFront?: MonitoringAspectType<CloudFrontDistributionMonitoringOptions>;
readonly codeBuild?: MonitoringAspectType<CodeBuildProjectMonitoringOptions>;
readonly documentDb?: MonitoringAspectType<DocumentDbMonitoringOptions>;
readonly dynamoDB?: MonitoringAspectType<DynamoTableMonitoringOptions>;
readonly ec2?: MonitoringAspectType<EC2MonitoringOptions>;
readonly elasticCache?: MonitoringAspectType<ElastiCacheClusterMonitoringOptions>;
Expand Down
99 changes: 99 additions & 0 deletions lib/monitoring/aws-docdb/DocumentDbMetricFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { DimensionsMap } from "aws-cdk-lib/aws-cloudwatch";
import { IDatabaseCluster } from "aws-cdk-lib/aws-docdb";

import {
getLatencyTypeLabel,
getLatencyTypeStatistic,
LatencyType,
MetricFactory,
MetricStatistic,
} from "../../common";

const DocumentDbNamespace = "AWS/DocDB";

export interface DocumentDbMetricFactoryProps {
/**
* database cluster
*/
readonly cluster: IDatabaseCluster;
}

export class DocumentDbMetricFactory {
readonly clusterIdentifier: string;
protected readonly metricFactory: MetricFactory;
protected readonly dimensionsMap: DimensionsMap;

constructor(
metricFactory: MetricFactory,
props: DocumentDbMetricFactoryProps
) {
this.metricFactory = metricFactory;
this.clusterIdentifier = props.cluster.clusterIdentifier;
this.dimensionsMap = { DBClusterIdentifier: this.clusterIdentifier };
}

metricAverageCpuUsageInPercent() {
return this.metric("CPUUtilization", MetricStatistic.AVERAGE, "CPU Usage");
}

metricMaxConnectionCount() {
return this.metric(
"DatabaseConnectionsMax",
MetricStatistic.MAX,
"Connections"
);
}

metricMaxCursorCount() {
return this.metric("DatabaseCursorsMax", MetricStatistic.MAX, "Cursors");
}

metricMaxTransactionOpenCount() {
return this.metric(
"TransactionsOpenMax",
MetricStatistic.MAX,
"Transactions"
);
}

metricOperationsThrottledDueLowMemoryCount() {
return this.metric(
"LowMemNumOperationsThrottled",
MetricStatistic.SUM,
"Operations throttled (low mem)"
);
}

metricReadLatencyInMillis(latencyType: LatencyType) {
const label = "Read " + getLatencyTypeLabel(latencyType);
return this.metric(
"ReadLatency",
getLatencyTypeStatistic(latencyType),
label
);
}

metricWriteLatencyInMillis(latencyType: LatencyType) {
const label = "Write " + getLatencyTypeLabel(latencyType);
return this.metric(
"WriteLatency",
getLatencyTypeStatistic(latencyType),
label
);
}

private metric(
metricName: string,
statistic: MetricStatistic,
label: string
) {
return this.metricFactory.createMetric(
metricName,
statistic,
label,
this.dimensionsMap,
undefined,
DocumentDbNamespace
);
}
}
171 changes: 171 additions & 0 deletions lib/monitoring/aws-docdb/DocumentDbMonitoring.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import {
GraphWidget,
HorizontalAnnotation,
IWidget,
} from "aws-cdk-lib/aws-cloudwatch";
import {
BaseMonitoringProps,
CountAxisFromZero,
DefaultGraphWidgetHeight,
DefaultSummaryWidgetHeight,
LatencyType,
MetricWithAlarmSupport,
Monitoring,
MonitoringScope,
PercentageAxisFromZeroToHundred,
QuarterWidth,
ThirdWidth,
TimeAxisMillisFromZero,
UsageAlarmFactory,
UsageThreshold,
} from "../../common";
import {
MonitoringHeaderWidget,
MonitoringNamingStrategy,
} from "../../dashboard";
import {
DocumentDbMetricFactory,
DocumentDbMetricFactoryProps,
} from "./DocumentDbMetricFactory";

export interface DocumentDbMonitoringOptions extends BaseMonitoringProps {
readonly addCpuUsageAlarm?: Record<string, UsageThreshold>;
}

export interface DocumentDbMonitoringProps
extends DocumentDbMetricFactoryProps,
DocumentDbMonitoringOptions {}

export class DocumentDbMonitoring extends Monitoring {
protected readonly title: string;
protected readonly url?: string;

protected readonly usageAlarmFactory: UsageAlarmFactory;
protected readonly usageAnnotations: HorizontalAnnotation[];

protected readonly cpuUsageMetric: MetricWithAlarmSupport;
protected readonly readLatencyMetric: MetricWithAlarmSupport;
protected readonly writeLatencyMetric: MetricWithAlarmSupport;
protected readonly connectionsMetric: MetricWithAlarmSupport;
protected readonly cursorsMetric: MetricWithAlarmSupport;
protected readonly transactionsMetric: MetricWithAlarmSupport;
protected readonly throttledMetric: MetricWithAlarmSupport;

constructor(scope: MonitoringScope, props: DocumentDbMonitoringProps) {
super(scope, props);

const metricFactory = new DocumentDbMetricFactory(
scope.createMetricFactory(),
props
);
this.cpuUsageMetric = metricFactory.metricAverageCpuUsageInPercent();
this.readLatencyMetric = metricFactory.metricReadLatencyInMillis(
LatencyType.P90
);
this.writeLatencyMetric = metricFactory.metricWriteLatencyInMillis(
LatencyType.P90
);
this.connectionsMetric = metricFactory.metricMaxConnectionCount();
this.cursorsMetric = metricFactory.metricMaxCursorCount();
this.transactionsMetric = metricFactory.metricMaxTransactionOpenCount();
this.throttledMetric =
metricFactory.metricOperationsThrottledDueLowMemoryCount();

const namingStrategy = new MonitoringNamingStrategy({
...props,
fallbackConstructName: metricFactory.clusterIdentifier,
namedConstruct: props.cluster,
});
this.title = namingStrategy.resolveHumanReadableName();
this.url = scope
.createAwsConsoleUrlFactory()
.getDocumentDbClusterUrl(metricFactory.clusterIdentifier);
const alarmFactory = this.createAlarmFactory(
namingStrategy.resolveAlarmFriendlyName()
);

this.usageAlarmFactory = new UsageAlarmFactory(alarmFactory);
this.usageAnnotations = [];

for (const disambiguator in props.addCpuUsageAlarm) {
const alarmProps = props.addCpuUsageAlarm[disambiguator];
const createdAlarm = this.usageAlarmFactory.addMaxCpuUsagePercentAlarm(
this.cpuUsageMetric,
alarmProps,
disambiguator
);
this.usageAnnotations.push(createdAlarm.annotation);
this.addAlarm(createdAlarm);
}

props.useCreatedAlarms?.consume(this.createdAlarms());
}

summaryWidgets(): IWidget[] {
return [
this.createTitleWidget(),
this.createResourceUsageWidget(ThirdWidth, DefaultSummaryWidgetHeight),
this.createConnectionsWidget(ThirdWidth, DefaultSummaryWidgetHeight),
this.createLatencyWidget(ThirdWidth, DefaultSummaryWidgetHeight),
];
}

widgets(): IWidget[] {
return [
this.createTitleWidget(),
this.createResourceUsageWidget(QuarterWidth, DefaultGraphWidgetHeight),
this.createConnectionsWidget(QuarterWidth, DefaultGraphWidgetHeight),
this.createTransactionsWidget(QuarterWidth, DefaultGraphWidgetHeight),
this.createLatencyWidget(QuarterWidth, DefaultGraphWidgetHeight),
];
}

protected createTitleWidget() {
return new MonitoringHeaderWidget({
family: "DocumentDB",
title: this.title,
goToLinkUrl: this.url,
});
}

protected createResourceUsageWidget(width: number, height: number) {
return new GraphWidget({
width,
height,
title: "CPU Usage",
left: [this.cpuUsageMetric],
leftYAxis: PercentageAxisFromZeroToHundred,
leftAnnotations: this.usageAnnotations,
});
}

protected createConnectionsWidget(width: number, height: number) {
return new GraphWidget({
width,
height,
title: "Connections",
left: [this.connectionsMetric],
leftYAxis: CountAxisFromZero,
});
}

protected createTransactionsWidget(width: number, height: number) {
return new GraphWidget({
width,
height,
title: "Transactions",
left: [this.transactionsMetric, this.cursorsMetric],
leftYAxis: CountAxisFromZero,
});
}

protected createLatencyWidget(width: number, height: number) {
return new GraphWidget({
width,
height,
title: "Latency",
left: [this.readLatencyMetric, this.writeLatencyMetric],
leftYAxis: TimeAxisMillisFromZero,
});
}
}
2 changes: 2 additions & 0 deletions lib/monitoring/aws-docdb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./DocumentDbMetricFactory";
export * from "./DocumentDbMonitoring";
Loading