Skip to content

feat(aws-serverless): Replace OTel Lambda instrumentation with handler redirection#22079

Merged
andreiborza merged 16 commits into
developfrom
ab/aws-lambda-handler-redirect
Jul 9, 2026
Merged

feat(aws-serverless): Replace OTel Lambda instrumentation with handler redirection#22079
andreiborza merged 16 commits into
developfrom
ab/aws-lambda-handler-redirect

Conversation

@andreiborza

@andreiborza andreiborza commented Jul 8, 2026

Copy link
Copy Markdown
Member

What

Replaces the vendored @opentelemetry/instrumentation-aws-lambda (module patching via InstrumentationBase + import-in-the-middle) with handler redirection.

The idea is that the AWS runtime reads _HANDLER only after all --import/--require preloads (and thus Sentry.init()) have run. So during init, the AwsLambda integration points _HANDLER at a Sentry shim and stores the original value in SENTRY_ORIGINAL_HANDLER. The runtime then loads the shim instead of the user's handler; the shim resolves the original handler with the same rules as the runtime, wraps it with the existing span logic and wrapHandler, and re-exports it.

sequenceDiagram
    participant Preload as Node.js preload<br/>(--import .../awslambda-auto)
    participant RIC as Lambda runtime
    participant Shim as Sentry shim<br/>(run-lambda-handler.mjs)
    participant User as User handler module

    Note over Preload: Sentry.init()
    Preload->>Preload: SENTRY_ORIGINAL_HANDLER = "index.handler"<br/>_HANDLER = ".../run-lambda-handler.handler"
    RIC->>Shim: reads _HANDLER, await import()
    Shim->>User: resolves SENTRY_ORIGINAL_HANDLER,<br/>require() for CJS / import() for ESM
    User-->>Shim: original handler
    Shim-->>RIC: exports wrapped handler<br/>(span logic + wrapHandler, streaming symbols preserved)
    loop every invocation
        RIC->>Shim: wrapped handler(event, context)
        Shim->>User: original handler(event, context)
        User-->>Shim: result
        Shim-->>RIC: result (span ended, events flushed)
    end
Loading

Because the shim wraps the handler value (whatever the export resolves to), this also covers wrapped (e.g. middy), re-exported and streaming handlers. Behavior is unchanged (same spans, attributes, origins, mechanisms), verified by the unchanged SAM e2e assertions (Node 18/20, layer and npm install methods) and against a real Lambda function. Datadog (redirect handler) and New Relic (wrapper handler) use the same approach in their layers.

Why not orchestrion

The issue proposed orchestrion, but it does not fit the Lambda case:

  • Module matching only recognizes packages under node_modules; the handler is an arbitrary user file resolved from _HANDLER.
  • Function queries match function definitions in source, silently missing middy(...), awslambda.streamifyResponse(...), re-exports and bundled output.
  • Tracing-channel subscribers are synchronous, so they cannot delay the handler's return for the flush that must complete before the runtime freezes the sandbox.

Closes: #20908

…r redirection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread packages/aws-serverless/src/handlerResolution.ts
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 27.59 kB - -
@sentry/browser - with treeshaking flags 26.03 kB - -
@sentry/browser (incl. Tracing) 46.34 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 48.13 kB - -
@sentry/browser (incl. Tracing, Profiling) 51.12 kB - -
@sentry/browser (incl. Tracing, Replay) 85.62 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 75.26 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 90.32 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 102.97 kB - -
@sentry/browser (incl. Feedback) 44.76 kB - -
@sentry/browser (incl. sendFeedback) 32.38 kB - -
@sentry/browser (incl. FeedbackAsync) 37.51 kB - -
@sentry/browser (incl. Metrics) 28.67 kB - -
@sentry/browser (incl. Logs) 28.91 kB - -
@sentry/browser (incl. Metrics & Logs) 29.59 kB - -
@sentry/react 29.38 kB - -
@sentry/react (incl. Tracing) 48.61 kB - -
@sentry/vue 33.03 kB - -
@sentry/vue (incl. Tracing) 48.3 kB - -
@sentry/svelte 27.61 kB - -
CDN Bundle 30 kB - -
CDN Bundle (incl. Tracing) 48.32 kB - -
CDN Bundle (incl. Logs, Metrics) 31.57 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 49.64 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 70.81 kB - -
CDN Bundle (incl. Tracing, Replay) 85.84 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 87.14 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 91.64 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 92.92 kB - -
CDN Bundle - uncompressed 89.35 kB - -
CDN Bundle (incl. Tracing) - uncompressed 146.1 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 94.05 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 150.07 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 218.78 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 265.3 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 269.26 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 279 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 282.95 kB - -
@sentry/nextjs (client) 51.16 kB - -
@sentry/sveltekit (client) 46.78 kB - -
@sentry/core/server 78.42 kB - -
@sentry/core/browser 64.74 kB - -
@sentry/node-core 62.72 kB +0.01% +1 B 🔺
@sentry/node 125.38 kB +0.01% +1 B 🔺
@sentry/node (incl. diagnostics channel injection) 138.54 kB +0.01% +1 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 69.95 kB - -
@sentry/node/light 50.72 kB - -
@sentry/node - without tracing 74.06 kB +0.01% +1 B 🔺
@sentry/aws-serverless 83.24 kB -2.65% -2.26 kB 🔽
@sentry/cloudflare (withSentry) - minified 181.71 kB - -
@sentry/cloudflare (withSentry) 449.16 kB - -

