AWS Lambda Native Tracing for Node.js

This page covers the setup Instana AutoTrace for AWS Lambda functions written in Node.js.

Supported Runtimes

  • nodejs14.x
  • nodejs12.x
  • nodejs10.x

Installation

Note: This documentation explains how to set up the tracing of Node.js Lambda functions. Ensure that you also have performed the setup of the AWS Sensor for Lambda monitoring to ensure the collection of necessary information about versions and some runtime metrics that Instana cannot collect from inside the AWS Lambda runtime.

The preferred method to enable AWS Lambda Node.js tracing is to follow the AutoTrace instructions. If, for some reason that is not possible due to runtime version limitations or other local constraints, there are other options listed below.

Instana AutoTrace setup

The Instana AutoTrace setup for Node.js allows you to trace your AWS Lambda functions is to use the Instana Lambda layer and a custom function handler. This approach requires no modification of the Lambda function code and is done purely through configuration. It is also suited to be automated or included in a Lambda deployment pipeline.

To enable tracing of Node.js Lambda functions, perform the following steps:

  1. Add the Instana Lambda layer to your function.

    1. In the configuration page for your Lambda function, click on the Layers box and then on Add a layer. layer
    2. In the popup that opens, select "Provide a layer version ARN" and copy and paste the ARN for the Instana Lambda Layer that matches your region; you can find the right ARN for the Instana Lambda Layer in the Instana Lambda Layers section. layer selection
  2. Configure the Lambda Handler. Set the handler to instana-aws-lambda-auto-wrap.handler. The Instana Lambda layer will automatically trigger the default Node.js runtime handler: index.handler.

    To change the setting for the Lambda handler, find the "Basic Settings" section on the configuration page. basic settings section edit button

    Click on edit and insert instana-aws-lambda-auto-wrap.handler into the field labeled "Handler". edit basic settings to set Instana's handler

    If you are using a custom handler value (as opposed to the default handler for your runtime, see above), specify your handler in the environment variable LAMBDA_HANDLER to notify the Instana Lambda Layer. That is, if your handler is myModule.myHandler, set the environment variable LAMBDA_HANDLER to myModule.myHandler. See the next section for other environment variables that need to be set.

    Note: The Lambda configuration page might display a warning like "Lambda can't find the file instana-aws-lambda-auto-wrap.js." This might also be displayed later when returning to the configuration page. Do not let this warning distract you. The handler is contained in the Instana Lambda layer but the AWS Lambda configuration page does not take that into account.

  3. Configure Environment Variables. Add the following environment variables:

    You can also obtain the correct values for these environment variables by going to your Instana installation, click on "... More" -> "Agents" -> "Installing Instana Agents" -> Platform: "AWS" -> Technology: "AWS Lambda".

  4. Save the Lambda function definition. save

All the steps outlined above can be done either via the AWS web console or any of the usual AWS management tools, such as:

Here is an example aws CLI command that might serve as a starting point if you want to automate the Instana integration of your AWS Lambdas:

# Do not copy and paste this verbatim!
# It will overwrite any previously defined collection of layers and
# environment variables.
aws --region $YOUR_REGION lambda update-function-configuration \
  --function-name $YOUR_LAMBDA_FUNCTION_NAME \
  --layers $INSTANA_LAYER_ARN \
  --handler instana-aws-lambda-auto-wrap.handler \
  --environment ""Variables={INSTANA_ENDPOINT_URL=... , INSTANA_AGENT_KEY=... , ...}""

Instana Lambda Layers

The ARNs of the latest version of the AWS Lambda layers for Node.js are the following, per region:

Region ARN @instana/aws-lambda Version
ap-northeast-1 arn:aws:lambda:ap-northeast-1:410797082306:layer:instana-nodejs:91 1.136.1
ap-northeast-2 arn:aws:lambda:ap-northeast-2:410797082306:layer:instana-nodejs:91 1.136.1
ap-south-1 arn:aws:lambda:ap-south-1:410797082306:layer:instana-nodejs:91 1.136.1
ap-southeast-1 arn:aws:lambda:ap-southeast-1:410797082306:layer:instana-nodejs:91 1.136.1
ap-southeast-2 arn:aws:lambda:ap-southeast-2:410797082306:layer:instana-nodejs:91 1.136.1
ca-central-1 arn:aws:lambda:ca-central-1:410797082306:layer:instana-nodejs:91 1.136.1
eu-central-1 arn:aws:lambda:eu-central-1:410797082306:layer:instana-nodejs:91 1.136.1
eu-north-1 arn:aws:lambda:eu-north-1:410797082306:layer:instana-nodejs:91 1.136.1
eu-west-1 arn:aws:lambda:eu-west-1:410797082306:layer:instana-nodejs:91 1.136.1
eu-west-2 arn:aws:lambda:eu-west-2:410797082306:layer:instana-nodejs:91 1.136.1
eu-west-3 arn:aws:lambda:eu-west-3:410797082306:layer:instana-nodejs:91 1.136.1
sa-east-1 arn:aws:lambda:sa-east-1:410797082306:layer:instana-nodejs:91 1.136.1
us-east-1 arn:aws:lambda:us-east-1:410797082306:layer:instana-nodejs:91 1.136.1
us-east-2 arn:aws:lambda:us-east-2:410797082306:layer:instana-nodejs:91 1.136.1
us-west-1 arn:aws:lambda:us-west-1:410797082306:layer:instana-nodejs:91 1.136.1
us-west-2 arn:aws:lambda:us-west-2:410797082306:layer:instana-nodejs:91 1.136.1

