如何在不修改类代码的情况下埋点记录类方法入参与返回值并解决栈溢出?
解决不修改原类代码记录方法入参和返回值的问题
我完全理解你的需求——要给一个无法修改的类的实例方法和静态方法添加日志埋点,记录入参和返回值,但之前的方案因为无限递归导致栈溢出了。问题的核心在于你没有缓存原始方法的引用,替换后的方法调用时又触发了自身,形成了死循环。
下面是可行的解决方案,核心思路是先缓存原始方法,再用包装函数替换目标方法,包装函数内调用原始方法而非替换后的方法,避免递归:
完整解决方案代码
class A { constructor () { this.x = 'x' } callinstance(x){ if (typeof this.x !== 'string') throw new Error('this.x MUST_BE_STRING') return x + 1 } } A.callstatic = function(x){ return x + 2 } A.a = 'a' // --- 埋点逻辑开始 --- // 处理静态方法 Object.getOwnPropertyNames(A).filter(p => typeof A[p] === 'function').forEach((prop) => { // 先缓存原始方法引用 const originalMethod = A[prop]; // 替换为包装函数 A[prop] = function(...args) { console.log(`[静态方法 ${prop}] 入参:`, args); // 调用原始方法,确保this绑定正确(静态方法的this是A类本身) const result = originalMethod.apply(this, args); console.log(`[静态方法 ${prop}] 返回值:`, result); return result; }; }); // 处理实例方法 Object.getOwnPropertyNames(A.prototype).filter(p => typeof A.prototype[p] === 'function' && p !== 'constructor').forEach((prop) => { // 缓存原始实例方法 const originalMethod = A.prototype[prop]; // 替换为包装函数 A.prototype[prop] = function(...args) { console.log(`[实例方法 ${prop}] 入参:`, args); // 调用原始方法,this绑定为当前实例对象 const result = originalMethod.apply(this, args); console.log(`[实例方法 ${prop}] 返回值:`, result); return result; }; }); // --- 埋点逻辑结束 --- // 验证 console.log(A.callstatic(1)); console.log((new A()).callinstance(11)); console.log(A.a);
运行输出
[静态方法 callstatic] 入参: [ 1 ] [静态方法 callstatic] 返回值: 3 3 [实例方法 callinstance] 入参: [ 11 ] [实例方法 callinstance] 返回值: 12 12 a
方案说明
- 缓存原始方法:在替换目标方法之前,先保存原始方法的引用,这样包装函数内调用的是未被修改的原始逻辑,不会触发递归。
- 正确绑定this:使用
apply方法确保原始方法的this上下文正确——静态方法的this指向类本身,实例方法的this指向当前实例对象,保证原方法逻辑不受影响。 - 不改动静态属性:我们只过滤并处理类上的函数属性,静态属性
A.a不会被触碰,完全保留原值。
为什么之前的方案会栈溢出?
你的instrument函数中,调用static[prop](...args)时,static[prop]已经被替换成了instrument返回的包装函数,导致每次调用都会进入新的包装函数调用,形成无限递归,最终触发栈溢出错误。而缓存原始方法后,我们直接调用未被修改的原始方法,从根本上避免了这个问题。
内容的提问来源于stack exchange,提问作者codeofnode
相关产品推荐
相关产品推荐

