Skip to content

fix: NodeJS sample #1840

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions .github/workflows/ci-nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,6 @@ jobs:
working-directory: nodejs
- run: npm test
working-directory: nodejs
- name: Build AWS SDK Sample
run: npm run build
working-directory: nodejs/sample-apps/aws-sdk
7,222 changes: 4,494 additions & 2,728 deletions nodejs/package-lock.json

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions nodejs/sample-apps/aws-sdk/.eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
build
cdk.out
bin
lib
2 changes: 1 addition & 1 deletion nodejs/sample-apps/aws-sdk/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ module.exports = {
"node": true
},
...require('../../eslint.config.js')
}
}
2 changes: 2 additions & 0 deletions nodejs/sample-apps/aws-sdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
cdk.out
build
84 changes: 73 additions & 11 deletions nodejs/sample-apps/aws-sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,77 @@
# AWS SDK Sample Application
# 🧪 AWS SDK Sample Application with OpenTelemetry

**This Sample App is a work-in-progress because it depends on the OpenTelemetry public layer. The public layer will not be published
This sample demonstrates how to use the **AWS SDK** within a Lambda function with **OpenTelemetry (OTel)** capabilities. It performs a simple `STS GetCallerIdentity` call for demonstration purposes and outputs Otel telemetry data to logs.

This sample application demonstrates usage of the AWS SDK. To try it out, make sure the collector and nodejs Lambda
layers are built.
Example log output:
![Sample Log Output](./sampleLogOutput.png)

In [collector](../../../collector), run `make package`.
In [nodejs](../../), run `npm install`.
---

Then, run `terraform init` and `terraform apply`. The lambda function will be initialized and the URL for an API Gateway invoking the Lambda
will be displayed at the end. Send a request to the URL in a browser or using curl to execute the function. Then,
navigate to the function's logs [here](https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#logStream:group=%252Faws%252Flambda%252Fhello-nodejs).
You will see a log stream with an event time corresponding to when you issued the request - open it and you can find
information about the exported spans in the log stream.
## 🚀 Deploy Using AWS CDK

Follow these steps to deploy the demo stack via CDK:

1. **Install dependencies**
`npm install`

2. **Bootstrap your AWS environment (if not done already)**
`npx cdk bootstrap`

3. **Deploy the stack**
`npx cdk deploy OtelSampleLambdaStack`

---

## 🚀 Deploy Using Terraform

Follow these steps to deploy the demo stack via Terraform:

1. **Install dependencies**
`npm install`

2. **Build the Lambda function artifact**
`npm run build`

3. **Move to deploy/wrapper folder**
`cd deploy/wrapper`

4. **Terraform init**
`terraform init`

5. **Terraform apply**
`terraform apply`

---

## 🛠️ Manual Deployment via AWS Console

If you'd prefer to deploy manually:

1. **Install dependencies**
`npm install`

2. **Build the Lambda function artifact**
`npm run build`

3. **Create a new AWS Lambda function**
- Runtime: Select the latest `nodejs.x`

4. **Upload the artifact**
- File location: `build/function.zip`

5. **Set the following environment variables**
```
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318/
OTEL_TRACES_EXPORTER=console
OTEL_METRICS_EXPORTER=console
OTEL_LOG_LEVEL=INFO
OTEL_TRACES_SAMPLER=always_on
AWS_LAMBDA_EXEC_WRAPPER=/opt/otel-handler
```
6. **Attach the Node.js instrumentation layer**
- Refer to the latest ARN in the OpenTelemetry Lambda releases, ie:
https://github.com/open-telemetry/opentelemetry-lambda/releases/tag/layer-nodejs%2F0.14.0

7. **Attach the OpenTelemetry Collector layer**
- Refer to the ARN in the release notes, ie:
https://github.com/open-telemetry/opentelemetry-lambda/releases/tag/layer-collector%2F0.15.0
6 changes: 6 additions & 0 deletions nodejs/sample-apps/aws-sdk/bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib';
import { OtelSampleLambdaStack } from '../lib/otel-sample-lambda-stack';

