Home

Awesome

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.

# 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:

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:

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:

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

Future ideas:

Made with :gift_heart: 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