Testing, monitoring, and debugging 

Now, you have a Lambda function deployed and ready to go. Let's test it to see if it works as expected! We're going to touch on the basic concepts of testing and debugging Lambdas and then delve more deeply into this in Chapter 6, Going Deeper with Lambda.

To test a function, we need to come up with an event object to invoke it with. If your function doesn't use anything from the event object, then this can simply be some arbitrary JSON data. If your function is being called by another service, then it's likely to be invoked with an event object of a known structure. 

For example, here's a simplified event object from an SQS queue event source:

{
"Records": [
{
"messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
"receiptHandle": "MessageReceiptHandle",
"body": "Hello from SQS!",
"attributes": { ... },
"messageAttributes": { ... },
"md5OfBody": "7b270e59b47ff90a553787216d55d91d",
"eventSource": "aws:sqs",
"eventSourceARN": "arn:aws:sqs:ap-southeast-2:123456789012:MyQueue",
"awsRegion": "ap-southeast-2"
}
]
}

The Lambda function is invoked with the details of one message from the queue. Presumably, the function would be performing some processing on Records[0].body. We can test our SQS message processing function by passing in our own data in this format. 

To do so through the console, go to the Lambda console, find your function, and configure a new test event:

Lambda console showing where to create test event objects

Paste your event object in and give it a name. Once you've created the test event, you can use the Test button to invoke the function.

Before we check the output of our invocation, let's learn how we achieve the same thing using the AWS CLI:

aws lambda invoke 
--invocation-type RequestResponse
--function-name myFunction
--payload file://test-sqs-message-event.json

We can either pass the JSON inline with the command or specify a file that contains the data. 

That's cool  we can invoke a function using our own inputs. What does the output look like? By default, each Lambda function comes with its own CloudWatch Logs log group. To write things to the logs, you can use the methods that are available on the console object or write out to stdout or stderrThe logs turn up fairly promptly in a log stream inside the log group associated with the function. A log stream is created for every new instance of the function. Predicting when a new instance of the function will happen can be fairly indeterministic, but we know that a new instance will be created if you update a function. We also know that the Lambda service can create new instances when scaling function for multiple concurrent invocations. 

To look at some example output, let's head over to the CloudWatch console and look at the latest invocation of myFunction:

The CloudWatch console showing recent log output

I've scribbled over this screenshot, but for good reason:

  1. Once you find the console for CloudWatch, head to the Logs page (1).
  2. Find the log group corresponding to your function (2) – Lambda will create this group for you on the first invocation.
  3. Click on the log stream with the most recent activity (3). Now, the console will show us all of the log output from the recent invocations.
  4. In the preceding screenshot, we can see a single invocation, denoted by the START, END, and REPORT syntax.
  5. We can tell which version of the function has been invoked as well (4).
  6. Before invoking the Node.js function, I added a line to test the log output:
console.log("Hello from myFunction!");
  1. This results in outputting a timestamp and our text (5).
  2. Finally, when the function finishes execution and terminates, we get a report of some of the metrics for that invocation (6).

Before we move on to explaining those metrics, let's see whether we can achieve the same thing using the CLI:

aws lambda invoke 
--invocation-type RequestResponse
--function-name myFunction
--log-type Tail
--query 'LogResult'
--output text
outfile
| base64 -d

Here, we've added some options to the basic invoke command so that we can see the output of the logs. The log type of Tail allows us to get an object with log data, which we can extract with the query and output format. The LogResult data is base64-encoded, so we pipe the output through the base64 program with a switch to decode (-d for Linux and Windows, -D for macOS).

The output is readable text in your command-line console that looks the same as what you would see in the CloudWatch log stream. Here's what the output looks like for me; it will be in a similar format for you but with different values and a different RequestID:

START RequestId: 87349dc3-49a2-4e5d-b02d-2fa4c317610e Version: $LATEST
END RequestId: 87349dc3-49a2-4e5d-b02d-2fa4c317610e
REPORT RequestId: 87349dc3-49a2-4e5d-b02d-2fa4c317610e Duration: 1.47 ms Billed Duration: 100 ms Memory Size: 128 MB Max Memory Used: 55 MB Init Duration: 106.83 ms

