diff --git a/lambda-cloudwatch-subscription-sns-notification/README.md b/lambda-cloudwatch-subscription-sns-notification/README.md new file mode 100644 index 000000000..64065615d --- /dev/null +++ b/lambda-cloudwatch-subscription-sns-notification/README.md @@ -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="your-Exmaple-email@mail.com"' + ``` +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 diff --git a/lambda-cloudwatch-subscription-sns-notification/example-pattern.json b/lambda-cloudwatch-subscription-sns-notification/example-pattern.json new file mode 100644 index 000000000..480426671 --- /dev/null +++ b/lambda-cloudwatch-subscription-sns-notification/example-pattern.json @@ -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="your-Exmaple-email@mail.com"'" + ] + }, + "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: cdk delete." + ] + }, + "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" + } + ] +} diff --git a/lambda-cloudwatch-subscription-sns-notification/notification-lambda/index.py b/lambda-cloudwatch-subscription-sns-notification/notification-lambda/index.py new file mode 100644 index 000000000..647dbb6d5 --- /dev/null +++ b/lambda-cloudwatch-subscription-sns-notification/notification-lambda/index.py @@ -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" + }), + } \ No newline at end of file diff --git a/lambda-cloudwatch-subscription-sns-notification/sample-lambda/index.py b/lambda-cloudwatch-subscription-sns-notification/sample-lambda/index.py new file mode 100644 index 000000000..91f1acc6b --- /dev/null +++ b/lambda-cloudwatch-subscription-sns-notification/sample-lambda/index.py @@ -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" + }), + } diff --git a/lambda-cloudwatch-subscription-sns-notification/template.yaml b/lambda-cloudwatch-subscription-sns-notification/template.yaml new file mode 100644 index 000000000..ddde12df9 --- /dev/null +++ b/lambda-cloudwatch-subscription-sns-notification/template.yaml @@ -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 \ No newline at end of file