const app = new cdk.App();
new OtelSampleLambdaStack(app, 'OtelSampleLambdaStack');
3 changes: 3 additions & 0 deletions nodejs/sample-apps/aws-sdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "npm run build"
}
30 changes: 12 additions & 18 deletions nodejs/sample-apps/aws-sdk/deploy/wrapper/main.tf
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
locals {
collector_layer_arn = "arn:aws:lambda:${data.aws_region.current.name}:${var.account_id}:layer:opentelemetry-collector-arm64-${var.collector_layer_version}:1"
sdk_layer_arn = "arn:aws:lambda:${data.aws_region.current.name}:${var.account_id}:layer:opentelemetry-nodejs-${var.nodejs_layer_version}:1"
}

data "aws_region" "current" {}

module "hello-lambda-function" {
source = "terraform-aws-modules/lambda/aws"
version = ">= 2.24.0"
version = "7.21.0"

architectures = compact([var.architecture])
function_name = var.name
Expand All @@ -14,33 +21,20 @@ module "hello-lambda-function" {
timeout = 20

layers = compact([
var.collector_layer_arn,
var.sdk_layer_arn
local.collector_layer_arn,
local.sdk_layer_arn
])

environment_variables = {
AWS_LAMBDA_EXEC_WRAPPER = "/opt/otel-handler"
OTEL_TRACES_EXPORTER = "logging"
OTEL_METRICS_EXPORTER = "logging"
OTEL_TRACES_EXPORTER = "console"
OTEL_METRICS_EXPORTER = "console"
OTEL_LOG_LEVEL = "DEBUG"
OTEL_EXPORTER_OTLP_ENDPOINT = "http://localhost:4318/"
OTEL_TRACES_SAMPLER = "always_on"
}

tracing_mode = var.tracing_mode

attach_policy_statements = true
policy_statements = {
s3 = {
effect = "Allow"
actions = [
"s3:ListAllMyBuckets"
]
resources = [
"*"
]
}
}
}

module "api-gateway" {
Expand Down
27 changes: 16 additions & 11 deletions nodejs/sample-apps/aws-sdk/deploy/wrapper/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,10 @@ variable "name" {
default = "hello-nodejs-awssdk"
}

variable "collector_layer_arn" {
variable "account_id" {
type = string
description = "ARN for the Lambda layer containing the OpenTelemetry collector extension"
// TODO(anuraaga): Add default when a public layer is published.
}

variable "sdk_layer_arn" {
type = string
description = "ARN for the Lambda layer containing the OpenTelemetry NodeJS Wrapper"
// TODO(anuraaga): Add default when a public layer is published.
description = "AWS account ID where the Lambda layers are published"
default = "184161586896"
}

variable "tracing_mode" {
Expand All @@ -25,12 +19,23 @@ variable "tracing_mode" {
variable "architecture" {
type = string
description = "Lambda function architecture, valid values are arm64 or x86_64"
default = "x86_64"
default = "arm64"
}

variable "runtime" {
type = string
description = "NodeJS runtime version used for sample Lambda Function"
default = "nodejs18.x"
default = "nodejs22.x"
Copy link
Contributor

@wpessers wpessers Jun 18, 2025

Choose a reason for hiding this comment

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

Non blocking nit, but should we not change the node engine version in package.json as well? To >=22.0.0 specifically for this sample

Copy link
Contributor

Choose a reason for hiding this comment

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

Since upgrade to v2 sdk, the sdk officially supports minimum node version of 18.19.0 anyway

}

variable "collector_layer_version" {
type = string
description = "Collector layer version, see latest releases here: https://github.com/open-telemetry/opentelemetry-lambda/releases"
default = "0_15_0"
}

variable "nodejs_layer_version" {
type = string
description = "Node.js layer version, see latest releases here: https://github.com/open-telemetry/opentelemetry-lambda/releases"
default = "0_14_0"
}
40 changes: 40 additions & 0 deletions nodejs/sample-apps/aws-sdk/lambda/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context,
} from 'aws-lambda';

import { STSClient, GetCallerIdentityCommand } from '@aws-sdk/client-sts';

const sts = new STSClient({});

export const handler = async (
event: APIGatewayProxyEvent,
_context: Context,
): Promise<APIGatewayProxyResult> => {
console.log('Received event:', JSON.stringify(event, null, 2));
console.log('Received context:', JSON.stringify(_context, null, 2));

try {
const result = await sts.send(new GetCallerIdentityCommand({}));

const response: APIGatewayProxyResult = {
statusCode: 200,
body: JSON.stringify({
message: 'Caller identity retrieved successfully',
identity: {
Account: result.Account,
Arn: result.Arn,
UserId: result.UserId,
},
}),
};
return response;
} catch (error) {
console.error('Error retrieving caller identity:', error);
return {
statusCode: 500,
body: 'Internal Server Error',
};
}
};
40 changes: 40 additions & 0 deletions nodejs/sample-apps/aws-sdk/lib/otel-sample-lambda-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as path from 'path';

const AWS_ACCOUNT_ID = '184161586896'; // Replace with your AWS account ID if you want to use a specific layer
const NODEJS_LAYER_VERSION = '0_14_0'; // Update with the latest version if needed
const COLLECTOR_LAYER_VERSION = '0_15_0'; // Update with the latest version if needed

export class OtelSampleLambdaStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const region = cdk.Stack.of(this).region;
const architecture = lambda.Architecture.ARM_64;
const collectorLayerArn = `arn:aws:lambda:${region}:${AWS_ACCOUNT_ID}:layer:opentelemetry-collector-${architecture}-${COLLECTOR_LAYER_VERSION}:1`;
const nodejsInstrumentationLayerArn = `arn:aws:lambda:${region}:${AWS_ACCOUNT_ID}:layer:opentelemetry-nodejs-${NODEJS_LAYER_VERSION}:1`;

new lambda.Function(this, 'MyLambdaFunction', {
runtime: lambda.Runtime.NODEJS_22_X,
handler: 'index.handler',
code: lambda.Code.fromAsset(path.join(__dirname, '../build/lambda')),
layers: [
lambda.LayerVersion.fromLayerVersionArn(this, 'OtelCollectorLayer', collectorLayerArn),
lambda.LayerVersion.fromLayerVersionArn(this, 'NodeJsInstrumentationLayer', nodejsInstrumentationLayerArn)
],
timeout: cdk.Duration.seconds(30),
memorySize: 256,
architecture,
environment: {
OTEL_EXPORTER_OTLP_ENDPOINT: 'http://localhost:4318/',
OTEL_TRACES_EXPORTER: 'console',
OTEL_METRICS_EXPORTER: 'console',
OTEL_LOG_LEVEL: 'DEBUG',
OTEL_TRACES_SAMPLER: 'always_on',
AWS_LAMBDA_EXEC_WRAPPER: '/opt/otel-handler',
},
});
}
}
7 changes: 3 additions & 4 deletions nodejs/sample-apps/aws-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"lint:fix": "ESLINT_USE_FLAT_CONFIG=false eslint . --ext .ts --fix",
"build": "npm run clean && npm run compile && npm run postcompile",
"compile": "tsc -p .",
"postcompile": "copyfiles 'package*.json' build/src/ && npm install --production --ignore-scripts --prefix build/src/ && cd build/src && bestzip ../function.zip *"
"postcompile": "copyfiles 'package*.json' build/lambda/ && npm install --production --ignore-scripts --prefix build/lambda/ && cd build/lambda && bestzip ../function.zip *"
},
"keywords": [
"opentelemetry",
Expand All @@ -35,15 +35,14 @@
"README.md"
],
"devDependencies": {
"@aws-sdk/client-sts": "^3.826.0",
"@types/aws-lambda": "8.10.150",
"@types/node": "24.0.3",
"aws-cdk-lib": "^2.201.0",
"bestzip": "2.2.1",
"copyfiles": "2.4.1",
"rimraf": "6.0.1",
"ts-node": "10.9.2",
"typescript": "5.8.3"
},
"dependencies": {
"aws-sdk": "2.1692.0"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 0 additions & 22 deletions nodejs/sample-apps/aws-sdk/src/index.ts

This file was deleted.

3 changes: 1 addition & 2 deletions nodejs/sample-apps/aws-sdk/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"outDir": "build"
},
"include": [
"src/**/*.ts",
"test/**/*.ts"
"lambda/**/*.ts",
]
}