Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 38 additions & 98 deletions docs/lambda.asciidoc
Original file line number Diff line number Diff line change
@@ -1,112 +1,72 @@
:framework: lambda

[[lambda]]

ifdef::env-github[]
NOTE: For the best reading experience,
please view this documentation at https://www.elastic.co/guide/en/apm/agent/nodejs/current/lambda.html[elastic.co]
endif::[]

=== Get started with Lambda

experimental::[]
=== Monitoring AWS Lambda Node.js Functions
:layer-section-type: with-agent

Getting Elastic APM set up for your lambda functions is easy,
and there are various ways you can tweak it to fit your needs.
Follow the guide below to get started, and for more advanced topics,
check out the <<api,API Reference>>.
The Node.js APM Agent can be used with AWS Lambda to monitor the execution of your AWS Lambda functions.

[float]
[[lambda-installation]]
==== Installation

Setting up your Lambda function is a two-step process.
[[aws-lambda-nodejs-quick-start]]
=== Quick Start

1. <<lambda-extension>>
2. <<lambda-instrumenting>>

First, https://github.com/elastic/apm-aws-lambda/blob/main/docs/aws-lambda-extension.asciidoc[install the Elastic
APM Lambda extension] into your Lambda environment. Elastic uses a Lambda
extension to forward data to APM Server in a way that does not interfere with the
execution of your Lambda function.
To get started with APM for your Node.js AWS Lambda functions follow the steps below.

[float]
[[lambda-extension]]
==== Install the Elastic APM Lambda extension

Elastic uses a Lambda extension to forward data to APM Server in a way that does not interfere with the
execution of your Lambda function.
[[aws-lambda-nodejs-prerequisites]]
==== Prerequisites

See the https://github.com/elastic/apm-aws-lambda/blob/main/docs/aws-lambda-extension.asciidoc[installation documentation] to get started.

Compatibility note: If using v3.30.0 or later of the APM Node.js Agent, one
must use v0.0.4 or later of the APM Lambda extension.

Once you have the extension installed in your environment, you can
configure your Lambda handler to instrument your function.
You need an APM Server to send APM data to. Follow the {apm-guide-ref}/apm-quick-start.html[APM Quick start] if you have not set one up yet. For the best-possible performance, we recommend setting up APM on {ecloud} in the same AWS region as your AWS Lambda functions.

[float]
[[lambda-instrumenting]]
==== Instrumenting your Lambda Handler
==== Step 1: Select the AWS Region and Architecture

Add the `elastic-apm-node` module as a dependency to your application:

[source,bash]
----
npm install elastic-apm-node --save
----
include::{apm-aws-lambda-root}/docs/lambda-selector/lambda-attributes-selector.asciidoc[]

[float]
[[lambda-initialization]]
==== Initialization

It's important that the agent is started before you require *any* other modules in your Node.js application - i.e. before `http`, etc.
==== Step 2: Add the APM Layers to your Lambda function

There are two ways to achieve this. The first is via automatic initialization using the `NODE_OPTIONS` environment variable. The second is via manual instrumentation.
include::{apm-aws-lambda-root}/docs/lambda-selector/extension-arn-replacement.asciidoc[]
include::./lambda/nodejs-arn-replacement.asciidoc[]

==== Automatic Initialization
Both the {apm-guide-ref}/aws-lambda-arch.html[APM Lambda Extension] and the Node.js APM Agent are added to your Lambda function as https://docs.aws.amazon.com/lambda/latest/dg/invocation-layers.html[AWS Lambda Layers]. Therefore, you need to add the corresponding Layer ARNs (identifiers) to your Lambda function.

In order to automatically initialize the agent, you'll need to set the following environment variable in your Lambda function's `Configuration -> Environment variables` section.
include::{apm-aws-lambda-root}/docs/add-extension/add-extension-layer-widget.asciidoc[]

[source,bash]
---
NODE_OPTIONS=-r elastic-apm-node/start.js
---

