@@ -3,8 +3,11 @@ import { Construct } from 'constructs';
33import { EventRouter } from './eventRouter' ;
44import { EventProducer } from './eventProducer' ;
55import { EventConsumer } from './eventConsumer' ;
6- import { Alarm , Dashboard , GraphWidget , Metric , TextWidget , TreatMissingData } from 'aws-cdk-lib/aws-cloudwatch' ;
6+ import { Alarm , AlarmRule , CompositeAlarm , Dashboard , GraphWidget , Metric , TextWidget , TreatMissingData } from 'aws-cdk-lib/aws-cloudwatch' ;
77import { Queue } from 'aws-cdk-lib/aws-sqs' ;
8+ import { EmailSubscription } from 'aws-cdk-lib/aws-sns-subscriptions' ;
9+ import { Topic } from 'aws-cdk-lib/aws-sns' ;
10+ import { ServicePrincipal } from 'aws-cdk-lib/aws-iam' ;
811
912export interface EventMonitoringProps {
1013 router : EventRouter ;
@@ -16,6 +19,7 @@ export interface EventMonitoringProps {
1619export class EventMonitoring extends Construct {
1720 // Public properties
1821 public readonly dashboardName : string ;
22+ private readonly alarms : Alarm [ ] = [ ] ;
1923
2024 constructor ( scope : Construct , id : string , props ?: EventMonitoringProps ) {
2125 super ( scope , id ) ;
@@ -180,138 +184,158 @@ export class EventMonitoring extends Construct {
180184 } ) ;
181185 } ) ;
182186
183- // Create a new dashboard
184- const dashboard = new Dashboard ( this , 'EventMonitoringDashboard' , {
185- dashboardName : `${ id } -monitoring-dashboard`
186- } ) ;
187-
188- // Add a title
189- dashboard . addWidgets ( new TextWidget ( {
190- markdown : '# Event-Driven Architecture Monitoring' ,
191- width : 24 ,
192- height : 1
193- } ) ) ;
194-
195- // API Gateway metrics
196- dashboard . addWidgets (
197- new GraphWidget ( {
198- title : 'API Gateway Metrics' ,
199- width : 12 ,
200- height : 6 ,
201- left : [
202- props ?. producer . api . metricClientError ( ) ! ,
203- props ?. producer . api . metricServerError ( ) ! ,
204- ] ,
205- right : [
206- props ?. producer . api . metricLatency ( ) !
207- ]
208- } )
209- ) ;
210-
211- // EventBridge Bus metrics
212- dashboard . addWidgets (
213- new GraphWidget ( {
214- title : 'EventBridge Bus Metrics' ,
215- width : 12 ,
216- height : 6 ,
217- left : [
218- busInvocations ,
219- busFailedInvocations
220- ]
187+ // Create a new dashboard
188+ const dashboard = new Dashboard ( this , 'EventMonitoringDashboard' , {
189+ dashboardName : `${ id } -monitoring-dashboard`
190+ } ) ;
191+
192+ // Add a title
193+ dashboard . addWidgets ( new TextWidget ( {
194+ markdown : '# Event-Driven Architecture Monitoring' ,
195+ width : 24 ,
196+ height : 1
197+ } ) ) ;
198+
199+ // API Gateway metrics
200+ dashboard . addWidgets (
201+ new GraphWidget ( {
202+ title : 'API Gateway Metrics' ,
203+ width : 12 ,
204+ height : 6 ,
205+ left : [
206+ props ?. producer . api . metricClientError ( ) ! ,
207+ props ?. producer . api . metricServerError ( ) ! ,
208+ ] ,
209+ right : [
210+ props ?. producer . api . metricLatency ( ) !
211+ ]
212+ } )
213+ ) ;
214+
215+ // EventBridge Bus metrics
216+ dashboard . addWidgets (
217+ new GraphWidget ( {
218+ title : 'EventBridge Bus Metrics' ,
219+ width : 12 ,
220+ height : 6 ,
221+ left : [
222+ busInvocations ,
223+ busFailedInvocations
224+ ]
225+ } )
226+ ) ;
227+
228+ // EventBridge Rule metrics
229+ const ruleWidgets = props ?. router . rules ?. map ( ( rule ) => {
230+ return new GraphWidget ( {
231+ title : `EventBridge Rule: ${ rule . ruleName } ` ,
232+ width : 8 ,
233+ height : 6 ,
234+ left : [
235+ new Metric ( {
236+ namespace : 'AWS/Events' ,
237+ metricName : 'Invocations' ,
238+ dimensionsMap : { RuleName : rule . ruleName } ,
239+ period : cdk . Duration . minutes ( 5 ) ,
240+ statistic : 'Sum'
241+ } ) ,
242+ new Metric ( {
243+ namespace : 'AWS/Events' ,
244+ metricName : 'FailedInvocations' ,
245+ dimensionsMap : { RuleName : rule . ruleName } ,
246+ period : cdk . Duration . minutes ( 5 ) ,
247+ statistic : 'Sum'
248+ } ) ,
249+ new Metric ( {
250+ namespace : 'AWS/Events' ,
251+ metricName : 'ThrottledRules' ,
252+ dimensionsMap : { RuleName : rule . ruleName } ,
253+ period : cdk . Duration . minutes ( 5 ) ,
254+ statistic : 'Sum'
221255 } )
222- ) ;
223-
224- // EventBridge Rule metrics
225- const ruleWidgets = props ?. router . rules ?. map ( ( rule ) => {
226- return new GraphWidget ( {
227- title : `EventBridge Rule: ${ rule . ruleName } ` ,
228- width : 8 ,
229- height : 6 ,
230- left : [
231- new Metric ( {
232- namespace : 'AWS/Events' ,
233- metricName : 'Invocations' ,
234- dimensionsMap : { RuleName : rule . ruleName } ,
235- period : cdk . Duration . minutes ( 5 ) ,
236- statistic : 'Sum'
237- } ) ,
238- new Metric ( {
239- namespace : 'AWS/Events' ,
240- metricName : 'FailedInvocations' ,
241- dimensionsMap : { RuleName : rule . ruleName } ,
242- period : cdk . Duration . minutes ( 5 ) ,
243- statistic : 'Sum'
244- } ) ,
245- new Metric ( {
246- namespace : 'AWS/Events' ,
247- metricName : 'ThrottledRules' ,
248- dimensionsMap : { RuleName : rule . ruleName } ,
249- period : cdk . Duration . minutes ( 5 ) ,
250- statistic : 'Sum'
251- } )
252- ]
253- } ) ;
254- } ) || [ ] ;
255-
256- if ( ruleWidgets . length > 0 ) {
257- dashboard . addWidgets ( ...ruleWidgets ) ;
258- }
259-
260- // SNS Topic metrics
261- const topicWidgets = props ?. router . topics ?. map ( ( topic , index ) => {
262- return new GraphWidget ( {
263- title : `SNS Topic: ${ topic . topicName } ` ,
264- width : 8 ,
265- height : 6 ,
266- left : [
267- topic . metricNumberOfNotificationsFailed ( ) ,
268- topic . metricNumberOfNotificationsDelivered ( ) ,
269- topic . metricNumberOfMessagesPublished ( )
270- ]
271- } ) ;
272- } ) || [ ] ;
273-
274- if ( topicWidgets . length > 0 ) {
275- dashboard . addWidgets ( ...topicWidgets ) ;
276- }
277-
278- // SQS Queue metrics for consumers
279- const queueWidgets = props ?. consumers ?. map ( ( consumer ) => {
280- return new GraphWidget ( {
281- title : `SQS Queue: ${ consumer . queue . queueName } ` ,
282- width : 12 ,
283- height : 6 ,
284- left : [
285- consumer . queue . metricApproximateNumberOfMessagesVisible ( ) ,
286- consumer . queue . metricApproximateAgeOfOldestMessage ( ) ,
287- consumer . queue . metricNumberOfMessagesReceived ( ) ,
288- consumer . queue . metricNumberOfMessagesDeleted ( )
289- ]
290- } ) ;
291- } ) || [ ] ;
292-
293- if ( queueWidgets . length > 0 ) {
294- dashboard . addWidgets ( ...queueWidgets ) ;
295- }
296-
297- // Dead Letter Queue metrics
298- const dlqWidgets = props ?. deadLetterQueues ?. map ( ( queue ) => {
299- return new GraphWidget ( {
300- title : `Dead Letter Queue: ${ queue . queueName } ` ,
301- width : 12 ,
302- height : 6 ,
303- left : [
304- queue . metricApproximateNumberOfMessagesVisible ( ) ,
305- queue . metricApproximateAgeOfOldestMessage ( )
306- ]
307- } ) ;
308- } ) || [ ] ;
309-
310- if ( dlqWidgets . length > 0 ) {
311- dashboard . addWidgets ( ...dlqWidgets ) ;
312- }
313-
314- // Store the dashboard name for reference
315- this . dashboardName = dashboard . dashboardName ;
256+ ]
257+ } ) ;
258+ } ) || [ ] ;
259+
260+ if ( ruleWidgets . length > 0 ) {
261+ dashboard . addWidgets ( ...ruleWidgets ) ;
262+ }
263+
264+ // SNS Topic metrics
265+ const topicWidgets = props ?. router . topics ?. map ( ( topic , index ) => {
266+ return new GraphWidget ( {
267+ title : `SNS Topic: ${ topic . topicName } ` ,
268+ width : 8 ,
269+ height : 6 ,
270+ left : [
271+ topic . metricNumberOfNotificationsFailed ( ) ,
272+ topic . metricNumberOfNotificationsDelivered ( ) ,
273+ topic . metricNumberOfMessagesPublished ( )
274+ ]
275+ } ) ;
276+ } ) || [ ] ;
277+
278+ if ( topicWidgets . length > 0 ) {
279+ dashboard . addWidgets ( ...topicWidgets ) ;
280+ }
281+
282+ // SQS Queue metrics for consumers
283+ const queueWidgets = props ?. consumers ?. map ( ( consumer ) => {
284+ return new GraphWidget ( {
285+ title : `SQS Queue: ${ consumer . queue . queueName } ` ,
286+ width : 12 ,
287+ height : 6 ,
288+ left : [
289+ consumer . queue . metricApproximateNumberOfMessagesVisible ( ) ,
290+ consumer . queue . metricApproximateAgeOfOldestMessage ( ) ,
291+ consumer . queue . metricNumberOfMessagesReceived ( ) ,
292+ consumer . queue . metricNumberOfMessagesDeleted ( )
293+ ]
294+ } ) ;
295+ } ) || [ ] ;
296+
297+ if ( queueWidgets . length > 0 ) {
298+ dashboard . addWidgets ( ...queueWidgets ) ;
299+ }
300+
301+ // Dead Letter Queue metrics
302+ const dlqWidgets = props ?. deadLetterQueues ?. map ( ( queue ) => {
303+ return new GraphWidget ( {
304+ title : `Dead Letter Queue: ${ queue . queueName } ` ,
305+ width : 12 ,
306+ height : 6 ,
307+ left : [
308+ queue . metricApproximateNumberOfMessagesVisible ( ) ,
309+ queue . metricApproximateAgeOfOldestMessage ( )
310+ ]
311+ } ) ;
312+ } ) || [ ] ;
313+
314+ if ( dlqWidgets . length > 0 ) {
315+ dashboard . addWidgets ( ...dlqWidgets ) ;
316+ }
317+
318+ // Store the dashboard name for reference
319+ this . dashboardName = dashboard . dashboardName ;
320+
321+ // Create composite alarm with alert action
322+
323+ // Create SNS topic for alarms
324+ const alarmTopic = new Topic ( this , 'AlarmTopic' , {
325+ displayName : 'Event Monitoring Alarms'
326+ } ) ;
327+ // Add email subscription
328+ alarmTopic . addSubscription ( new EmailSubscription ( '[email protected] ' ) ) ; 329+ alarmTopic . grantPublish ( new ServicePrincipal ( 'events.amazonaws.com' ) ) ;
330+
331+ // Create composite alarm
332+ const compositeAlarm = new CompositeAlarm ( this , 'CompositeAlarm' , {
333+ alarmRule : AlarmRule . anyOf ( ...this . alarms ) ,
334+ alarmDescription : 'Composite alarm that triggers when any component alarm is in ALARM state' ,
335+ actionsEnabled : true
336+ } ) ;
337+
338+ // Add SNS action to composite alarm
339+ compositeAlarm . addAlarmAction ( new cdk . aws_cloudwatch_actions . SnsAction ( alarmTopic ) ) ;
316340 }
317341}
0 commit comments