0

I have many functions in AWS Lambda that are invoked on various periodic schedules by CloudWatch Rules. If their Node JavaScript encounters an error, it’s logged to CloudWatch Log Groups.

  • Per this doc, I can create a CloudWatch Simple Notification Service (SNS) to email me.
  • Per this doc, I can create a CloudWatch Metric to count occurrences of the word ERROR in a single Log Group.
  • Per this doc, I can create a CloudWatch Alarm that uses the SNS to email me when a single Metric is observed.

Can either or both of these two complaints be solved by these tools, or should other tools be used?

  • The received email is generic. Can it include the error message?
  • This works for only one Lambda function’s one Log Group. Can it be configured to work for multiple Lambda functions’ Log Groups? Can my multiple Lambda functions be configured to log to a single, shared Log Group? Or would I need to create a pair of Metrics and Alarms for each Lambda function?
2540625
  • 11,022
  • 8
  • 52
  • 58

3 Answers3

2

TL;DR this is the solution architecture you're probably looking for.

Photo: Amazon blog lambda error parsing architecture

(source: Amazon)

Can my multiple Lambda functions be configured to log to a single, shared Log Group?

No. Even if you could have all your lambda log to a single log group, however, this would be highly inadvisable.

You could, however, stream multiple log groups into a single destination. This is commonly done for centralized logging solutions, for example.

would I need to create a pair of Metrics and Alarms for each Lambda function?

This is probably the most straightforward approach. Using CloudFormation (or related tools, AWS CDK, Terraform, etc) you can automate this setup with the creation of your lambda function.

The received email is generic. Can it include the error message?

Not easily. The CloudWatch Alarm event source won't include any reference to the logs that caused the count metric to go into alarm, so this would not work. Usually, when you receive such an alert, you would use other services to find the errors (e.g. xray, cloudwatch logs, etc). For example, your message may simply include a link to the CloudWatch metric/alarm. From CloudWatch, there should be a 'view logs' button that provides the logs for the metric timeframe. See: Pivot from metrics to logs

If you really need the error to be contained in the notification, a different solution architecture would be needed for this. You could, for example, stream your CloudWatch events to a Lambda function that will process the event and send the notification including the error message from the stream.

See: Real-time processing of log data with subscriptions and How to get notified on specific Lambda function error patterns using CloudWatch which has a full guide to getting your error message in your notifications.

Alternatively, you may want to consider other monitoring solutions, such as sentry, rollbar, NewRelic, DataDog, etc.

sytech
  • 29,298
  • 3
  • 45
  • 86
  • Thank you. That’s very similar to the route I took, which I’ve documented as a separate answer to my question. I appreciate your detail and will upvote. Cheers. – 2540625 Sep 01 '21 at 01:43
1

Partially informed by this answer to a similar question, and by trial/error and who knows what other googling, I ended up pivoting away from AWS Metrics and AWS Alarms, and instead utilized a new Lambda function that is triggered by any number of my select Log groups. I kept the SNS functionality, which is very easy to set up if you own your emailing domain.

Upon creation of a new Lambda function, the option to search for blueprints is presented, and searching log yields (among other options) the cloudwatch-logs-process-data blueprint. It can also be created from scratch, as its code is merely this:

const zlib = require('zlib');

exports.handler = async (event, context) => {
    const payload = Buffer.from(event.awslogs.data, 'base64');
    const parsed = JSON.parse(zlib.gunzipSync(payload).toString('utf8'));
    console.log('Decoded payload:', JSON.stringify(parsed));
    return `Successfully processed ${parsed.logEvents.length} log events.`;
};

I then chose for it as multiple Triggers the Log groups I wish to monitor, setting their Filter pattern to the word ERROR, and setting their Filter name to my desired email subject.

Using the console log from the blueprint, I created a test event of this format:

{
  "awslogs": {
    "data": "LONG_STRING_OF_ENCODED_CHARACTERS_FROM_ABOVE_CONSOLE_LOG_HERE"
  }
}

And I modified the blueprint code more to my liking as this:

const zlib = require('zlib');
const aws = require('aws-sdk');
const ses = new aws.SES({ region: '<some-region-1>' });

exports.handler = async function (event) {
    const payload = Buffer.from(event.awslogs.data, 'base64');
    const parsed = JSON.parse(zlib.gunzipSync(payload).toString('utf8'));

    const bodyText = JSON.stringify(parsed, null, 2).replace(/\\n/g, ' \\\n         ').replace(/\\t/g, '\\\n         ');
    console.log('bodyText:', bodyText);

    var params = {
        Source: '<some_email@example.com>',
        Destination: {
            ToAddresses: ['<some_email@example.com>'],
        },
        Message: {
            Subject: { Data: parsed.subscriptionFilters[0] },
            Body: {
                Text: { Data: bodyText },
            },
        },
    };
    return ses.sendEmail(params).promise();
};

And it works, I get an immediate email if a Lambda function logs an error (with no noticeable increase to my monthly AWS bill as well).

2540625
  • 11,022
  • 8
  • 52
  • 58
0

The updated lambda sending emails through ses will work well. But, Lambda role has to be updated with necessary permisssions. See article: https://aws.amazon.com/premiumsupport/knowledge-center/lambda-send-email-ses/

Policies i have added to default lambda role to make it working:

{
"Version": "2012-10-17",
"Statement": [
    {
        "Sid": "VisualEditor0",
        "Effect": "Allow",
        "Action": [
            "ses:SendEmail",
            "ses:SendRawEmail",
            "logs:CreateLogGroup"
        ],
        "Resource": [
            "arn:aws:logs:your-region-1:xxxaccountidxxxx:*",
            "arn:aws:ses:*:xxxaccountidxxxx:configuration-set/*",
            "arn:aws:ses:your-region-1:xxxaccountidxxxx:identity/your@email.com"
        ]
    },
    {
        "Sid": "VisualEditor1",
        "Effect": "Allow",
        "Action": [
            "ses:SendEmail",
            "logs:CreateLogStream",
            "logs:PutLogEvents"
        ],
        "Resource": [
            "arn:aws:ses:your-region-1:xxxaccountidxxxx:identity/your@email.com",
            "arn:aws:logs:your-region-1:xxxaccountidxxxx:log-group:/aws/lambda/yourlambdaname:*"
        ]
    }
]

}

Otherwise error is reported:

code": "AccessDenied",
    "message": "User `arn:aws:sts::xxxx:assumed-role/zzz/yyyy' is not authorized to perform `ses:SendEmail' on resource `arn:aws:ses:us-east-1:xxxxx:identity/<your@email.com>'"