• Stars
    star
    291
  • Rank 142,563 (Top 3 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created about 7 years ago
  • Updated almost 3 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Run Node in Lambda. Debug from your machine.

AWS Lambda Debugger

image

Do you want to step through code running live in Lambda? Do you want to fix bugs faster? Do you want free pizza?

This project will help you with the first 2 questions. When you show it to a friend, you might get that 3rd one too :)

This is only for the AWS Node runtime. Node 4.3 is not supported.

Isn't this impossible?

No. Well, not anymore.

How?

Normally, debugging is a one hop process: developer's debugger connect directly to the process. This is impossible with Lambda.

However, we fork your code to a separate child process that is running in debug mode and connected to the original via an interprocess communication channel. The parent process opens 2 WebSockets as well: one to the child process' V8 Inspector and the other to a broker server, becoming a proxy between the 2 connections. Next, the developer connects a debugger to the broker server, which connects them to the proxy, which is connected to the child's debugger port. Now, you have a 3 hop chain like this:

Debugger <=> Broker <=> Proxy <=> Child

Once the developer's debugger connects to the broker server, the debug version of the handler is executed. The proxy and the child coordinate to shim the event, context, and callback. Result: the developer is connected to a live running Lambda function with full control.

Oh, and you only have to add one line of code at the end of your handler file(s)...

I want one!

Good. There are 5 steps:

  1. Deploy the broker server
  2. Add the proxy to your code
  3. Configure the proxy via environment variables
  4. Increase your Lambda timeout
  5. Use it!

Deploy the broker server

You should only need one of these for you or your team to start. The broker has been designed for multiple simultaneous sessions. We found a t2.small to be more than enough for starting out.

  • Kick off an EC2 Amazon Linux Instance
  • Attach Security Group
    • exposing ports 8080 and 9229 to 0.0.0.0/0
    • expose port 22 to YOUR IP
  • SSH in to the box
# Install Docker
sudo yum update -y
sudo yum install -y docker
sudo service docker start

# Run the Broker
docker run --name debug-broker \
    -d -p 8080:8080 -p 9229:9229 \
    --restart always \
    trek10/aws-lambda-debugger

# To view logs
docker logs -f debug-broker

Advanced networking configuration info

Here's extra details about the port configurations:

  • Lambda connects to 8080. If your Lambdas are in the same VPC, you can configure the security group to just allow them in this side.
  • The default port for the V8 Inspector is 9229. You can restrict access to this port based on where your developers are coming from.

Add the proxy to your code

Add the package to your repo:

npm install aws-lambda-debugger --save

Require the package at the very end of each file that contains a Lambda handler that you want to debug. Example:

module.exports.myHandler = (event, context, callback) => {
  // put some code that you want to debug here
}

require('aws-lambda-debugger');

That's it!!!

How the magic works: By putting the require call at the end of the module, it is the last thing to be evaluated before the module is loaded by Lambda. For whatever reason, Node provides access to the parent module and does not or can not stop us from mutating that parent module. This allows us to reach up into the parent and swap the handler before it is executed. The proxy code takes over and launches your handler in the child process instead.

Configure the proxy via environment variables

There are 3 magic environment variables that need to be set:

  • DEBUGGER_ACTIVE: As long as this value is present and it is not 'false' or empty string, the proxy will do its job.
  • DEBUGGER_BROKER_ADDRESS: This is the IP address or domain for the broker server.
  • DEBUGGER_FUNCTION_ID: This is a unique "ID" of your own choosing (per function!) that is used by the broker to pair the debugger connection (this function ID is also part of the URL that the debugger connects to - see below) to the appropriate Lambda function. It does not need to be a UUID/GUID/etc. It just needs to be unique relative to any other functions that you or your teammates are using on the same broker instance.

Increase your Lambda timeout

Alter the timeout to 300 seconds to allow maximum debug time.