With this value set the agent will initialize prior to AWS loading your Lambda handler. This will allow the agent to automatically instrument your modules including the lambda handler itself.
[float]
==== Step 3: Configure APM on AWS Lambda

==== Manual Initialization
The APM Lambda Extension and the APM Node.js agent are configured through environment variables on the AWS Lambda function.

If you can't use the `NODE_OPTIONS` variable, you'll need to manually initialize the agent and manually instrument your Lambda handler. Here's a simple lambda example with the Elastic APM agent installed:
For the minimal configuration, you will need the _APM Server URL_ to set the destination for APM data and an _{apm-guide-ref}/secret-token.html[APM Secret Token]_.
If you prefer to use an {apm-guide-ref}/api-key.html[APM API key] instead of the APM secret token, use the `ELASTIC_APM_API_KEY` environment variable instead of `ELASTIC_APM_SECRET_TOKEN` in the following configuration.

[source,js]
----
// Add this to the VERY top of your lambda handler module
const apm = require('elastic-apm-node').start({})
include::./lambda/configure-lambda-widget.asciidoc[]

exports.handler = apm.lambda(function handler (event, context, callback) {
callback(null, `Hello, ${event.name}!`)
})
----
You can optionally <<configuration, fine-tune the Node.js agent>> or the {apm-guide-ref}/aws-lambda-config-options.html[configuration of the APM Lambda Extension].

The agent will now monitor the performance of your lambda function.
That's it. After following the steps above, you're ready to go!
Your Lambda function invocations should be traced from now on.

*IMPORTANT*: During installation of the Lambda extension you'll have set a number of environment variables for configuring the agent. We recommend relying on these environment variables -- while it's possible to set configuration values via the configuration object passed to `start`, doing so may interfere with the configuration required by the Lambda extension.
Read on to learn more about the features and limitations of the Node.js APM Agent on AWS Lambda Functions.

[float]
[[lambda-full-documentation]]
===== Full documentation
[[aws-lambda-features-and-caveats]]
=== Features and Caveats

* <<advanced-setup,Setup and Configuration>>
* <<api,API Reference>>
The AWS Lambda as a runtime behaves differently from conventional runtimes.
While most APM and monitoring concepts apply to AWS Lambda, there are a few differences and limitations to be aware of.

[float]
[[lambda-performance-monitoring]]
[[aws-lambda-performance-monitoring]]
==== Performance monitoring

Elastic APM automatically measures the performance of your lambda function executions.
It records traces for database queries,
external HTTP requests,
It records traces for database queries, external HTTP requests,
and other slow operations that happen during execution.

By default, the agent will trace <<supported-technologies,the most common modules>>.
Expand All @@ -116,35 +76,15 @@ For information about custom traces,
see the <<custom-spans,Custom Spans section>>.

[float]
[[lambda-error-logging]]
==== Error logging
[[aws-lambda-error-monitoring]]
==== Error monitoring

include::./shared-set-up.asciidoc[tag=error-logging]

[float]
[[lambda-filter-sensitive-information]]
==== Filter sensitive information

include::./shared-set-up.asciidoc[tag=filter-sensitive-info]

[float]
[[lambda-add-your-own-data]]
==== Add your own data
[[aws-lambda-caveats]]
==== Caveats

The Node.js agent will keep track of the active lambda function execution and will link it to errors and recorded transaction metrics when they are sent to the Elastic APM server.
This allows you to see details about which execution resulted in a particular error or which lambda functions are slow.

But in many cases,
information about the lambda execution itself isn't enough.
To add even more metadata to errors and transactions,
use one of the two functions below:

* <<apm-set-user-context,`apm.setUserContext()`>> - Call this to enrich collected performance data and errors with information about the user/client
* <<apm-set-custom-context,`apm.setCustomContext()`>> - Call this to enrich collected performance data and errors with any information that you think will help you debug performance issues and errors (this data is only stored, but not indexed in Elasticsearch)
* <<apm-set-label,`apm.setLabel()`>> - Call this to enrich collected performance data and errors with simple key/value strings that you think will help you debug performance issues and errors (labels are indexed in Elasticsearch)

