Skip to content

Commit

Permalink
Adding new Serverless pattern demonstrating notification system for L…
Browse files Browse the repository at this point in the history
…ambda using CloudWatch log subscription filter and sns
  • Loading branch information
shubsmor committed Nov 29, 2024
1 parent 05045de commit c7258fe
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
67 changes: 67 additions & 0 deletions lambda-cloudwatch-subscription-sns-notification/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Amazon CloudWatch Logs Subscription to AWS Lambda along with SNS notification using SAM

This pattern demonstrates an alerting system using CloudWatch Logs Subscription filter to a Lambda function to trigger a SNS notification when the original function encounters timeout. Currently we dont have out of box offering to alert the customer when there Lambda function encounters timeout. For time sensitive applciations Customer would like to get notification when their Lambda function is timedout.

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [AWS Serverless Application Model](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html) (AWS SAM) installed

## Deployment Instructions

1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository:
```
git clone https://github.com/aws-samples/serverless-patterns
```
1. Change directory to the pattern directory:
```
cd lambda-cloudwatch-subscription-sns-notification
```
1. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yml file:
```
sam build
sam deploy --guided --parameter-overrides 'EmailAddress="[email protected]"'
```
1. During the prompts:
* Enter a stack name
* Enter the desired AWS Region
* Allow SAM CLI to create IAM roles with the required permissions.
Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults.
1. Note the outputs from the SAM deployment process. These contain the resource names and/or ARNs which are used for testing.
## How it works
This pattern deploys :
1. One SNS topic with email subsciption
2. CloudWatch loggroup and subscription filter
3. Two Lambda functions
- Sample Lambda [ to simulate timeout ]
- Notification Lambda.
When Sample Lambda function timeout, CloudWatch subscription filter configured for the sample lambda Log group, triggered Notification Lambda. Notification Lambda parse the event and Publish a SNS alert to the email address provided while deploying the solution
*This parrten cretes a new SNS topic with email subscription. Please confirm email verification by clicking on "Confirm subscription" link sent via Amazon SNS.*
## Testing
Invoke Sample Lambda using CLI/Console to demonstrate the template. Sample lambda is hardcoded to timeout. Thus, triggering the CloudWatch log subscription filter with pattern matching. The notification Lambda publish a sns email notification with timeout function, loggroupa and logstream details
## Cleanup
1. Delete the stack
```bash
aws cloudformation delete-stack --stack-name STACK_NAME
```
1. Confirm the stack has been deleted
```bash
aws cloudformation list-stacks --query "StackSummaries[?contains(StackName,'STACK_NAME')].StackStatus"
```
----
Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: MIT-0
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{
"title": "Amazon CloudWatch Log Subscription filter to Lambda and SNS notification",
"description": "Creates alerting system for a time sensitive Lambda where a Notification is triggered to SNS topic when the original Lambda encounters timeout",
"language": "Python",
"level": "200",
"framework": "SAM",
"introBox": {
"headline": "How it works",
"text": [
"This sample project demonstrates an alerting system using CloudWatch Logs Subscription filter to a Lambda function to trigger a SNS notification when the original function encounters timeout",
"Currently we dont have out of box offering to alert the customer when there Lambda function encounters timeout. For time sensitive applciations Customer would like to get notification when their Lambda function is timedout",
"This pattern deploys one SNS topic with email subsciption, CloudWatch loggroup and subscription filter, two Lambda functions - sample Lambda [ to demonstrate timeout ] , notification Lambda [Is triggered by CloudWatch log Subscription and publish a SNS message]"
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/lambda-cloudwatch-subscription-sns-notification",
"templateURL": "serverless-patterns/lambda-cloudwatch-subscription-sns-notification",
"projectFolder": "lambda-cloudwatch-subscription-sns-notification",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": [
{
"text": "CloudWatch Subscription Fileter",
"link": "https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/Subscriptions.html"
},
{
"text": "Lambda function is timing out?",
"link": "https://repost.aws/knowledge-center/lambda-verify-invocation-timeouts"
}
]
},
"deploy": {
"text": [
"sam build",
"sam deploy --guided --parameter-overrides 'EmailAddress="[email protected]"'"
]
},
"testing": {
"text": [
"Invoke the Sample Lambda to demonstrate the template. Sample lambda is hardcoded to timeout. Thus, triggering the CloudWatch log subscription filter with pattern matching. The notification Lambda publish a sns email notification with timeout function, loggroup and logstream details",
"This pattern also creates a new SNS topic with email subscription for provided email address. Please confirm the subscription by clicking on the confirmation request mail sent by Amazon SNS to allow this pattern to send a SNS notification successfully"
]
},
"cleanup": {
"text": [
"Delete the stack: <code>cdk delete</code>."
]
},
"authors": [
{
"name": "Shubham",
"image": "https://avatars.githubusercontent.com/u/150242047?s=400&u=aaa2db07529d3e1e82ec59daacb05b6abc7c3a5f&v=4",
"bio": "Cloud Support Engineer - SVLS",
"linkedin": "shubham-more-1b6aa185"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import json
import boto3
import gzip
import base64
from io import BytesIO
import os
sns = boto3.client('sns')

def lambda_handler(event, context):
topicArn = os.environ['SNS_TOPIC_ARN']

compressed_log_data = event['awslogs']['data']
uncompressed_log_data = gzip.decompress(base64.b64decode(compressed_log_data))
print("Printing uncompressed_log_data :", uncompressed_log_data)

log_events = json.loads(uncompressed_log_data)['logEvents']
log_group = json.loads(uncompressed_log_data)['logGroup']
log_stream = json.loads(uncompressed_log_data)['logStream']
function_name = log_group.split('/')[-1]

notification_message = f"***Task Timeout Error Has Encountered for Lambda: {function_name}***. \n Logdetails : \n Log events received from log group: {log_group}, log stream: {log_stream}, function: {function_name}"

response = sns.publish(
TopicArn=topicArn,
Message=notification_message,
Subject='Task Timeout Notification'
)

print(f"Notification published - messageID:", response['MessageId'])

return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world"
}),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import json
# import requests
import time

def lambda_handler(event, context):
print("sleeping now - to produce timeout")
time.sleep(4)
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world"
}),
}
88 changes: 88 additions & 0 deletions lambda-cloudwatch-subscription-sns-notification/template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM template to demonstrate alerting with CloudWatch Subscription Filter

