Skip to content

Commit 93e480d

Browse files
authored
meta: Improve README.md, package.json metadata and add LICENSE (#10)
1 parent aa10cfb commit 93e480d

File tree

4 files changed

+136
-36
lines changed

4 files changed

+136
-36
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Changelog
2+
3+
## 0.1.1
4+
5+
- meta: Improve `README.md`, `package.json` metadata and add `LICENSE` (#10)
6+
17
## 0.1.0
28

39
Initial release

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Functional Software, Inc. dba Sentry
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6+
this software and associated documentation files (the "Software"), to deal in
7+
the Software without restriction, including without limitation the rights to
8+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9+
of the Software, and to permit persons to whom the Software is furnished to do
10+
so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 92 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,40 @@
11
# `@sentry-internal/node-native-stacktrace`
22

3-
Native Node module to capture stack traces from all registered threads.
3+
A native Node.js module that can capture JavaScript stack traces for registered
4+
main or worker threads from any other thread, even if event loops are blocked.
45

5-
This allows capturing main and worker thread stack traces from another watchdog
6-
thread, even if the event loops are blocked.
6+
The module also provides a means to create a watchdog system to track event loop
7+
blocking via periodic heartbeats. When the time from the last heartbeat crosses
8+
a threshold, JavaScript stack traces can be captured. The heartbeats can
9+
optionally include state information which is included with the corresponding
10+
stack trace.
711

8-
In the main or worker threads:
12+
## Basic Usage
13+
14+
### 1. Register threads you want to monitor
15+
16+
In your main thread or worker threads:
917

1018
```ts
11-
const { registerThread } = require("@sentry-internal/node-native-stacktrace");
19+
import { registerThread } from "@sentry-internal/node-native-stacktrace";
1220

21+
// Register this thread for monitoring
1322
registerThread();
1423
```
1524

16-
Watchdog thread:
25+
### 2. Capture stack traces from any thread
1726

1827
```ts
19-
const {
20-
captureStackTrace,
21-
} = require("@sentry-internal/node-native-stacktrace");
28+
import { captureStackTrace } from "@sentry-internal/node-native-stacktrace";
2229

30+
// Capture stack traces from all registered threads
2331
const stacks = captureStackTrace();
2432
console.log(stacks);
2533
```
2634

27-
Results in:
35+
### Example Output
36+
37+
Stack traces show where each thread is currently executing:
2838

2939
```js
3040
{
@@ -83,50 +93,99 @@ Results in:
8393
}
8494
```
8595

86-
## Detecting blocked event loops
96+
## Advanced Usage: Automatic blocked event loop Detection
97+
98+
Set up automatic detection of blocked event loops:
99+
100+
### 1. Set up thread heartbeats
87101

88-
In the main or worker threads if you call `registerThread()` regularly, times
89-
are recorded.
102+
Send regular heartbeats with optional state information:
90103

91104
```ts
92-
const {
105+
import {
93106
registerThread,
94107
threadPoll,
95-
} = require("@sentry-internal/node-native-stacktrace");
108+
} from "@sentry-internal/node-native-stacktrace";
96109

110+
// Register this thread
97111
registerThread();
98112

113+
// Send heartbeats every 200ms with optional state
99114
setInterval(() => {
100-
threadPoll({ optional_state: "some_value" });
115+
threadPoll({
116+
endpoint: "/api/current-request",
117+
userId: getCurrentUserId(),
118+
});
101119
}, 200);
102120
```
103121

104-
In the watchdog thread you can call `getThreadsLastSeen()` to get how long it's
105-
been in milliseconds since each thread polled.
122+
### 2. Monitor from a watchdog thread
106123

107-
If any thread has exceeded a threshold, you can call `captureStackTrace()` to
108-
get the stack traces for all threads.
124+
Monitor all registered threads from a dedicated thread:
109125

110126
```ts
111-
const {
127+
import {
112128
captureStackTrace,
113129
getThreadsLastSeen,
114-
} = require("@sentry-internal/node-native-stacktrace");
130+
} from "@sentry-internal/node-native-stacktrace";
115131

116132
const THRESHOLD = 1000; // 1 second
117133

118134
setInterval(() => {
119-
for (const [thread, time] in Object.entries(getThreadsLastSeen())) {
120-
if (time > THRESHOLD) {
121-
const threads = captureStackTrace();
122-
const blockedThread = threads[thread];
123-
const { frames, state } = blockedThread;
124-
console.log(
125-
`Thread '${thread}' blocked more than ${THRESHOLD}ms`,
126-
frames,
127-
state,
128-
);
135+
const threadsLastSeen = getThreadsLastSeen();
136+
137+
for (const [threadId, timeSinceLastSeen] of Object.entries(threadsLastSeen)) {
138+
if (timeSinceLastSeen > THRESHOLD) {
139+
// Thread appears to be blocked - capture diagnostics
140+
const stackTraces = captureStackTrace();
141+
const blockedThread = stackTraces[threadId];
142+
143+
console.error(`🚨 Thread ${threadId} blocked for ${timeSinceLastSeen}ms`);
144+
console.error("Stack trace:", blockedThread.frames);
145+
console.error("Last known state:", blockedThread.state);
129146
}
130147
}
131-
}, 1000);
148+
}, 500); // Check every 500ms
149+
```
150+
151+
## API Reference
152+
153+
### Functions
154+
155+
#### `registerThread(threadName?: string): void`
156+
157+
Registers the current thread for monitoring. Must be called from each thread you
158+
want to capture stack traces from.
159+
160+
- `threadName` (optional): Name for the thread. Defaults to the current thread
161+
ID.
162+
163+
#### `captureStackTrace<State>(): Record<string, Thread<State>>`
164+
165+
Captures stack traces from all registered threads. Can be called from any thread
166+
but will not capture the stack trace of the calling thread itself.
167+
168+
```ts
169+
type Thread<S> = {
170+
frames: StackFrame[];
171+
state?: S;
172+
};
173+
174+
type StackFrame = {
175+
function: string;
176+
filename: string;
177+
lineno: number;
178+
colno: number;
179+
};
132180
```
181+
182+
#### `threadPoll<State>(state?: State): void`
183+
184+
Sends a heartbeat from the current thread with optional state information. The
185+
state object will be serialized and included as a JavaScript object with the
186+
corresponding stack trace.
187+
188+
#### `getThreadsLastSeen(): Record<string, number>`
189+
190+
Returns the time in milliseconds since each registered thread called
191+
`threadPoll()`.

package.json

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@
33
"version": "0.1.0",
44
"main": "lib/index.js",
55
"types": "lib/index.d.ts",
6+
"repository": "git://github.com/getsentry/sentry-javascript-node-native-stacktrace.git",
7+
"homepage": "https://github.com/getsentry/sentry-javascript-node-native-stacktrace",
8+
"author": "Sentry",
69
"license": "MIT",
10+
"description": "A native Node.js module that can capture JavaScript stack traces from main and worker threads, even with blocked event loops.",
11+
"keywords": [
12+
"stacktrace",
13+
"native",
14+
"nodejs",
15+
"worker",
16+
"sentry"
17+
],
718
"scripts": {
819
"install": "node scripts/check-build.mjs",
920
"lint": "yarn lint:eslint && yarn lint:clang",
@@ -23,8 +34,8 @@
2334
"clean": "node-gyp clean && rm -rf lib && rm -rf build",
2435
"test": "node ./test/prepare.mjs && vitest run --silent=false --disable-console-intercept"
2536
},
26-
"volta": {
27-
"node": "24.1.0"
37+
"engines": {
38+
"node": ">=18"
2839
},
2940
"dependencies": {
3041
"detect-libc": "^2.0.4",
@@ -51,5 +62,8 @@
5162
],
5263
"publishConfig": {
5364
"access": "public"
65+
},
66+
"volta": {
67+
"node": "24.1.0"
5468
}
55-
}
69+
}

0 commit comments

Comments
 (0)