We have been developing AWS Lambda functions in Node JS for a few months. Can we debug, i.e. step through the Node JS code as we can with .Net C# code in Visual Studio?
Trek 10 released an interesting tool recently that allows stepping through node.js code live in AWS lambda functions! How, you may ask? "Magic and mirrors", according to them :-)
Apparently, it doesn't attach to Amazon's host process directly (which is impossible), but it forks your code into a child process running in debug mode and proxies connections to your local Chrome DevTools. (There is actually a little more to the configuration, which you can read about at the github repo below.)
Here's the announcement: https://www.trek10.com/blog/aws-lambda-debugger/
and the github repo: https://github.com/trek10inc/aws-lambda-debugger
Step through debugging is now possible with AWS Lambda with the Sigma IDE. Yes, you debug the function while its actually running within AWS - See the demo below https://serverless.asankha.com/2019/08/write-lambda-function-test-it-instantly.html
IDE-based development tools are not available by default for many Lambda functions. There are some plugins, such as the Visual Studio support introduced by AWS on their blog at https://aws.amazon.com/blogs/developer/aws-lambda-support-in-visual-studio/, but these will have varying levels of feature sets and support.
In order to test Lambda using step debugging, you'll need to focus on two domains - the hardware on which it runs, and the way in which your Lambda function is invoked. The hardware is challenging to emulate, as AWS keeps the particulars of the machine instances that run your lambda functions secret. As such, when it comes to emulating hardware, you'll simply need to keep within what's reasonable for your language and operating system - make sure that the correct run time is installed (as in, don't install NodeJS 4.X when you're working with the version 6 runtime), make sure you don't exceed storage requirements (AMIs for Lambda get 500 MB of temporary storage space in /tmp), and ensure you're not saving any state locally prior to runs of your code.
Once you've nailed down your machine requirements (or decided to pass on them as your code doesn't do any hardware-specific work), then you'll need to write a test harness to invoke your AWS Lambda function. This test harness serves as an entry point for your debugger, and while it is most likely not 100% accurate with respect to how AWS invokes Lambda (for example, the context
parameter contains information on your current Lambda invocation, which will by nature vary between executions), it gets you to the point where you can invoke all of your standard coding support tools.
Note: the following simple test harness is written for Node.JS, but you can adapt the concepts to the runtime in which your Lambda executes
The first thing we'll do is create a new file - debug.js - and import the handler function prototype. Assuming you've defined your handler in handler.js, and called it handler
, you do that as follows:
var handler = require('./handler.js').handler;
Next, we need to invoke the handler function. As I alluded to above, each of the parameters has a different purpose. The first parameter to the handler -event
- has details of the event causing the invocation. Note: This also includes your function arguments. The second parameter, as we discussed, contains information on the context in which your function is running. There's also a third parameter, callback, that can be used to invoke a callback upon completion of your Lambda execution. Review the AWS docs here: http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
So for our purposes, for a simple test harness, we just need to send the parameters through in the event
parameter. We'll leave the context
and callback
parameters alone for now (with a minor modification, more on that below), but if you want to provide extra data there that your function relies upon that's fine - just make sure it doesn't conflict with any of the automated data put there in AWS. So we define the parameter hash, and invoke the function, using the following code in debug.js
:
var parameters = {
"key1":"val1",
"object" :{},
// other keys as necessary
};
handler(parameters, {succeed:function(result){
console.log("success: ", JSON.stringify(result, null, 2));
process.exit(0);
}, fail:function(error){
console.error("error: ", error);
process.exit(1);
}});
This code does a few interesting things:
context.succeed(message)
or context.fail(error)
. These are not officially supported by Lambda, but are instead used as a workaround in our code to get access to success/fail behavior
Once you've written this simple test harness, and adapted your Lambda code to invoke the success/fail handler if they are present (something as simple as if(context.success){context.success(args);}
should be sufficient), you can now invoke the lambda function using node debug.js
and see the results in the console.
I've also had great luck with unit testing in my Lambda functions. As you now have an entry point, and an example of how to call the Lambda function, you should be able to write suitable unit and function tests that express all of your functionality.
As I mentioned, this approach isn't perfect. Here are a few problems with the test harness that could potentially arise:
context
object, this approach may run into problemsHowever, despite the above, you should now have the capability to use local debugging tools to test and debug your Lambda functions. We use a similar framework at Backand - https://www.backand.com - for our Lambda function development tool, and it has greatly increased our Lambda development velocity.