[float]
[[lambda-troubleshooting]]
==== Troubleshooting
* System and custom metrics are not collected for Lambda functions. This is both because most of those are irrelevant
and because the interval-based event sending model is not suitable for FaaS environments.

include::./shared-set-up.asciidoc[tag=troubleshooting-link]
99 changes: 99 additions & 0 deletions docs/lambda/configure-lambda-widget.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
++++
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@trentm @bmorelli25 First -- this widget looks super cool. I didn't realize this sort of thing was possible with ASCII Doc. 💯

Second -- A couple of questions about the widget (apologies if these are retreads of discussions had elsewhere) --

  1. Is this using an authenticated or unauthenticated request to api.github.com? It looks like the later to me -- if so, do we have any concerns about hitting GitHub's API limits for public requests? Per the docs -- For unauthenticated requests, the rate limit allows for up to 60 requests per hour. Unauthenticated requests are associated with the originating IP address, and not the person making requests.?

  2. If there anything special we need to do/maintain in order to make sure this continues to work with future releases? (It looks like all we need to do is make sure the full ARNs are posted to the releases tab in GitHub as part of the release body text -- is there more to it than that?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't realize this sort of thing was possible with ASCII Doc.

All credit to @AlexanderWert and Brandon.

do we have any concerns about hitting GitHub's API limits for public requests?

It'll be limited by the IP of the personal reading the docs... so not super concerned. However, yah, I think the templating could perhaps improve to have reasonable content for if/when that request fails. Currently it'll look a little confusing. I opened elastic/apm-aws-lambda#171 for this.

anything special we need to do/maintain in order to make sure this continues to work with future releases?

My understanding is just that: full ARNs in the releases content.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All credit to @AlexanderWert and Brandon.

The ARN generator was all Alex—a really neat use of Asciidoc's HTML passthrough blocks.

<div class="tabs" data-tab-group="os">
<div role="tablist" aria-label="dependency">
<button role="tab"
aria-selected="true"
aria-controls="console-tab-lambda-nodejs-config"
id="console-lambda-nodejs-config">
AWS Web Console
</button>
<button role="tab"
aria-selected="false"
aria-controls="cli-tab-lambda-nodejs-config"
id="cli-lambda-nodejs-config"
tabindex="-1">
AWS CLI
</button>
<button role="tab"
aria-selected="false"
aria-controls="sam-tab-lambda-nodejs-config"
id="sam-lambda-nodejs-config"
tabindex="-1">
SAM
</button>
<button role="tab"
aria-selected="false"
aria-controls="serverless-tab-lambda-nodejs-config"
id="serverless-lambda-nodejs-config"
tabindex="-1">
Serverless
</button>
<button role="tab"
aria-selected="false"
aria-controls="terraform-tab-lambda-nodejs-config"
id="terraform-lambda-nodejs-config"
tabindex="-1">
Terraform
</button>
</div>
<div tabindex="0"
role="tabpanel"
id="console-tab-lambda-nodejs-config"
name="lambda-tabpanel"
aria-labelledby="console-lambda-nodejs-config">
++++

include::configure-lambda.asciidoc[tag=console-{layer-section-type}]

++++
</div>
<div tabindex="0"
role="tabpanel"
id="cli-tab-lambda-nodejs-config"
name="lambda-tabpanel"
aria-labelledby="cli-lambda-nodejs-config"
hidden="">
++++

include::configure-lambda.asciidoc[tag=cli-{layer-section-type}]

++++
</div>
<div tabindex="0"
role="tabpanel"
id="sam-tab-lambda-nodejs-config"
name="lambda-tabpanel"
aria-labelledby="sam-lambda-nodejs-config"
hidden="">
++++

include::configure-lambda.asciidoc[tag=sam-{layer-section-type}]

++++
</div>
<div tabindex="0"
role="tabpanel"
id="serverless-tab-lambda-nodejs-config"
name="lambda-tabpanel"
aria-labelledby="serverless-lambda-nodejs-config"
hidden="">
++++

include::configure-lambda.asciidoc[tag=serverless-{layer-section-type}]

++++
</div>
<div tabindex="0"
role="tabpanel"
id="terraform-tab-lambda-nodejs-config"
name="lambda-tabpanel"
aria-labelledby="terraform-lambda-nodejs-config"
hidden="">
++++

include::configure-lambda.asciidoc[tag=terraform-{layer-section-type}]

++++
</div>
</div>
++++
95 changes: 95 additions & 0 deletions docs/lambda/configure-lambda.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// tag::console-with-agent[]

To configure APM through the AWS Management Console:

1. Navigate to your function in the AWS Management Console
2. Click on the _Configuration_ tab
3. Click on _Environment variables_
4. Add the following required variables:

[source,bash]
----
NODE_OPTIONS = -r elastic-apm-node/start # use this exact fixed value
ELASTIC_APM_LAMBDA_APM_SERVER = <YOUR-APM-SERVER-URL> # this is your APM Server URL
ELASTIC_APM_SECRET_TOKEN = <YOUR-APM-SECRET-TOKEN> # this is your APM secret token
----

--
include::{apm-aws-lambda-root}/docs/images/images.asciidoc[tag=nodejs-env-vars]
--

// end::console-with-agent[]

// tag::cli-with-agent[]

To configure APM through the AWS command line interface execute the following command:

[source,bash]
----
aws lambda update-function-configuration --function-name yourLambdaFunctionName \
--environment "Variables={NODE_OPTIONS=-r elastic-apm-node/start,ELASTIC_APM_LAMBDA_APM_SERVER=<YOUR-APM-SERVER-URL>,ELASTIC_APM_SECRET_TOKEN=<YOUR-APM-SECRET-TOKEN>}"
----

// end::cli-with-agent[]

// tag::sam-with-agent[]

In your SAM `template.yml` file add the Layer ARNs of the APM Extension and the APM Agent as follows:

[source,yml]
----
...
Resources:
yourLambdaFunction:
Type: AWS::Serverless::Function
Properties:
...
Environment:
Variables:
NODE_OPTIONS: -r elastic-apm-node/start
ELASTIC_APM_LAMBDA_APM_SERVER: <YOUR-APM-SERVER-URL>
ELASTIC_APM_SECRET_TOKEN: <YOUR-APM-SECRET-TOKEN>
...
----

// end::sam-with-agent[]

// tag::serverless-with-agent[]

In your `serverless.yml` file add the Layer ARNs of the APM Extension and the APM Agent to your function as follows:

[source,yml]
----
...
functions:
yourLambdaFunction:
...
environment:
NODE_OPTIONS: -r elastic-apm-node/start
ELASTIC_APM_LAMBDA_APM_SERVER: <YOUR-APM-SERVER-URL>
ELASTIC_APM_SECRET_TOKEN: <YOUR-APM-SECRET-TOKEN>
...
----

// end::serverless-with-agent[]

// tag::terraform-with-agent[]
To add the APM Extension and the APM Agent to your function add the ARNs to the `layers` property in your Terraform file:

[source,terraform]
----
...
resource "aws_lambda_function" "your_lambda_function" {
...
environment {
variables = {
NODE_OPTIONS = "-r elastic-apm-node/start"
ELASTIC_APM_LAMBDA_APM_SERVER = "<YOUR-APM-SERVER-URL>"
ELASTIC_APM_SECRET_TOKEN = "<YOUR-APM-SECRET-TOKEN>"
}
}
}
...
----

// end::terraform-with-agent[]
7 changes: 7 additions & 0 deletions docs/lambda/nodejs-arn-replacement.asciidoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
++++
<script>
window.addEventListener("DOMContentLoaded", async () => {
addArnGenerator('agent', 'apm-agent-nodejs', 'arn:aws:lambda:${region}:267093732750:layer:elastic-apm-node-${version}');
});
</script>
++++
Loading