From dce0fae3e76c93e4c8782fdc14ae2b9b01ccc170 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Thu, 9 Apr 2026 14:19:53 +0900 Subject: [PATCH 1/2] feat(aws-serverless): Add lambda extension to npm package This PR adds the Lambda Extension as a build output to the npm package. It was previously only available through our Lambda layer but is useful for users that do not use the layer but would still like to use our extension. To use the extension with container image lambdas, copy the extension files to your Docker image and set the `tunnel` option in your application. This requires the installation of `@sentry/aws-serverless`, regardless of which other Sentry SDK is used in your application. ```dockerfile RUN mkdir -p /opt/sentry-extension COPY node_modules/@sentry/aws-serverless/build/lambda-extension/sentry-extension /opt/extensions/sentry-extension COPY node_modules/@sentry/aws-serverless/build/lambda-extension/index.mjs /opt/sentry-extension/index.mjs RUN chmod +x /opt/extensions/sentry-extension /opt/sentry-extension/index.mjs ``` ```js Sentry.init({ dsn: '__DSN__', tunnel: 'http://localhost:9000/envelope', }); ``` Closes: #20114 --- CHANGELOG.md | 20 +++++++++ packages/aws-serverless/README.md | 44 +++++++++++++++++++ packages/aws-serverless/package.json | 23 ++++++++-- .../rollup.lambda-extension.config.mjs | 2 +- .../scripts/buildLambdaExtension.ts | 18 ++++++++ .../scripts/buildLambdaLayer.ts | 12 ++++- 6 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 packages/aws-serverless/scripts/buildLambdaExtension.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d5f23d019578..d8e174f4376f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +- **feat(aws-serverless): Ship Lambda extension in npm package for container image Lambdas ([#20133](https://github.com/getsentry/sentry-javascript/pull/20133))** + + The Sentry Lambda extension is now included in the npm package, enabling container image-based Lambda functions to use it. Copy the extension files into your Docker image and set the `tunnel` option: + + ```dockerfile + RUN mkdir -p /opt/sentry-extension + COPY node_modules/@sentry/aws-serverless/build/lambda-extension/sentry-extension /opt/extensions/sentry-extension + COPY node_modules/@sentry/aws-serverless/build/lambda-extension/index.mjs /opt/sentry-extension/index.mjs + RUN chmod +x /opt/extensions/sentry-extension /opt/sentry-extension/index.mjs + ``` + + ```js + Sentry.init({ + dsn: '__DSN__', + tunnel: 'http://localhost:9000/envelope', + }); + ``` + + This works with any Sentry SDK (`@sentry/aws-serverless`, `@sentry/sveltekit`, `@sentry/node`, etc.). + - **feat(cloudflare): Support basic WorkerEntrypoint ([#19884](https://github.com/getsentry/sentry-javascript/pull/19884))** `withSentry` now supports instrumenting classes extending Cloudflare's `WorkerEntrypoint`. This instruments `fetch`, `scheduled`, `queue`, and `tail` handlers. diff --git a/packages/aws-serverless/README.md b/packages/aws-serverless/README.md index 81372f2178d2..bf25bb74f177 100644 --- a/packages/aws-serverless/README.md +++ b/packages/aws-serverless/README.md @@ -73,6 +73,50 @@ export const handler = (event, context, callback) => { }; ``` +## Container Image-based Lambda Functions + +When using container image-based Lambda functions (e.g., with [Lambda Web Adapter](https://github.com/awslabs/aws-lambda-web-adapter) for frameworks like SvelteKit, Next.js, or Remix), Lambda layers cannot be attached. Instead, you can install the Sentry Lambda extension directly into your Docker image. The extension tunnels Sentry events through a local proxy, improving event delivery reliability during Lambda freezes. + +### Setup + +1. Install `@sentry/aws-serverless` as a dependency — even if you use a different Sentry SDK in your application (e.g., `@sentry/sveltekit`), this package contains the extension files needed for the Docker image. + +2. Copy the extension files from the npm package into your Docker image: + +```dockerfile +FROM public.ecr.aws/lambda/nodejs:22 + +# Copy the Sentry Lambda extension +RUN mkdir -p /opt/sentry-extension +COPY node_modules/@sentry/aws-serverless/build/lambda-extension/sentry-extension /opt/extensions/sentry-extension +COPY node_modules/@sentry/aws-serverless/build/lambda-extension/index.mjs /opt/sentry-extension/index.mjs +RUN chmod +x /opt/extensions/sentry-extension /opt/sentry-extension/index.mjs + +# ... rest of your Dockerfile +``` + +3. Point your Sentry SDK at the extension using the `tunnel` option. The extension always listens on `http://localhost:9000/envelope` — this URL is fixed and must be used exactly as shown: + +```js +import * as Sentry from '@sentry/aws-serverless'; + +Sentry.init({ + dsn: '__DSN__', + tunnel: 'http://localhost:9000/envelope', +}); +``` + +This works with any Sentry SDK: + +```js +import * as Sentry from '@sentry/sveltekit'; + +Sentry.init({ + dsn: '__DSN__', + tunnel: 'http://localhost:9000/envelope', +}); +``` + ## Integrate Sentry using the Sentry Lambda layer Another much simpler way to integrate Sentry to your AWS Lambda function is to add the official layer. diff --git a/packages/aws-serverless/package.json b/packages/aws-serverless/package.json index 961b3258e88c..123453c72ee3 100644 --- a/packages/aws-serverless/package.json +++ b/packages/aws-serverless/package.json @@ -12,7 +12,8 @@ "files": [ "/build/npm", "/build/import-hook.mjs", - "/build/loader-hook.mjs" + "/build/loader-hook.mjs", + "/build/lambda-extension" ], "main": "build/npm/cjs/index.js", "module": "build/npm/esm/index.js", @@ -79,8 +80,9 @@ "@vercel/nft": "^1.3.0" }, "scripts": { - "build": "run-p build:transpile build:types && run-s build:layer", - "build:layer": "rimraf build/aws && rollup -c rollup.lambda-extension.config.mjs && yarn ts-node scripts/buildLambdaLayer.ts", + "build": "run-p build:transpile build:types build:extension && run-s build:layer", + "build:extension": "rollup -c rollup.lambda-extension.config.mjs && yarn ts-node scripts/buildLambdaExtension.ts", + "build:layer": "rimraf build/aws && yarn ts-node scripts/buildLambdaLayer.ts", "build:dev": "run-p build:transpile build:types", "build:transpile": "rollup -c rollup.npm.config.mjs", "build:types": "run-s build:types:core build:types:downlevel", @@ -118,13 +120,26 @@ "{projectRoot}/build/npm/cjs" ] }, + "build:extension": { + "inputs": [ + "production", + "^production" + ], + "dependsOn": [ + "^build:transpile" + ], + "outputs": [ + "{projectRoot}/build/lambda-extension" + ] + }, "build:layer": { "inputs": [ "production", "^production" ], "dependsOn": [ - "build:transpile" + "build:transpile", + "build:extension" ], "outputs": [ "{projectRoot}/build/aws" diff --git a/packages/aws-serverless/rollup.lambda-extension.config.mjs b/packages/aws-serverless/rollup.lambda-extension.config.mjs index cf7f369d9175..8a63778be7af 100644 --- a/packages/aws-serverless/rollup.lambda-extension.config.mjs +++ b/packages/aws-serverless/rollup.lambda-extension.config.mjs @@ -7,7 +7,7 @@ export default [ outputFileBase: 'index.mjs', packageSpecificConfig: { output: { - dir: 'build/aws/dist-serverless/sentry-extension', + dir: 'build/lambda-extension', sourcemap: false, }, }, diff --git a/packages/aws-serverless/scripts/buildLambdaExtension.ts b/packages/aws-serverless/scripts/buildLambdaExtension.ts new file mode 100644 index 000000000000..a417247e7b01 --- /dev/null +++ b/packages/aws-serverless/scripts/buildLambdaExtension.ts @@ -0,0 +1,18 @@ +import * as fs from 'fs'; + +// Copy the bash wrapper script into the rollup output directory +// so the npm package ships both the compiled extension and the wrapper. +const targetDir = './build/lambda-extension'; +const source = './src/lambda-extension/sentry-extension'; +const target = `${targetDir}/sentry-extension`; + +if (!fs.existsSync(targetDir)) { + fs.mkdirSync(targetDir, { recursive: true }); +} + +fs.copyFileSync(source, target); + +// The wrapper must be executable because AWS Lambda discovers extensions by +// scanning /opt/extensions/ for executable files. If the file isn't executable, +// Lambda won't register it as an extension. +fs.chmodSync(target, 0o755); diff --git a/packages/aws-serverless/scripts/buildLambdaLayer.ts b/packages/aws-serverless/scripts/buildLambdaLayer.ts index cae52eb44eeb..cca3b739bf6b 100644 --- a/packages/aws-serverless/scripts/buildLambdaLayer.ts +++ b/packages/aws-serverless/scripts/buildLambdaLayer.ts @@ -54,10 +54,18 @@ async function buildLambdaLayer(): Promise { replaceSDKSource(); + // Copy the Lambda extension from the shared build output into the layer structure. + // build/lambda-extension/ contains both index.mjs and the sentry-extension wrapper. + // Lambda requires the wrapper to be in /opt/extensions/ for auto-discovery, + // so it gets copied there separately. + fs.cpSync('./build/lambda-extension', './build/aws/dist-serverless/sentry-extension', { recursive: true }); + fs.chmodSync('./build/aws/dist-serverless/sentry-extension/index.mjs', 0o755); fsForceMkdirSync('./build/aws/dist-serverless/extensions'); - fs.copyFileSync('./src/lambda-extension/sentry-extension', './build/aws/dist-serverless/extensions/sentry-extension'); + fs.copyFileSync( + './build/aws/dist-serverless/sentry-extension/sentry-extension', + './build/aws/dist-serverless/extensions/sentry-extension', + ); fs.chmodSync('./build/aws/dist-serverless/extensions/sentry-extension', 0o755); - fs.chmodSync('./build/aws/dist-serverless/sentry-extension/index.mjs', 0o755); const zipFilename = `sentry-node-serverless-${version}.zip`; // Only include these directories in the zip file From cd81499895c91a4c4479daf595c0be9593ece077 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Thu, 9 Apr 2026 20:15:57 +0900 Subject: [PATCH 2/2] Avoid unnecessary exist check on targetDir --- packages/aws-serverless/scripts/buildLambdaExtension.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/aws-serverless/scripts/buildLambdaExtension.ts b/packages/aws-serverless/scripts/buildLambdaExtension.ts index a417247e7b01..508d7934e725 100644 --- a/packages/aws-serverless/scripts/buildLambdaExtension.ts +++ b/packages/aws-serverless/scripts/buildLambdaExtension.ts @@ -6,9 +6,7 @@ const targetDir = './build/lambda-extension'; const source = './src/lambda-extension/sentry-extension'; const target = `${targetDir}/sentry-extension`; -if (!fs.existsSync(targetDir)) { - fs.mkdirSync(targetDir, { recursive: true }); -} +fs.mkdirSync(targetDir, { recursive: true }); fs.copyFileSync(source, target);