At IOpipe, we enable users of AWS Lambda to monitor, analyze, and tune their serverless architectures. Getting started is a breeze with the iopipe wrapper library.
The Serverless Framework is an indispensable tool for Lambda, OpenWhisk, and Azure functions-as-service development and deployment. Once you get the hang of launching functions with it, the sky's the limit. But when you have 10 or 100 functions, you might want to abstract your build pipeline further to include your favorite tooling + processes. There are plenty of powerful plugins already.
So to enable even easier integration with IOpipe, we made a plugin too.
How Does it Work?
The task of code modification shouldn't be taken lightly - and luckily we don't have to reinvent the wheel here. We can use an AST parser and builder to do the hard work for us. With the rise of varied JS build tools, AST libraries are certainly en vogue and we have plenty of options. For this project we use jscodeshift that provides some nice sugar on top of recast.
What are we trying to accomplish?
Here's a super-simple function that outputs a message with a UUID.
const uuid = require('uuid');
exports.handler = (event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'Your uuid is: ' + uuid.v4(),
input: event
})
};
callback(null, response);
};
Now say we need some extra metrics for our little function - how often it runs, when it errors, why it was slow. It's time for IOpipe.
Here's the new code:
const uuid = require('uuid');
const iopipe = require('iopipe')({clientId: 'TOKEN'});
exports.handler = iopipe((event, context, callback) => {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'Your uuid is: ' + uuid.v4(),
input: event
})
};
callback(null, response);
});
Nice and straightforward. The IOpipe library automatically records stats about the invocation and makes it ready for inspection in near real-time. How can we go from A => B automatically though?
Putting Code In Your Code
Let's fire up jscodeshift and parse that 🌴. First, we need to find the AST "node" that we want to wrap.
const shift = require('jscodeshift');
const code = 'The string literal of the code to transform (from above)';
const searchObject = {
left: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'exports'
},
property: {
type: 'Identifier',
name: 'handler'
}
}
};
const found = shift(code).find(shift.AssignmentExpression, searchObject);
This simply looks for the statement
exports.handler = //anything here
With our code represented as a tree, the original formatting won't make a lick of difference. As long as it runs the same, we will still find the statement we need.
exports
.handler = // 😎 this will work too
Within the Serverless plugin lifecycle, we know the functions defined in the serverless.yml
and what the handlers are named. With this info, we can be fairly confident that the right side of the statement will be a function. This is the function we need to wrap.
We create a new AST node from raw text and insert the original function node into that.
const wrapper = "require('iopipe')({clientId: 'TOKEN'})(REPLACE)";
shift(wrapper)
.find(shift.Identifier, {
name: 'REPLACE'
})
.replaceWith(found);
Not bad! You can see the real thing here.
Uploading the Result
Serverless provides some great hooks to work with and the ones we are after here are
before:deploy:createDeploymentArtifacts
after:deploy:createDeploymentArtifacts
The plugin creates a temporary .iopipe
folder, copies the source code, applies transformations, and tells Serverless to use the transformed code instead of the source code. After it's done deploying the .iopipe
folder is deleted and we go on with our day. Thanks to Jonathan Goldwasser for inspiration from the Serverless Webpack Plugin.
What Else?
While we're at it, why not keep your IOpipe npm package up to date? Upgrading automatically provides the latest speed, developer experience, and feature enhancements.
While the process of auto-upgrading the package could be a separate post, the main idea is to fire off a child process to run some npm commands. We ❤️ yarn at IOpipe, so we also support that.
A simplified example:
npm outdated && yarn add iopipe@latest
Final Thoughts
The IOpipe Serverless Plugin is now available in Beta for you to try out. Let us know what you think!