如何获取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());
替代品是callsites
和stackman
软件包。 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); } ...