Node.js:获取调用函数的文件名

39

我想知道如何获取调用函数的绝对路径?

假设:

在文件a.js中,我调用了b()函数;b()函数定义在b.js文件中。 a.js需要b。那么,在Node中,如何从b.js获取a.js的绝对路径?


1
请参阅 https://dev59.com/JmYs5IYBdhLWcg3wAfEi。 - Myrne Stol
请参考以下链接:https://dev59.com/l2kw5IYBdhLWcg3wJHGF - basilikum
请查看我的答案这里 - x-yuri
console.log((new Error()).stack.split("\n")[1].split("/").slice(-1)[0].split(":")[0]) // "caller_file_name.js" - marcelosalloum
8个回答

51

无法恢复prepareStackTrace函数可能会导致问题。以下是一个消除副作用的示例。

function _getCallerFile() {
    var originalFunc = Error.prepareStackTrace;

    var callerfile;
    try {
        var err = new Error();
        var currentfile;

        Error.prepareStackTrace = function (err, stack) { return stack; };

        currentfile = err.stack.shift().getFileName();

        while (err.stack.length) {
            callerfile = err.stack.shift().getFileName();

            if(currentfile !== callerfile) break;
        }
    } catch (e) {}

    Error.prepareStackTrace = originalFunc; 

    return callerfile;
}

太好了!我试了好几个小时才解决与那个相关的问题。重新分配原始函数修复了一切。 - Marcelo Lazaroni
1
嗨,这个解决方案很好用...但如果在回调函数中调用了一个函数,则它将获取触发回调的文件的名称。 - Mohamed Mahmoud
使用finally重置Error.prepareStackTrace会更安全,不是吗? - Christoph Thiede
使用打包工具时会失败,而且大部分与早期答案重复。 - brillout

34

这是一个示例,展示如何使用 stacktrace 在 Node 中查找调用者文件

function _getCallerFile() {
    var filename;

    var _pst = Error.prepareStackTrace
    Error.prepareStackTrace = function (err, stack) { return stack; };
    try {
        var err = new Error();
        var callerfile;
        var currentfile;

        currentfile = err.stack.shift().getFileName();

        while (err.stack.length) {
            callerfile = err.stack.shift().getFileName();

            if(currentfile !== callerfile) {
                filename = callerfile;
                break;
            }
        }
    } catch (err) {}
    Error.prepareStackTrace = _pst;

    return filename;
}

5
重要!向下滚动一点,有更新的答案,与此相比不会有相同的潜在漏洞和问题(虽然我进行了编辑以省略最严重的漏洞)。@Christoph 的回答似乎是最好的。 - coolaj86
使用打包工具时,这会失败。 - brillout
@brillout 你至少需要提到是哪一个 - undefined

9
并非直接回答问题,但有些人可能会喜欢这个信息。使用NodeJS和Forever(-monitor),以下内容包含了启动进程的文件名:
process.mainModule.filename

虽然没有尝试过太多使用方法™。

这似乎是一个相当不错的解释:https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi


1
肯定没有回答这个问题,但这对我有帮助。谢谢! - shriek

7

如果您在实现函数的文件中未调用需要调用者文件的函数(如OP方案中的情况),您可以只需编写:

function _getCallerFile()
{
    const prepareStackTraceOrg = Error.prepareStackTrace;
    const err = new Error();

    Error.prepareStackTrace = (_, stack) => stack;

    const stack = err.stack;

    Error.prepareStackTrace = prepareStackTraceOrg;

    return stack[1].getFileName();
}

try...catch 是不必要的,因为如果将 Error 赋值给变量,它就不会抛出异常。

此外,如果想在多个项目中使用 _getCallerFile,可以将其单独放在一个文件中,但这样你将得到调用 _getCallerFile 的那个文件的名称。在这种情况下,只需写成 return stack[2].getFileName(); 即可,也就是在调用栈中再往上一层。

如果你正在使用 TypeScript,则必须编写 const stack = err.stack as unknown as NodeJS.CallSite[];,因为 Error.stack 的声明类型是 string,而我们的 prepareStackTrace 函数返回的是一个由 NodeJS.CallSite 对象组成的数组。

另外需要注意的是:NodeJS.CallSite 还有其他有趣的方法,例如 getFunctionName

更新

我注意到在分配 lambda 前 Error.prepareStackTrace === undefined。如果你不相信我,只需将 console.log('prepareStackTraceOrg:', prepareStackTraceOrg); 添加到函数中即可。 因此,我们可以简化该函数:

function _getCallerFile()
{
    const err = new Error();

    Error.prepareStackTrace = (_, stack) => stack;

    const stack = err.stack;

    Error.prepareStackTrace = undefined;

    return stack[1].getFileName();
}

4

3

npm包caller package中有一个函数,返回调用者的路径和文件名。


调用者包在调试时返回内部/module.js。 - ucipass
2
在github上发布的示例在node v8.11.1上也返回相同的"internal/module.js"作为调用者。 - ucipass

1
在JavaScript中获取堆栈跟踪非常困难。我发现最好的方法是抛出一个错误,捕获它,从Error.getStack()(并非所有浏览器都实现了,这意味着您的IE可能不支持)中获取堆栈,并格式化输出。
每个堆栈帧都提供了文件路径、行号和函数名。Webkit甚至支持参数,但我上次检查时还没有起作用。
然后就有了跨不同事件跟踪代码的问题。
我实际上写了一篇关于此的博客文章:http://fritsvancampen.wordpress.com/2013/03/28/error-handling-in-javascript-a-better-way/

尝试 { throw new Error(); } 捕获(e) { console.log(JSON.stringify(e)); } 在nodejs上无法工作。 - einstein
你不能将本地对象进行JSON编码。为什么你会想要这样做呢?:S - Halcyon

-1
您可以使用 require.resolve(module) 来确定模块的完整路径:
var path = require.resolve("a");

//or

var path = require.resolve("./a.js");

调用者并不总是预先确定的,因此:这种方式行不通。 - Tim Long
Resolve 用于合并路径或遍历路径。查找调用者的路径是不同的。 - Tarun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接