That is, the pattern is arn:aws:lambda:${region}:410797082306:layer:instana-nodejs:${layer-version}.

Please make sure to always use the latest versions and update the layer version you are using in regular intervals to benefit from new features and fixes that we provide when publishing a new version of the layer.

Note: Until version 22, the layer name was just instana instead of instead-nodejs. Starting with version 22, we switched to the more descriptive layer name instana-nodejs.

As of version 71/1.121.0, the Node.js layer includes Instana's AWS Lambda extension. Monitoring data and traces will be offloaded locally to Instana's Lambda extension, which will then forward it to the Instana back end. This provides the following advantage: The Lambda function's response will be send back to the invoking client immediately when the handler has finished, without waiting for data to be send to the Instana back end. This feature is currently limited to Lambda functions configured with 256 MB of memory or more. Using the Instana Lambda extension can be disabled by setting the environment variable INSTANA_DISABLE_LAMBDA_EXTENSION to a non-empty string for the Lambda function.

Instana Lambda Layer for Container Based Functions

If your team uses Container based Lambda deploys, you can use our base containers on DockerHub or include the following snippet in your Dockerfile and set the build time arguments to the appropriate versions.

# This is the container image that delivers Instana's monitoring capabilities.
# It will not become the base image for your Lambda container image, it just provides a few files.
FROM instana/aws-lambda-nodejs:latest as instana-layer

# This is the actual base image for your Lambda container image. You can also use any other base image that is suitable
# for container image based Lambda functions.
FROM public.ecr.aws/lambda/nodejs:14

# Copy Instana's Node.js monitoring components into your Lambda container image.
COPY --from=instana-layer /opt/extensions/ /opt/extensions/
COPY --from=instana-layer /opt/nodejs/ /opt/nodejs/

# The remainder of your Dockerfile, as it was without adding the Instana layer. The following is just an example:
COPY index.js package.json package-lock.json /var/task/
WORKDIR /var/task
RUN npm install

# Override the CMD. This can also be done as a parameter override outside of the Dockerfile, for example in the AWS console.
CMD [ "instana-aws-lambda-auto-wrap.handler" ]

Instead of instana/aws-lambda-nodejs:latest, you can also use a specific version of the Instana npm module @instana/aws-lambda greater or equal to 1.121.0 (see CHANGELOG), so for example:

FROM instana/aws-lambda-nodejs:1.121.0 as instana-layer

Instana Lambda Layer & Manual Wrapping

You can also add the Instana Lambda layer to your function as described above, but refrain from using instana-aws-lambda-auto-wrap.handler. In particular, this is the recommended approach for AWS Lambdas based on the "Node.js 8.10" runtime. Here are the required steps for this setup:

  1. Add the Instana Lambda layer to your function. You can find the ARN of the Instana Lambda layer in the table above.
  2. Add the environment variables INSTANA_ENDPOINT_URL and INSTANA_AGENT_KEY with the required values.
  3. Modify the function's code according to the section Manually Wrapping the Handler.

Installing @instana/aws-lambda Manually

Instead of using the Instana Lambda layer, you can also choose to install the npm package @instana/aws-lambda manually:

  1. Add the dependency @instana/aws-lambda to your project by executing npm install -S @instana/aws-lambda (or yarn add @instana/aws-lambda) in your project directory. This will add the package to your node_modules folder and save the dependency in your package.json file.
  2. Add the environment variables INSTANA_ENDPOINT_URL and INSTANA_AGENT_KEY with the required values.
  3. Modify the function's code according to the section Manually Wrapping the Handler.

Manually Wrapping the Handler

If you do not want to use the auto-wrap handler, you will need to modify the code of your Node.js AWS Lambda function slightly to enable Instana tracing for it.

Note: The code modifications described in this section are not required if you use the Instana Lambda layer and the auto-wrap handler as described in the section AutoTrace AWS Lambdas.

Note: If you bundle your Lambda handler with webpack, this approach is not recommended, since Instana's core packge does not support being pre-processed with webpack. The AutoTrace AWS Lambdas approach is suitable for Lambdas using webpack. As an alternative, you can use manual wrapping and exclude, at a minimum, the package @instana/aws-lambda (or all dependencies) from being preprocessed by webpack. See this section in the Node.js documentation; for the serverless framework please also refer to the Serverless framework section.

  1. Add the line const instana = require('@instana/aws-lambda'); to the very top of your handler JavaScript file.
  2. Wrap your handler function in an instana.wrap() call.
  3. Configure the environment variables for the connection to the Instana backend as described in the Instana AutoTrace for AWS Lambda documentation.

