5
Houston, we have a problem!

This chapter covers

  • Reading console logs using CloudWatch
  • The challenges of debugging serverless applications
  • Debugging serverless APIs

By our nature, we—humans—aren’t perfect. No matter what we do, there is always the possibility of making a mistake, even if we do our best not to make one. This is especially true when developing or interacting with software. Do you remember the last time when a mobile application you were using crashed or a website stopped responding? Chances are you have experienced this recently, and you had to refresh your browser or restart your app.

We all make mistakes, and applications crash on a daily basis. Though usually harmless, application bugs can sometimes result in huge losses. Let’s take the example of a bug occurring in your pizzeria application that prevents you from creating orders. How would you find the bug in the first place? How does debugging work in serverless applications?

This chapter helps you learn how to find errors in your serverless applications, how to debug them, and what debugging tools you have at your disposal.

5.1 Debugging a serverless app

Because you’re progressing quickly, Aunt Maria sends you a message saying that she’s hired a mobile developer, Pierre. She wanted to increase the reach to her customers, and a mobile application for making pizza orders seemed like a good start. Pierre wanted to try out your serverless application. Unfortunately, as he tried creating a pizza order, the application returned an invalid response. Pierre complained to your aunt, and now you have your aunt on the phone. You’re probably scratching your head thinking, “Where could I have gone wrong?” and “How can I debug this?”

In a traditional Node.js server application, you could just type in a console.log ("some text") command somewhere between the lines to log some text or objects to the console, or even type in debugger to activate a breakpoint in your code to debug your application. Afterwards you could start it locally and try it out, or log in to the server and tail your application log to debug it.

Logging in a serverless application is quite different compared to a traditional one. Although your serverless application consists of completely separated modules—an API Gateway and a Lambda function—you cannot run it locally and properly debug the whole application flow. Additionally, because your application is serverless, there isn’t a server you can log into to tail its logs. Yes, this probably does sound weird and frustrating, but don’t worry.

Each serverless provider has a tool to help you monitor and debug your serverless functions. For AWS, it’s CloudWatch.

CloudWatch is the AWS service designed for tracking, logging, and monitoring your AWS resources. Think of it as a serverless version of your old server tail log, though capable of much more. As with other AWS services, CloudWatch is available in AWS CLI, and you’ll use it from your terminal.

Because you are using AWS, CloudWatch is your default choice.

Here are several ways you can use AWS CloudWatch:

  • Via the AWS web console from your browser
  • Using the AWS CLI from your terminal
  • With the AWS API
  • With the AWS SDK (depending on your programming language)

You can use any of these you like, but in this book you will be working mostly with the AWS CLI because it is developer-friendly and you can invoke it from your local terminal.

CloudWatch is a simple service that captures logs and errors from your serverless functions. Whenever you log something in your function—for example, with a console.log in Node.js—those logs are automatically sent to AWS CloudWatch. AWS CloudWatch is responsible for their storage and grouping. You can access those logs via the AWS CLI in your terminal or using the AWS web console UI. For a visual understanding of this handling process, see figure 5.1.

figure-5.1.eps

Figure 5.1 AWS Lambda sends console.log output directly to CloudWatch.

5.2 Debugging your Lambda function

Now that you know what CloudWatch is, you’ll use it to find the source of Pierre’s problems. Pierre let you know that the error occurs when he tries to create a pizza order with a pizza type and a delivery address. You need to try to reproduce the issue while monitoring the logs using CloudWatch. You will put a log statement at the beginning of your create-order.js handler, redeploy your API, and ask Pierre to try again.

You should log the request with some prefix text—for example, “Save an order”—on the first line of your createOrder function, as shown in the following listing. Adding prefix text to your request logs will help you search the logs, but it is not required. (The snippet in this listing shows just the beginning of the file; the rest of the file is unchanged.)

Listing 5.1 Updated create-order.js handler