Use it!

  1. Launch your Lambda function (from console, via CLI, etc)

  2. Replace the DEBUGGER_BROKER_ADDRESS and DEBUGGER_FUNCTION_ID in the following URL and paste it into Chrome.

    chrome-devtools://devtools/remote/serve_file/@60cd6e859b9f557d2312f5bf532f6aec5f284980/inspector.html?experiments=true&v8only=true&ws=[DEBUGGER_BROKER_ADDRESS]:9229/[DEBUGGER_FUNCTION_ID]
    
  3. DEBUG!!!

Bonus! Use this bookmarklet to pull the URL from the Code tab for the function in the AWS console:

javascript:(function()%7Bconst obj %3D %7B%7D%3B document.querySelectorAll('.env-var-list .key-value').forEach(elem %3D> %7B if (elem.querySelectorAll('input%5Bplaceholder%3D"Key"%5D').item(0).value) obj%5Belem.querySelectorAll('input%5Bplaceholder%3D"Key"%5D').item(0).value%5D %3D elem.querySelectorAll('input%5Bplaceholder%3D"Value"%5D').item(0).value %7D)%3B const win %3D window.open(''%2C '_blank')%3B win.document.write(%60Debugger URL%3A chrome-devtools%3A%2F%2Fdevtools%2Fremote%2Fserve_file%2F%4060cd6e859b9f557d2312f5bf532f6aec5f284980%2Finspector.html%3Fexperiments%3Dtrue%26v8only%3Dtrue%26ws%3D%24%7Bobj.DEBUGGER_BROKER_ADDRESS%7D%3A9229%2F%24%7Bobj.DEBUGGER_FUNCTION_ID%7D%60)%7D)()

What's the catch?

There are a few catches/known issues:

  • Multiple console.log calls close together sometimes causes them to be aggregated in a single CloudWatch Log entry
  • This only works with the AWS Node 6.10 runtime. We expect it to work with Node 8 whenever AWS offers support for it too.
  • Chrome DevTools is the only debugger that we have proven to work. YMMV with your IDE.
  • You pay for your debug time. No way around this. It is a running Lambda function after all.
  • context.callbackWaitsForEmptyEventLoop is defaulted to false. You can change it back to true (we even shimmed that!), but your function will run until it times out if you do.
  • context.getRemainingTimeInMillis is technically an approximation. We grab the remaining time and the current timestamp when the debugger connects and ship both to the child. The time delta is then subtracted from the original time. Since all times are retrieved inside of the Lambda, this should be a very close approximation.

Anything else I should know?

Functionally, this thing is complete. However it is still very new, so don't be surprised if something goes wrong. Feel free to file an issue. We're happy to take PRs too.

Items that need work still

  • Tests: There are no tests. Because of all of the dark arts, eventing stuff, and the sockets, we didn't want to delay release because the tests are going to be hard to write. If you're a ninja at this, feel free to reach out.
  • Improving logging in the broker server
  • Bulletproof socket cleanup: We're still trying to make sure that all sockets are cleaned up as fast as possible. If you find any leaks, please let us know.
  • Study memory usage in the broker.

Future ideas:

  • Make things more configurable
  • Web UI for broker server
    • Get direct link as soon as Lambda connects to broker
    • Track remaining time

Made with 💝 and a pinch of black magic: by Jessica Ribeiro + Trek10

P.S.: We do AWS consulting and monitoring. Come talk to us.

Twitter: Jessica + Trek10

More Repositories

1

awsume

A utility for easily assuming AWS IAM roles from the command line.
Python
493
star
2

awsets

A utility for crawling an AWS account and exporting all its resources for further analysis.
Go
194
star
3

serverless-secrets

An opinionated tool for safely managing and deploying Serverless projects and their secrets.
JavaScript
165
star
4

ddb-single-table-example

Companion code to this blog post https://www.trek10.com/blog/dynamodb-single-table-relational-modeling/
Python
151
star
5

ecr-cleaner

A Serverless implementation for removing old images from ECR Repositories with some intelligence.
JavaScript
78
star
6

awsume-console-plugin

This is a plugin that enables you to use your assumed role credentials to open the AWS console in your default browser.
Python
62
star
7