So, that was the basics of testing. These methods are useful for ad hoc or one-off tests, but ideally, we want to automate our testing in a pipeline. 

Next, as promised, let's analyze some metrics that Lambda reports through CloudWatch. There are seven different metrics that are available out of the box for Lambda, as follows:

  • Invocation count: The number of times a function is invoked.
  • Invocation duration: The amount of time that a function took to execute in milliseconds.
  • Invocation errors: The number of times that a function failed to execute, whether this is through handled exits from the code or unhandled errors such as reaching the configured timeout.
  • Throttled invocation count: The number of times that a request to invoke a function was throttled due to hitting concurrency limits.
  • Concurrency: This is a count of the number of functions that are concurrently executing in an AWS account.
  • Iterator age: This applies to stream-based invocations where iterators are used to move through batches of messages.
  • Dead letter error count: When there is an error writing to the Dead Letter Queue after failed invocations.

You can see these rolled up into a graph by using the CloudWatch metrics console. You can also create your own custom metrics using the AWS SDK. Maybe you want to track the number of new customer sign-ups or count the number of invalid data errors when processing a batch of messages. Let's have a look at these two examples using Node.js and Java SDKs. 

For a Node.js Lambda, follow these steps:

  1. First, you need to define the imports that the function will need to run:
const AWS = require('aws-sdk');
const cloudwatch = new AWS.CloudWatch();
  1. Then, create the data object that you want to publish as a metric:
const metric = {
MetricData: [{
MetricName: 'NewCustomerCount',
Dimensions: [{
Name: 'NewCustomers',
Value: successfulSignup
}],
Timestamp: new Date(),
Unit: 'Count',
Value: 1
}],
Namespace: 'Customers'
};
  1. Finally, use the PutMetricData action from the CloudWatch API to send the metric data to CloudWatch:
cloudwatch.putMetricData(metric, (e, data) => {
if (e) console.log(e, e.stack);
else console.log(data);
});

For Java, there are a bunch of imports to make:

import com.amazonaws.services.cloudwatch.AmazonCloudWatch;
import com.amazonaws.services.cloudwatch.AmazonCloudWatchClientBuilder;
import com.amazonaws.services.cloudwatch.model.Dimension;
import com.amazonaws.services.cloudwatch.model.MetricDatum;
import com.amazonaws.services.cloudwatch.model.PutMetricDataRequest;
import com.amazonaws.services.cloudwatch.model.PutMetricDataResult;
import com.amazonaws.services.cloudwatch.model.StandardUnit;

The code looks as follows:

final AmazonCloudWatch cloudwatch = AmazonCloudWatchClientBuilder.defaultClient();
Double validation_errors = Double.parseDouble(args[0]);

Dimension dimension =
new Dimension()
.withName(
"DataValidationErrors")
.withValue(
"Errors").build();
MetricDatum datum =
new MetricDatum()
.withMetricName(
"DataValidationErrorCount")
.withUnit(StandardUnit.Count)
.withValue(validation_errors)
.withDimensions(dimension).build();
PutMetricDataRequest request =
new PutMetricDataRequest()
.withNamespace(
"Customers/DataErrors")
.withMetricData(datum);
PutMetricDataResult response = cloudwatch.putMetricData(request);

Often, an easier way to achieve metric collection is to simply log the value or occurrence out to stdout or stderr. Then, you can set up a metric filter in CloudWatch Logs to search for a preconfigured pattern. The filter will search incoming logs in your log group and increment a metric when a match is found. This is also a more efficient way of collecting data because you aren't using compute time within the function to make the API request.

What happens when a Lambda function fails to complete execution for some reason? In Chapter 6, Going Deeper with Lambda, we'll talk about what it means when function execution results in an error, and how we can rectify this. In the next section, we'll learn how to write our first Lambda function.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.223.170.223