Here are some before/after examples for the different handler function styles that can be used for Node.js based AWS Lambdas:

Async Function Style Handler

If you use an async function as your handler, it should look like this:

exports.handler = async (event, context) => {
  // your code
};

The resulting code should look like this:

const instana = require('@instana/aws-lambda');

exports.handler = instana.wrap(async (event, context) => {
  // your code
}); // <- don't forget the closing ) for the instana.wrap(

Promise Style Lambda Handler

If you use a promise style handler, it should look like this:

exports.handler = (event, context) => {
  // your code, which returns a promise
};

The resulting code should look like this:

const instana = require('@instana/aws-lambda');

exports.handler = instana.wrap((event, context) => {
  // your code, which returns a promise
}); // <- don't forget the closing ) for the instana.wrap(

Callback Style Lambda Handler

If you use a callback style handler, it should look like this:

exports.handler = (event, context, callback) => {
  // your code
};

The resulting code should look like this:

const instana = require('@instana/aws-lambda');

exports.handler = instana.wrap((event, context, callback) => {
  // your code
}); // <- don't forget the closing ) for the instana.wrap(

Configuration Object

You can also pass in an optional configuration object as the first argument when wrapping your handler:

exports.handler = instana.wrap({
    // ... your configuration, for example:
    tracing: {
      stackTraceLength: 10
    }
  },
  async (event, context) => {
  // your code
});

Configuration values that are not supported in native Lambda tracing (like agentHost, agentPort, reportUncaughtException and reportUnhandledPromiseRejections) will be silently ignored.

Note that you can also use the environment variables listed in section Other Configuration Options and most of the ones listed on the Node.js configuration page.

Integrating Instana Tracing with the Serverless Framework

Due to the way that Serverless uses the handler configuration entry as the entrypoint for packaging, you need to follow the proceduce described in the Instana Lambda Layer & Manual Wrapping section of the documentation.

As serverless-offline does not offer out-of-the-box support for Lamda layers, you might also want to install @instana/aws-lambda manually for easier local testing.

In case you use serverless-webpack or libraries based on it, you will need to exclude the package @instana/aws-lambda from preprocessing. One way of doing this is by using webpack-node-externals and a custom webpack.config.js:

const nodeExternals = require('webpack-node-externals')
const slsw = require('serverless-webpack');

module.exports = {
  entry: slsw.lib.entries,
  target: 'node',
  externals: [nodeExternals()],
  mode: 'production'
}

To include node_modules in the final package, you will also have to add a configuration entry to your serverless.yml:

custom:
  webpack:
    includeModules: true

See Node modules / externals in the serverless-webpack documentation for more details.

Finally, you need to include the necessary environment variables in your Serverless configuration. The INSTANA_AGENT_KEY environment variable should be treated as a secret. The Serverless framework blog has some suggestions for working with API keys.

Fire & Forget Calls

If you use fire-and-forget style calls in your Node.js Lambda function, Instana cannot guarantee to capture those reliably. A fire-and-forget call is any asynchronous call for which you do not wait for the result. Here is an example:

const fetch = require('node-fetch');

exports.handler = async event => {
  fetch('https://example.com/fire-and-forget'); // No await here!

  const response = await fetch('https://example.com/wait-for-me');

  return {
    message: response.text()
  };
}

This Lambda handler uses the node-fetch package to make two HTTP calls. The second call is called with the async keyword, thus execution will wait for it to finish. This call will be traced reliably.

In contrast to this, the code does not wait for the result of the first call (fetch('https://example.com/fire-and-forget')). That call might have finished by the time the Lambda execution finishes or not. If it has not finished when the Lamba execution as a whole terminates, the call will not be reported to the Instana backend. If you inspect a corresponding trace in the Instana UI you might see a warning that says "Parent span is missing for some of the entry spans in this trace during the processing."

This example used the async/await style, but similar examples also exist for promise-style and callback-style code.

Older Node.js Lambda Runtimes

At the time of writing, Amazon offers Node.js 14, 12, and 10 as Lambda runtimes. Instana does not support older Node.js Lambda runtimes that have already been decomissioned by AWS (e.g. Node 4 and 6). If you still have Lambdas deployed that use those runtimes, please update them to a more recent Node.js Lambda runtime before configuring them for native Instana tracing. Node.js 8 is still supported for now although it is already end of life.

Using the Instana API in Lambda Functions

You can access and use the entire Instana API in your Lambda code, same as in a plain vanilla Node.js app using @instana/collector. If you use the Lambda layer and the auto-wrap handler you need to add the following line to your code:

const instana = require('@instana/aws-lambda');

If you use manual wrapping you will have that line already.