如何获取node.js中调用者函数的文件path?

以下是三个文件的示例代码:

// foo.js var myFunc = require("./myFunc"); function foo(){ myFunc("message"); } // bar.js var myFunc = require("./myFunc"); function bar(){ myFunc("message"); } // myFunc.js module.exports = myFunc; function myFunc(arg1){ console.log(arg1); // Here I need the file path of the caller function // For example, "/path/to/foo.js" and "/path/to/bar.js" } 

我需要dynamic获取调用者函数的文件path,而不需要传递任何额外的参数给myFunc

你需要摆弄v8的内部工作。 请参阅: 有关JavaScript Stack Trace API的wiki条目 。

我在一个提议的提交中对一些代码进行了一些testing,似乎可行。 你最终的绝对path。

 // omfg.js module.exports = omfg function omfg() { var caller = getCaller() console.log(caller.filename) } // private function getCaller() { var stack = getStack() // Remove superfluous function calls on stack stack.shift() // getCaller --> getStack stack.shift() // omfg --> getCaller // Return caller's caller return stack[1].receiver } function getStack() { // Save original Error.prepareStackTrace var origPrepareStackTrace = Error.prepareStackTrace // Override with function that just returns `stack` Error.prepareStackTrace = function (_, stack) { return stack } // Create a new `Error`, which automatically gets `stack` var err = new Error() // Evaluate `err.stack`, which calls our new `Error.prepareStackTrace` var stack = err.stack // Restore original `Error.prepareStackTrace` Error.prepareStackTrace = origPrepareStackTrace // Remove superfluous function call on stack stack.shift() // getStack --> Error return stack } 

还有一个包含omfg模块的testing:

 #!/usr/bin/env node // test.js var omfg = require("./omfg") omfg() 

你将在控制台上得到test.js的绝对path。


说明

这不是一个“node.js”问题,因为它是一个“V8”的问题。

请参阅: 自定义例外的堆栈跟踪收集

Error.captureStackTrace(error, constructorOpt)error参数添加一个stack属性,默认情况下,该属性将评估为String (通过FormatStackTrace )。 如果Error.prepareStackTrace(error, structuredStackTrace)是一个Function ,那么它被调用,而不是FormatStackTrace

因此,我们可以用我们自己的函数覆盖Error.prepareStackTrace ,它将返回任何我们想要的 – 在这种情况下,只是structuredStackTrace参数。

然后, structuredStackTrace[1].receiver是一个表示调用者的对象。

或者,您可以使用module.parent.filename获取需要您模块的模块的绝对path,而不必调整V8引擎的内部工作方式。 正如这里演示的: https : //gist.github.com/capaj/a9ba9d313b79f1dcd9a2

只要记住模块被caching,所以如果有其他文件需要它并调用它,它将始终是第一个导入器的path。

我的2美分:

比方说,你有一个log对象,作为额外的信息添加到控制台的调用者的文件名,例如log响应log.info(msg) ,将做类似于:

 // my_module.js log.info('hello') $> [[my_module.js]] hello 

info将是:

 info: function(msg) { let caller = path.basename(module.parent.filename); console.log(`[[${caller}]] ${msg}`); } 

问题 :如前所述, parent.filename将首先返回你需要模块的人,而不是调用者本身。

替代方法 : 堆栈跟踪是一个模块,它将做的伎俩:

 const stackTrace = require('stack-trace'); ... info: function(msg) { let caller = path.basename(stackTrace.get()[0].getFilename()); console.log(`[[${caller}]] ${msg}`); } 

重点: stackTrace.get()[0]返回最后一个Caller响应(只是其中的一些)

  • getFileName()
  • getColumnNumber()
  • getFunctionName()
  • getLineNumber()
  • getMethodName()

您可以使用caller-callsite包:

 console.log(callerCallsite().getFileName()); 

替代品是callsitesstackman软件包。 callsites为您提供所有呼叫站点( v8术语中的“栈帧”)。 stackman给出了用自定义函数和行为装饰的调用网站。 来源的背景下,等等。 围绕呼叫站点线的代码行 也可以使用源地图。

stackman的问题是,它asynchronous返回呼叫站点。 从debugging器运行时,这不是特别有用。

以下是我使用的一些代码,您可能会发现它们很有用:

 var callsites = require('callsites'); var util = require('util'); var path = require('path'); function printStackTrace() { callsites().slice(1).forEach(function(cs) { printCallSite(cs); }); } function printCallSite(cs) { console.log(util.format('%s:%i', path.relative(process.cwd(), cs.getFileName()), cs.getLineNumber())); console.log(' getTypeName(): ' + cs.getTypeName()); console.log(' getFunctionName(): ' + cs.getFunctionName()); console.log(' getMethodName(): ' + cs.getMethodName()); // console.log(' getEvalOrigin(): ' + cs.getEvalOrigin()); // console.log(' isTopLevel(): ' + (cs.isTopLevel ? cs.isTopLevel() : null)); // console.log(' isEval(): ' + cs.isEval()); // console.log(' isNative(): ' + cs.isNative()); // console.log(' isConstructor(): ' + cs.isConstructor()); } function getCallSiteIndexes(cond) { var cond = cond || function() { return true; }; var options = arguments[1] || {}; var css = options['callsites'] || callsites().slice(1); var r = []; for (var i = 0; i < css.length; i++) { var cs = css[i]; if (cond(cs)) { if (options['first']) return i; r.push(i); } } return options['first'] ? null : r; } function getFirstCallSiteIndex(cond) { var css = callsites().slice(1); return getCallSiteIndexes(cond, {first: true, callsites: css}); } function getCallSites(cond) { var options = arguments[1] || {}; var css = options['callsites'] || callsites().slice(1); var indexes = getCallSiteIndexes(cond, Object.assign({}, {callsites: css}, options)); if (options['first']) return css[indexes]; return indexes.map(function(i) { return css[i]; }); } function getFirstCallSite(cond) { var css = callsites().slice(1); return getCallSites(cond, {first: true, callsites: css}); } fucntion f() { var firstCS = callsites()[0]; var runAsChildCSIndex = getFirstCallSiteIndex(function(cs) { return cs.getFileName() == firstCS.getFileName() && cs.getFunctionName() == 'Compiler.runAsChild'; }); if (runAsChildCSIndex) { printCallSite(callsites()[runAsChildCSIndex + 1]); } else { var compilerRunCS = getFirstCallSite(function(cs) { return cs.getFileName() == firstCS.getFileName() && cs.getFunctionName() == 'Compiler.run'; }); printCallSite(compilerRunCS); } ...