Description
Affected URL(s)
https://nodejs.org/api/async_hooks.html#async-hooks
Description of the problem
I need to track state for async contexts that I do not control.
Specifically, I am tracking the 'stack' through setTimeout
.
I have overridden setTimeout
to be (simplified):
const handler = {
apply(target: Function, self: unknown, args: unknown[]) {
const [func, ...rest] = args
const st = getStackTrace()
var wrapper = function(...args: unknown[]) {
getCallStack().unshift(st)
func.apply(this, ...args)
getCallStack().pop()
}
return Reflect.apply(target, self, [wrapper, ...rest])
}
}
setTimeout = new Proxy(setTimeout, handler)
and getCallStack is (simplified)
import { createHook, executionAsyncId } from "async_hooks"
const callStacks: Map<number, string[]> = new Map()
function init() {
const id = executionAsyncId()
callStacks.set(0, [])
function onThreadInit(id: number, {}, triggerId: number, {}) {
const parentStack = callStacks.get(triggerId)
if(!parentStack) {
callStacks.set(id, [])
} else {
callStacks.set(id, [...parentStack])
}
}
const hook = createHook({init: onThreadInit)
hook.enable()
}
init()
export function getCallStack() {
const id = executionAsyncId()
return callStacks.get(id)
}
and getStackTrace uses Error to get a stacktrace.
Within several calls deep within the setTimeout-d function, further instrumentation can access getCallStack()
to trace where the function was called from.
I do something similar with EventEmitters -- I want to know where an event was registered
The use-case for this is an instrumentation library -- I do not control any of the code other than the specific functions that I overwrite. Thus AsyncLocalStorage, which (as far as I can tell?) requires modifying the async calls, doesn't work for this use-case. Also, threads need to be able to access/get a copy of their parent's storage, which doesn't seem possible with AsyncLocalStorage either?
It is entirely possible I am missing something obvious, but so far async_hooks seems like the only way to do this.