View base workflow run

@andreiborza andreiborza marked this pull request as ready for review July 8, 2026 20:24
@andreiborza andreiborza requested a review from a team as a code owner July 8, 2026 20:24
@andreiborza andreiborza requested review from JPeer264, Lms24, chargome and mydea and removed request for a team and JPeer264 July 8, 2026 20:24
Comment thread packages/aws-serverless/src/integration/awslambda.ts

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2d5a898. Configure here.

Comment thread packages/aws-serverless/src/instrumentHandler.ts Outdated
Comment thread packages/aws-serverless/src/run-lambda-handler.ts

@Lms24 Lms24 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The approach seems reasonable to me, thanks for explaining it offline!

Had some minor comments regarding conventions and attribute deprecations.

return { file: basePath, format: 'cjs' };
}
if (isFile(`${basePath}.js`)) {
return { file: `${basePath}.js`, format: hasTypeModulePackageJson(path.dirname(basePath)) ? 'esm' : 'cjs' };

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

l: Do lambdas always have a package.json? Or IOW, could we run into issues here where users define an index.js file in ESM but we identify CJS?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think that's an issue indeed but that issue also exists for the user, i.e. AWS looks at it the same way. If there's a .js file but no package.json then it automatically treats it as CJS regardless of what's in the file.

* @deprecated Removed from the stable semantic conventions; not present in `@sentry/conventions`, so vendored here.
* Removed from the stable semantic conventions; not present in `@sentry/conventions`, so vendored here.
*/
export const ATTR_FAAS_EXECUTION = 'faas.execution';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m (but no blocker for this PR, since we already send the attribute today): We should add this to conventions. OR, there's already in OTEL faas.invocation.id which sounds like the replacement to this?

Either way, faas.execution should be added to conventions and depending on if we use a replacement, it needs to be deprecated.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, actually OTel already replaced this in their latest instrumentation update to use faas.invocation.id. I'll add a TODO that we should switch to faas.invocation.id in v11 but keep it as is for now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

And I'll add it as a deprecated attribute to the conventions :)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added to conventions in #22079 (comment), updated the comment in 079361e

Comment on lines +34 to 36
* Removed from the stable semantic conventions; not present in `@sentry/conventions`, so vendored here.
*/
export const ATTR_FAAS_ID = 'faas.id';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

m: I'm not sure what the best replacement for this is (but I guess there should be one since it was deprecated). But similarly to above, we should add this attribute to conventions and mark it as deprecated if there is a replacement.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Replacement is https://getsentry.github.io/sentry-conventions/attributes/cloud/#cloud-resource_id. I'll note this for v11 but keep as is for v10.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added to conventions with getsentry/sentry-conventions#475 and updated comment in 3c049fc.

@andreiborza andreiborza merged commit 2414cdc into develop Jul 9, 2026
885 of 890 checks passed
@andreiborza andreiborza deleted the ab/aws-lambda-handler-redirect branch July 9, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Rewrite AwsLambdaInstrumentation to orchestrion

2 participants