serverless-graph

Outputs your serverless architecture and resources as a Graphviz dot compatible output
JavaScript
61
star
8

cloudformation-graph

Generate dot files for GraphViz from an AWS CloudFormation template.
JavaScript
56
star
9

serverless-cloudformation-changesets

Natively deploy to CloudFormation via Change sets, instead of directly. Allowing you to queue changes, and safely require escalated roles for final deployment.
JavaScript
47
star
10

dynamodb-autoscaling

Serverlessly monitor and autoscale DynamoDB
JavaScript
41
star
11

gitlab-boilerplate-injector

A small tool for injecting files into new GitLab repos
JavaScript
33
star
12

serverless-cognito-manager

Make it easy to use and manage cognito in your serverless projects
JavaScript
24
star
13

serverless-cloudformation-parameter-setter

Set cloudformation parameters when deploying using serverless framework
JavaScript
21
star
14

lambda-error-hound

Utility for quickly finding cloudwatch logs surrounding an lambda error
JavaScript
18
star
15

lambda-clock

An awesome little utility for 1-minute serverless cron on AWS, brought to you by the AWS experts at Trek10.
JavaScript
17
star
16

graphql-default-value-transformer

TypeScript
16
star
17

sfn-yaml-macro

DEPRECATED - An AWS CloudFormation macro to allow the definition of Amazon States Language in YAML within a CloudFormation template
Python
16
star
18

config-excess-access-exorcism

An AWS config rule that uses IAM Access Advisor to find over-permissioned IAM users, groups, and roles.
Python
16
star
19

iam-scrapbook

A collection of IAM policies we use
11
star
20

lambda-local-cache

JavaScript
10
star
21

lambda-router

Simple lightweight lambda router
JavaScript
10
star
22

serverless-aws-lambda-fanout

Quickly add a fanout lambda with targets to a serverless project
JavaScript
8
star
23

appsync-cloudformation-transformer-cli

JavaScript
8
star
24

jekyll-rocket

A quickstart for building and deploying high performance static websites with Jekyll.
CSS
8
star
25

aws-serverless-workshops

JavaScript
8
star
26

serverless-secrets-python

Python
7
star
27

serverless-kinesis-stream-management

JavaScript
6
star
28

dynamodb-mutex

npm module that implements a mutex using DynamoDB
JavaScript
6
star
29

cognito-login

A CLI to get a jwt from cognito login credentials
Python
6
star
30

serverless-video-sharing

Workshop lab for building a serverless video sharing site
JavaScript
6
star
31

event-driven-security

Hands on lab for event-driven security on AWS
Python
5
star
32

lambda-powertools-typescript-examples

Example code for Lambda Powertools for Typescript from the "Power-up Lambda functions with AWS Lambda Powertools for TypeScript" blog post.
TypeScript
4
star
33

cloudformation-toolbox

Various cloudformation utilities we've built over time
Python
3
star
34

cake

CloudFormation Make
Makefile
2
star
35

dynamodb-table-cache

JavaScript
2
star
36

awsume-key-rotation-plugin

Python
2
star
37

trek10-ci

Dockerfile
2
star
38

cohesive-networks-vns3-vpn-monitoring

Python
2
star
39

cfn-changeset-viewer

View CloudFormation ChangeSets
JavaScript
1
star
40

generative-ai-curriculum

Trek10's curriculum for running generative AI workloads on AWS
Jupyter Notebook
1
star
41

serverless-xkcd-for-slack

A super efficient, fast XKCD slash command for Slack.
JavaScript
1
star
42

serverless-chatbot

Hands-on lab for building a serverless chatbot
JavaScript
1
star
43

s3-permissions-blog-post

Companion code to this blog post https://www.trek10.com/blog/s3-permissions
Python
1
star
44

cloudoffset.com

Save the Cloud, Save the Earth
JavaScript
1
star
45

LBR-Context-Manager

The best tool for AWS lambda-backed custom resources since sliced cheese!
Python
1
star