'use strict'
const AWS = require('aws-sdk')
const docClient = new AWS.DynamoDB.DocumentClient()
const rp = require('minimal-request-promise')
module.exports = function createOrder(request) {
  console.log('Save an order', request)    ①  

  if (!request || !request.pizza || !request.address)
    throw new Error('To order pizza please provide pizza type and address where pizza should be delivered')

  // ...    ②  

Pierre tries again, and receives the same error. Now you should search the logs for the “Save an order” text. Looping through CloudWatch logs for your Lambda function can be difficult, because there can be many entries with a bunch of metadata. Fortunately, you can do it faster with the AWS CLI and the logs filter-log-events command for filtering logs.

Because CloudWatch stores the logs in log groups, before running the logs filter-log-events command you’ll need to find the name of your log group. To do this, you’ll use the logs service of the AWS CLI once again—more precisely, the describe-log-groups command, as shown in the next listing.

Listing 5.2 describe-logs-groups

aws logs describe-log-groups --region eu-central-1

This command will return a response that includes logGroupName, similar to the next one:

{
    "logGroups": [
        {
            "arn": "arn:aws:logs:eu-central-1:123456789101:log-group:/aws/lambda/pizza-api:*",
            "creationTime": 1524828117184,
            "metricFilterCount": 0,
            "logGroupName": "/aws/lambda/pizza-api",
            "storedBytes": 1024
        }
    ]
}

Run the logs filter-log-events command from your terminal and provide the “Save an order” text as a filter, as shown in listing 5.3. You’ll also need to specify an output format, which will be JSON again. See the next listing for the full code example.

Listing 5.3 Filtering CloudWatch logs with a selected log group and by the “Save an order” text

aws logs     ①  
  filter-log-events     ②  
  --filter='Save an order'     ③  
  --log-group-name=/aws/lambda/pizza-api     ④  
  --region=eu-central-1 
  --output=json    ⑤  

Running the command in listing 5.3 finally allows you to read console.log from your Lambda function. But, as you can see in listing 5.4, it returns it as a JSON with a lot of metadata that isn’t relevant for your use case. The only info you care about is “message” from each of the events. Everything else from the response is metadata about the log streams you searched for and some additional info about the log messages.

Listing 5.4 Your Pizza API logs from CloudWatch with metadata

{
    "searchedLogStreams": [    ①  
        {
            "searchedCompletely": true,
            "logStreamName": "2017/06/18/[$LATEST]353ce211793946dba5bb276b0bde3e0e"
        }
    ],
    "events": [    ②  
        {
            "ingestionTime": 1497802509940,
            "timestamp": 1497802509920,
            "message": "2017-06-18T16:15:09.860Z	4cc844ea-5441-11e7-8919-29f1e77e006c	Save an order 
            { pizza: 1,
    adress: '420 Paper St.' }
", 
            "eventId": "33402112131445556039184566359053029477419337484906135552",
            "logStreamName": "2017/06/18/[$LATEST]e24e0cab3d6f47f2b03005ba4ca16b8b"
        }
    ]
}

Also, the JSON output is not very readable when it’s formatted as single-line text. You can improve readability by changing the output type to text and updating the filter to return just the message, as shown in the next listing.

Listing 5.5 Updated CloudWatch logs filtered by the selected log group and the “Save an order” text

aws logs 
  filter-log-events 
  --filter='Save an order' 
  --log-group-name=/aws/lambda/pizza-api 
  --query='events[0].message'     ①  
  --region=eu-central-1 
  --output=text    ②  

Running this command from the terminal returns a much cleaner response, similar to the one in the following listing.

Listing 5.6 The logged Pizza API message, without any metadata

2017-06-18T16:15:09.860Z    4cc844ea-5441-11e7-8919-29f1e77e006c    
  Save an order { pizza: 1, adress: '420 Paper St.' }    ①  

This output looks a lot cleaner and more useful. And would you look at that, Pierre made a typo! He was sending adress instead of address by mistake. Such a fuss about a simple spelling mistake, right? Well, at least you won’t be the one on the phone explaining the issue to your aunt.

5.3 X-Ray your app

Debugging serverless applications is sometimes hard because it’s not easy to visualize the data flow—but AWS has a tool to help you out. AWS X-Ray is a service that shows the data flow of your application and all its involved services in near real time. You can use X-Ray with applications running on EC2, ECS, Lambda, and Elastic Beanstalk. In addition, the X-Ray SDK automatically captures metadata for all API calls made to AWS services using the AWS SDK. Figures 5.2 and 5.3 show the visual representation of your Pizza API created with AWS X-Ray.

To enable AWS X-Ray for your Lambda function, you need to add a policy that allows X-Ray to interact with it, and you need to set the tracing mode to Active in your function configuration.

figure-5.2.tif

Figure 5.2 A visual representation of the “Create an order” flow of the Pizza API

figure-5.3.tif

Figure 5.3 A detailed view of the “Create an order” flow of the Pizza API

Let’s see how to add a policy and to set the tracing mode to Active using the AWS CLI from your terminal. To attach the policy, you’ll use the iam attach-role-policy command again, but now with arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess, as shown in listing 5.7.

Listing 5.7 Attaching the X-Ray managed read-only policy to your Lambda role

aws iam     ①  
  attach-role-policy     ②  
  --policy-arn arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess     ③  
  --role-name pizza-api-executor     ④  
  --region eu-central-1 
  --output json

As you already know, that command returns an empty result when it’s executed successfully.

The next step is to update the function configuration. You can do that with the lambda update-function-configuration AWS CLI command. This command expects a function name and options; in this case, you want to update tracing-config by setting its mode to Active. The following listing shows the full command.

Listing 5.8 Enabling active tracking using AWS X-Ray

aws lambda     ①  
  update-function-configuration     ②  
  --function-name pizza-api     ③  
  --tracing-config Mode=Active     ④  
  --region eu-central-1

This command returns the Lambda function configuration as JSON output, as shown in the next listing. At this point, X-Ray displays your Lambda function flow, but by default you won’t be able to see other AWS services your function is using, such as DynamoDB.

Listing 5.9 Response after activating the X-Ray tracing

{
    "TracingConfig": {    ①  
        "Mode": "Active"
    },
    "CodeSha256": "HwV+/VdUztZ782NBEqY9Dvzj3nxF6tigLOZPt8yyCoU=",
    "FunctionName": "pizza-api",    ②  
    // ...    ②  
}

To be able to see other AWS services supported by X-Ray, you’ll need to wrap the AWS SDK for Node.js in the aws-xray-sdk-core module. After installing the aws-xray-sdk-core module from NPM, update your create-order.js handler as shown in the following listing.

Listing 5.10 Update the create-order.js handler to wrap the AWS SDK in an X-Ray function.

'use strict'
const AWSXRay = require('aws-xray-sdk-core')    ①  
const AWS = AWSXRay.captureAWS(require('aws-sdk'))    ②  
const docClient = new AWS.DynamoDB.DocumentClient()

module.exports = function updateDeliveryStatus(request) {
  console.log('Save an order', request)

  if (!request.deliveryId || !request.status)
    throw new Error('Status and delivery ID are required')

// ...    ③  

After you run the claudia update command to redeploy your API, X-Ray will be fully set up.

To see the visual representation of your function, go to the X-Ray section of the AWS web console. For this case, the URL is https://eu-central-1.console.aws.amazon.com/xray/home?region=eu-central-1#/service-map. Your URL may be different if you used a different region to deploy your function.

5.4 Taste it!

The exercise for this chapter is quite easy, but the next chapter brings in some more serious topics.

5.4.1 Exercise

Now that you’ve learned how to debug your serverless applications, let’s revisit the code listings from chapters 3 and 4 and try to read their logs.

Your exercise for this chapter is to try to read the CloudWatch logs for all success and error messages from the create-order.js handler.

Because this is just a debug exercise, there will be no tips this time. In case you need help, feel free to take a peek at the solution in the next section.

5.4.2 Solution

In chapter 3, you updated the create-order.js handler to log success messages and errors. To read those logs with CloudWatch, use the aws logs filter-log-events command. As you learned in this chapter, this command requires a filter. As a reminder, success messages were logged with an “Order is saved!” prefix. For errors, you used the “Oops, order is not saved :(” prefix. Use both of these prefixes to help you to filter the logs.

The command to filter with the “Order is saved!” prefix is shown in the following listing.

Listing 5.11 Command to filter the logs containing the text “Order is saved!” from CloudWatch

aws logs 
  filter-log-events 
  --filter='Order is saved!'     ①  
  --log-group-name=/aws/lambda/pizza-api 
  --query='events[0].message' 
  --output=text

The responses for both commands will differ depending on the number of successful and failed orders in your system. If there aren’t any errors, the output will display None.

Summary

  • You need to use CloudWatch to read logs from Lambda functions.
  • Instead of manually filtering the logs, you can use various commands from the AWS CLI.
  • To visualize your function flow, you can use the AWS X-Ray service.
..................Content has been hidden....................

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