Skip to content

Commit 22dc6da

Browse files
ran-isenbergRan Isenberg
andauthored
feature: alarms have action with sns (ran-isenberg#749)
Co-authored-by: Ran Isenberg <ran.isenberg@ranthebuilder.cloud>
1 parent eb8a009 commit 22dc6da

File tree

3 files changed

+60
-9
lines changed

3 files changed

+60
-9
lines changed

cdk/service/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
TABLE_NAME_OUTPUT = 'DbOutput'
88
IDEMPOTENCY_TABLE_NAME_OUTPUT = 'IdempotencyDbOutput'
99
APIGATEWAY = 'Apigateway'
10+
MONITORING_TOPIC = 'MonitoringTopic'
1011
GW_RESOURCE = 'orders'
1112
LAMBDA_LAYER_NAME = 'common'
1213
API_HANDLER_LAMBDA_MEMORY_SIZE = 128 # MB

cdk/service/monitoring.py

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
from aws_cdk import Duration, aws_apigateway
1+
import aws_cdk.aws_sns as sns
2+
from aws_cdk import Duration, aws_apigateway, CfnOutput
23
from aws_cdk import aws_dynamodb as dynamodb
34
from aws_cdk import aws_lambda as _lambda
4-
from cdk_monitoring_constructs import CustomMetricGroup, ErrorRateThreshold, LatencyThreshold, MetricStatistic, MonitoringFacade
5+
from cdk_monitoring_constructs import (
6+
AlarmFactoryDefaults,
7+
CustomMetricGroup,
8+
ErrorRateThreshold,
9+
LatencyThreshold,
10+
MetricStatistic,
11+
MonitoringFacade,
12+
SnsAlarmActionStrategy,
13+
)
14+
from aws_cdk import aws_kms as kms
515
from constructs import Construct
6-
16+
from aws_cdk import aws_iam as iam
717
from cdk.service import constants
818

919

@@ -20,11 +30,39 @@ def __init__(
2030
) -> None:
2131
super().__init__(scope, id_)
2232
self.id_ = id_
23-
self._build_high_level_dashboard(crud_api)
24-
self._build_low_level_dashboard(db, idempotency_table, functions)
33+
self.notification_topic = self._build_topic()
34+
self._build_high_level_dashboard(crud_api, self.notification_topic)
35+
self._build_low_level_dashboard(db, idempotency_table, functions, self.notification_topic)
36+
37+
def _build_topic(self) -> sns.Topic:
38+
key = kms.Key(
39+
self,
40+
'MyKey',
41+
description='KMS Key for SNS Topic Encryption',
42+
enable_key_rotation=True # Enables automatic key rotation
43+
)
44+
topic = sns.Topic(self, f'{self.id_}alarms', display_name=f'{self.id_}alarms', master_key=key)
45+
# Grant CloudWatch permissions to publish to the SNS topic
46+
topic.add_to_resource_policy(
47+
statement=iam.PolicyStatement(
48+
actions=['sns:Publish'],
49+
effect=iam.Effect.ALLOW,
50+
principals=[iam.ServicePrincipal('cloudwatch.amazonaws.com')],
51+
resources=[topic.topic_arn],
52+
))
53+
CfnOutput(self, id=constants.MONITORING_TOPIC, value=topic.topic_name).override_logical_id(constants.MONITORING_TOPIC)
54+
return topic
2555

26-
def _build_high_level_dashboard(self, crud_api: aws_apigateway.RestApi):
27-
high_level_facade = MonitoringFacade(self, f'{self.id_}HighFacade')
56+
def _build_high_level_dashboard(self, crud_api: aws_apigateway.RestApi, topic: sns.Topic):
57+
high_level_facade = MonitoringFacade(
58+
self,
59+
f'{self.id_}HighFacade',
60+
alarm_factory_defaults=AlarmFactoryDefaults(
61+
actions_enabled=True,
62+
alarm_name_prefix=self.id_,
63+
action=SnsAlarmActionStrategy(on_alarm_topic=topic),
64+
),
65+
)
2866
high_level_facade.add_large_header('Order REST API High Level Dashboard')
2967
high_level_facade.monitor_api_gateway(
3068
api=crud_api,
@@ -43,8 +81,16 @@ def _build_high_level_dashboard(self, crud_api: aws_apigateway.RestApi):
4381
group = CustomMetricGroup(metrics=[create_metric], title='Daily Order Requests')
4482
high_level_facade.monitor_custom(metric_groups=[group], human_readable_name='Daily KPIs', alarm_friendly_name='KPIs')
4583

46-
def _build_low_level_dashboard(self, db: dynamodb.Table, idempotency_table: dynamodb.Table, functions: list[_lambda.Function]):
47-
low_level_facade = MonitoringFacade(self, f'{self.id_}LowFacade')
84+
def _build_low_level_dashboard(self, db: dynamodb.Table, idempotency_table: dynamodb.Table, functions: list[_lambda.Function], topic: sns.Topic):
85+
low_level_facade = MonitoringFacade(
86+
self,
87+
f'{self.id_}LowFacade',
88+
alarm_factory_defaults=AlarmFactoryDefaults(
89+
actions_enabled=True,
90+
alarm_name_prefix=self.id_,
91+
action=SnsAlarmActionStrategy(on_alarm_topic=topic),
92+
),
93+
)
4894
low_level_facade.add_large_header('Orders REST API Low Level Dashboard')
4995
for func in functions:
5096
low_level_facade.monitor_lambda_function(

docs/best_practices/monitoring.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ For P90, P50 metrics, follow this [explanation.](https://www.dnv.com/article/ter
7878
For internal server errors rate, we define the following alarm:
7979
![5xx](../media/monitoring/alarm_5xx.png)
8080

81+
#### Actions
82+
83+
Alarms are of no use unless they have an action. We have configured the alarms to send an SNS notification to a new SNS topic.
84+
From there, you can connect any subscription - HTTPS/SMS/Email, etc. to notify your teams with the alarm details.
8185

8286
## **CDK Reference**
8387

0 commit comments

Comments
 (0)