Parameters:
EmailAddress:
Type: String
Description: Email address to receive notifications

Globals:
Function:
Timeout: 3
MemorySize: 128

Resources:
# SNS Topic for notifications
NotificationTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: TaskTimedOutNotification
# SNS Topic Subscription for Email Notifications

EmailSubscription:
Type: AWS::SNS::Subscription
Properties:
Endpoint: !Ref EmailAddress
Protocol: email
TopicArn: !Ref NotificationTopic

# Sample Lambda function
SampleLambda:
Type: AWS::Serverless::Function
Properties:
Handler: index.lambda_handler
Runtime: python3.13
CodeUri: sample-lambda/

# creating loggroup first
SampleLambdaLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join [ "", [ "/aws/lambda/", !Ref SampleLambda] ]
RetentionInDays: 30

# Notification Lambda function
NotificationLambda:
Type: AWS::Serverless::Function
Properties:
Handler: index.lambda_handler
Runtime: python3.13
CodeUri: notification-lambda/
Environment:
Variables:
SNS_TOPIC_ARN: !Ref NotificationTopic
Policies:
- SNSPublishMessagePolicy:
TopicName: !GetAtt NotificationTopic.TopicName

#Gives permissions to the Log Group to invoke the second Lambda Function
CloudWatchSubscriptionFunctionPermission:
Type: AWS::Lambda::Permission
Properties:
FunctionName: !GetAtt NotificationLambda.Arn
Action: 'lambda:InvokeFunction'
Principal: 'logs.amazonaws.com'
SourceArn: !GetAtt SampleLambdaLogGroup.Arn

# CloudWatch Subscription Filter for "Task timed out" logs
TaskTimedOutSubscriptionFilter:
Type: AWS::Logs::SubscriptionFilter
Properties:
DestinationArn: !GetAtt NotificationLambda.Arn
FilterName: Lambda
FilterPattern: "\"Status: timeout\""
LogGroupName: !Ref SampleLambdaLogGroup
DependsOn: CloudWatchSubscriptionFunctionPermission


Outputs:
SampleLambdaArn:
Description: ARN of the Sample Lambda function
Value: !GetAtt SampleLambda.Arn
NotificationLambdaArn:
Description: ARN of the Notification Lambda function
Value: !GetAtt NotificationLambda.Arn
NotificationTopicArn:
Description: ARN of the SNS Topic for notifications
Value: !Ref NotificationTopic

0 comments on commit c7258fe

Please sign in to comment.