feat: propagate host-language stack traces through the jsii protocol#5153
Open
otaviomacedo wants to merge 4 commits into
Open
feat: propagate host-language stack traces through the jsii protocol#5153otaviomacedo wants to merge 4 commits into
otaviomacedo wants to merge 4 commits into
Conversation
## The problem
When using the AWS CDK from a jsii-hosted language (Python, Java, Go, .NET), the CDK captures stack traces to record in
construct metadata — for example, to show where a construct was instantiated or where a property was assigned.
However, these stack traces are captured inside the Node.js kernel process, so they contain JavaScript frames that are
meaningless to the user. A Python developer sees references to
`/tmp/jsii-kernel-xxxx/node_modules/aws-cdk-lib/...` instead of their own `my_stack.py:42`.
## Overview of the solution
We extend the jsii wire protocol with an optional `$jsii.stacktrace` field that host runtimes can attach to any request.
The kernel extracts this field and makes it available to JavaScript code (such as the CDK) via a well-known
global Symbol — no dependency between the CDK and jsii is required. The feature is opt-in, controlled by the
`JSII_HOST_STACK_TRACES=1` environment variable.
This PR implements the full pipeline for Python and the kernel-side plumbing. Java, Go, and .NET runtimes will follow in
separate PRs, and CDK integration (reading the global instead of capturing a JS stack) is a separate change
against the aws-cdk repository.
## Data flow
Python user code
│
▼
jsii Python runtime (_kernel/providers/process.py)
│ captures stack trace via traceback.extract_stack()
│ filters out jsii-internal and synthetic frames
│ attaches result as "$jsii.stacktrace" on the JSON request
▼
┌─────────── stdio ────────────┐
│ JSON request with │
│ "$jsii.stacktrace" field │
└──────────────────────────────┘
│
▼
@jsii/runtime KernelHost (host.ts)
│ extracts "$jsii.stacktrace" from the request
│ sets global[Symbol.for('jsii.context.hostStackTrace')]
│ dispatches the kernel method (e.g., create, set, invoke)
│
▼
JavaScript library code (e.g., CDK constructs)
│ reads global[Symbol.for('jsii.context.hostStackTrace')]
│ uses it in place of new Error().stack
│
▼
@jsii/runtime KernelHost (host.ts)
│ clears the global in finally block
│ writes the response
▼
┌─────────── stdio ────────────┐
│ JSON response │
└──────────────────────────────┘
## Data format
The `$jsii.stacktrace` field is an array of frames, each represented as a 4-element tuple:
```json
{
"api": "create",
"fqn": "aws-cdk-lib.aws_s3.Bucket",
"args": [
...
],
"$jsii.stacktrace": [
[
"my_app/my_stack.py",
42,
0,
"MyStack.__init__"
],
[
"app.py",
12,
0,
"<module>"
]
]
}
```
```
┌───────┬──────────┬────────┬─────────────────────────────────────────────────┐
│ Index │ Field │ Type │ Description │
├───────┼──────────┼────────┼─────────────────────────────────────────────────┤
│ 0 │ file │ string │ Source file path │
├───────┼──────────┼────────┼─────────────────────────────────────────────────┤
│ 1 │ line │ number │ 1-indexed line number │
├───────┼──────────┼────────┼─────────────────────────────────────────────────┤
│ 2 │ column │ number │ 0-indexed column number (0 when unavailable) │
├───────┼──────────┼────────┼─────────────────────────────────────────────────┤
│ 3 │ function │ string │ Qualified function name (e.g. MyStack.__init__) │
└───────┴──────────┴────────┴─────────────────────────────────────────────────┘
```
Frames are ordered most-recent-first, matching the convention of V8's Error.stack. The field is omitted entirely when
the feature is disabled, adding zero overhead to the default path.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The problem
When using the AWS CDK from a jsii-hosted language (Python, Java, Go, .NET), the CDK captures stack traces to record in construct metadata — for example, to show where a construct was instantiated or where a property was assigned. However, these stack traces are captured inside the Node.js kernel process, so they contain JavaScript frames that are meaningless to the user. A Python developer sees references to
/tmp/jsii-kernel-xxxx/node_modules/aws-cdk-lib/...instead of their ownmy_stack.py:42.Overview of the solution
We extend the jsii wire protocol with an optional
$jsii.stacktracefield that host runtimes can attach to any request. The kernel extracts this field and makes it available to JavaScript code (such as the CDK) via a well-known global Symbol — no dependency between the CDK and jsii is required. The feature is opt-in, controlled by theJSII_HOST_STACK_TRACES=1environment variable.This PR implements the full pipeline for Python and the kernel-side plumbing. Java, Go, and .NET runtimes will follow in separate PRs, and CDK integration (reading the global instead of capturing a JS stack) is a separate change against the aws-cdk repository.
Data flow
Wire format
The
$jsii.stacktracefield is an array of frames, each represented as a 4-element tuple:{ "api": "create", "fqn": "aws-cdk-lib.aws_s3.Bucket", "args": [ ... ], "$jsii.stacktrace": [ [ "my_app/my_stack.py", 42, 0, "MyStack.__init__" ], [ "app.py", 12, 0, "<module>" ] ] }Frames are ordered most-recent-first, matching the convention of V8's Error.stack. The field is omitted entirely when the feature is disabled, adding zero overhead to the default path.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.