In index.js, init() currently does:
const e = {}
Error.captureStackTrace(e)
const cached = { asyncId, type, stack: e.stack }
Because this runs inside the async_hooks init callback, the captured stack is dominated by the async_hooks machinery (and init() itself). In practice this is not a reliable "who blocked the process" call site; it’s at best "where the async resource was initialized" and often looks like it’s pointing at the hook rather than userland.
I’d like to propose adding a separate API to manually set the "blame" context on demand, instead of always relying on the async_hook init callback.
The idea is:
- Expose a function (e.g.
setBlockedContext() or similar) that users can call at the point in their code where they expect blocking might happen.
- When this function is called, it captures the stack at that moment and stores it.
- Later, if the event loop is detected as blocked, the stored stack is used, so the reported stack trace reflects the userland call site where
setBlockedContext() was called, not the internals of the async_hook.
This way, the tool can still use async_hooks to detect blocking, but the visible stack points to the relevant application code (the call site that opted into tracking), rather than to the async_hooks implementation itself.
Example of the current stack
at AsyncHook.init (/node_modules/blocked-at/index.js:31:11)
at TLSWrap.emitInitNative (node:internal/async_hooks:205:43)
at TLSSocket._wrapHandle (node:_tls_wrap:616:24)
at new TLSSocket (node:_tls_wrap:515:18)
at Object.connect (node:_tls_wrap:1623:19)
at Socket.<anonymous> (/node_modules/pg/lib/connection.js:94:27)
at Object.onceWrapper (node:events:510:26)
at Socket.emit (node:events:390:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
at TCP.callbackTrampoline (node:internal/async_hooks:130:17)
In
index.js,init()currently does:Because this runs inside the
async_hooks initcallback, the captured stack is dominated by theasync_hooksmachinery (andinit()itself). In practice this is not a reliable "who blocked the process" call site; it’s at best "where the async resource was initialized" and often looks like it’s pointing at the hook rather than userland.I’d like to propose adding a separate API to manually set the "blame" context on demand, instead of always relying on the
async_hook initcallback.The idea is:
setBlockedContext()or similar) that users can call at the point in their code where they expect blocking might happen.setBlockedContext()was called, not the internals of theasync_hook.This way, the tool can still use async_hooks to detect blocking, but the visible stack points to the relevant application code (the call site that opted into tracking), rather than to the
async_hooksimplementation itself.Example of the current stack