如何获取JavaScript调用函数的行号和调用源URL

133

我正在使用以下代码获取JavaScript调用函数的名称:

var callerFunc = arguments.callee.caller.toString();
callerFuncName = (callerFunc.substring(callerFunc.indexOf("function") + 8, callerFunc.indexOf("(")) || "anoynmous")

有没有一种方法可以发现调用方法的行号?

此外,是否有办法获取调用方法的JavaScript文件名称?或源URL?


2
我不认为在IE中这是可能的,否则我们就会有一种方法来绕过那些没有提供任何详细信息的糟糕错误消息。但如果可能的话,我也非常愿意知道! - Zoidberg
是的。这里有一个跨浏览器函数,利用了每个浏览器的专有方法:http://github.com/eriwen/javascript-stacktrace [已修复链接] - scotts
17个回答

117

在Chrome/QtWebView中对我有效

function getErrorObject(){
    try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();
var caller_line = err.stack.split("\n")[4];
var index = caller_line.indexOf("at ");
var clean = caller_line.slice(index+2, caller_line.length);

72
无需抛出错误,只需要创建即可:var caller_line = (new Error).stack.split("\n")[4] - ELLIOTTCABLE
2
将此建议与另一个类似的答案合并,以获得FF / Webkit“标准化”的响应--请参见https://dev59.com/qGYr5IYBdhLWcg3wQYJa#14841411 - drzaus
1
在PhantomJS中也可以工作,但你必须抛出它,否则错误的“stack”属性不会被设置。 - Joshua Richardson
@ELLIOTTCABLE - 你的解决方案非常优雅,但在IE中无法工作。在我看来,这应该是被接受的答案。 - mpd
2
@ELLIOTTCABLE 实际上在一些浏览器中,比如iOS Safari,你确实需要抛出异常!那么为什么不这样做呢? - Arctelix
显示剩余3条评论

29
如果您需要在Firefox或Opera中访问某个东西的行号,只需访问

标签即可。

(new Error).lineNumber

注意:这在Chrome/Safari上不起作用。

11
你好,感谢你的附加组件。 你知道是否可以从之前的调用中获取行号吗? 假设方法A调用B,现在我想知道在A下的哪一行进行了调用? - Tal
89
这是一个选择框,但并未回答问题,即如何获取调用函数的行号。 - mikemaccana
3
此处的功能非常有限。最佳解决方案是抛出一个错误,然后在错误堆栈(error.stack)上使用正则表达式,这在所有现代浏览器中都可用。您可以轻松地提取路径、文件、行号和列号。毫无问题。 - Arctelix

20

我很惊讶大部分回答都默认你要处理错误,而不是在正常情况下输出有用的调试追踪信息。

比如,我喜欢使用这样的 console.log 包装器:

consoleLog = function(msg) {//See https://dev59.com/uXE95IYBdhLWcg3wXcrd#27074218
    var e = new Error();
    if (!e.stack) {
        try {
            // IE requires the Error to actually be thrown or else the 
            // Error's 'stack' property is undefined.
            throw e;
        } catch (e) {
            if (!e.stack) {
                //return 0; // IE < 10, likely
            }
        }
    }
    var stack = e.stack.toString().split(/\r\n|\n/);
    if (msg === '') {
        msg = '""';
    }
    console.log(msg, '          [' + stack[1] + ']');        
}

这最终会在我的控制台上打印出以下内容:

1462567104174 [getAllPosts@http://me.com/helper.js:362:9]

请参见https://dev59.com/uXE95IYBdhLWcg3wXcrd#27074218/以及A proper wrapper for console.log with correct line number?


1
适用于Firefox浏览器,但不适用于Node.js。 - zipper
1
在节点下,您需要记录 stack[2]。 - monika mevenkamp
1
有人测试过这段代码是否有效吗?如果条件if (!e.stack)是用来做什么的?看起来这段代码上有Python程序员的痕迹;-) - puk
@puk 我从来不喜欢Python,但正如我在上面的代码注释中提到的那样,我是从https://dev59.com/uXE95IYBdhLWcg3wXcrd#27074218获取的。我不能说那个人是否喜欢Python。但是,这段代码在2016年对我有效。虽然多年来我没有使用过它。https://dev59.com/-HM_5IYBdhLWcg3whznx#55392553看起来很有趣。 - Ryan
1
这个不起作用,在现代浏览器更新中,没有办法管理或读取自动生成的控制台日志,因为浏览器使用另一个内置函数来写入控制台,如果您覆盖控制台函数,它只会影响您对控制台的调用,而不是浏览器的调用。 - Eyni Kave
显示剩余3条评论

12
我知道这是一个老问题,但现在有一种方法叫做
console.trace("Message");

该方法可以展示记录的行数和调用链,以及您传递的信息。关于JavaScript日志技巧的更多信息,请参阅freecodecamp这里这篇博客文章。


问题在于console.trace()没有返回任何内容。 - PinkTurtle

5

通常通过在当前上下文中抛出错误来实现;然后分析错误对象的属性,例如lineNumberfileName(某些浏览器有)

function getErrorObject(){
  try { throw Error('') } catch(err) { return err; }
}

var err = getErrorObject();

err.fileName;
err.lineNumber; // or `err.line` in WebKit

请注意,callee.caller属性已经被废弃(并且在ECMA第三版中从未真正存在过)。

另外,请记住函数反编译被指定为实现相关的,因此可能会产生非常意想不到的结果。我在这里这里写过相关内容。


谢谢, 在我需要的应用程序中添加这段代码似乎有些问题。(一些js追踪框架) 你知道有没有其他不被弃用的方法可以使用? - Tal
你应该能够检查错误对象,而不依赖于已弃用的 callee.caller - kangax
你不需要抛出错误,只需使用 (new Error).lineNumber 来访问脚本中的当前行号。 - Eli Grey
只要考虑到某些客户端可能不存在 callee.caller(由于其非标准性),您就可以使用它。出于调试目的,使用函数反编译是可以的,但是在其上构建实际应用程序(并且没有针对缺少它的客户端的备用方案)是不可取的。 - kangax
你真的需要抛出错误吗?难道你不能只是创建它吗? - user663031
显示剩余4条评论

4

看起来我有点晚了:), 但这个讨论非常有趣,所以...这就是我的翻译...假设你想构建一个错误处理程序,并且你正在使用自己的异常处理程序类,比如:

function  errorHandler(error){
    this.errorMessage = error;
}
errorHandler.prototype. displayErrors = function(){
    throw new Error(this.errorMessage);
}

你正在这样包装你的代码:

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message');
}
}catch(e){
    e.displayErrors();
}

很可能你会把错误处理程序放在一个单独的.js文件中。

你会注意到,在Firefox或Chrome的错误控制台中,显示的代码行号(和文件名)是抛出“Error”异常的行(文件),而不是你真正想要的“errorHandler”异常,以便进行轻松调试。抛出自己的异常很好,但在大型项目中定位它们可能会成为一个问题,特别是如果它们具有相似的消息。因此,你可以将对一个实际空错误对象的引用传递给你的错误处理程序,该引用将保存你想要的所有信息(例如在Firefox中,你可以获取文件名和行号等;在Chrome中,如果读取Error实例的'stack'属性,则会得到类似的东西)。简而言之,你可以这样做:

function  errorHandler(error, errorInstance){
    this.errorMessage = error;
    this. errorInstance = errorInstance;
}
errorHandler.prototype. displayErrors = function(){
    //add the empty error trace to your message
    this.errorMessage += '  stack trace: '+ this. errorInstance.stack;
    throw new Error(this.errorMessage);
}

try{
if(condition){
    //whatever...
}else{
    throw new errorHandler('Some Error Message', new Error());
}
}catch(e){
    e.displayErrors();
}

现在你可以获取抛出自定义异常的实际文件和行号。


4

行号实际上是静态的东西,因此如果您只想将其用于日志记录,则可以使用类似于gulp的预处理方式进行处理。我编写了一个小型的gulp插件,正是这样做的:

var gulp = require('gulp');
var logLine = require('gulp-log-line');
gulp.task('log-line', function() {
    return gulp.src("file.js", {buffer : true})
    //Write here the loggers you use.
        .pipe(logLine(['console.log']))
        .pipe(gulp.dest('./build'))

})

gulp.task('default', ['log-line'])

这将会把文件名和行号附加到所有来自console.log的日志中,所以console.log(something) 将变成 console.log('filePath:fileNumber', something)。优点是现在你可以连接你的文件,转换它们... 然后你仍然能够获得行号。

这似乎是一个很好的建议,适用于使用转译器的情况(例如,在使用TypeScript时)。谢谢! - Andy King

4
这是我的做法,我在Firefox和Chrome中进行了测试。这使得可以检查调用函数的文件名和行号。
logFileAndLineNumber(new Error());

function logFileAndLineNumber(newErr)
{
   if(navigator.userAgent.indexOf("Firefox") != -1)
   {
      var originPath = newErr.stack.split('\n')[0].split("/");
      var fileNameAndLineNumber = originPath[originPath.length - 1].split(">")[0];
      console.log(fileNameAndLineNumber);
   }else if(navigator.userAgent.indexOf("Chrome") != -1)
   {
      var originFile = newErr.stack.split('\n')[1].split('/');
      var fileName = originFile[originFile.length - 1].split(':')[0];
      var lineNumber = originFile[originFile.length - 1].split(':')[1];
      console.log(fileName+" line "+lineNumber);
    }
}

3
如果您想为了调试目的或仅在开发期间(由于某种原因)知道代码行号,可以使用Firefox扩展Firebugthrow异常。 编辑:
如果您真的有某些原因需要在生产中这样做,那么可以对Javascript文件进行预处理,以便每个函数都可以跟踪其所在的行。 我知道一些框架用于找到代码覆盖率(如JSCoverage)。
例如,假设您最初的调用是:
function x() {
  1 + 1;
  2 + 2;
  y();
}

您可以编写一个预处理器将其转换为以下内容:
function x() {
  var me = arguments.callee;
  me.line = 1;
  1 + 1;
  me.line = 2;
  2 + 2;
  me.line = 3;
  y();
}

y()函数中,你可以使用arguments.callee.caller.line来知道它被调用的行号,例如:

function y() {
  alert(arguments.callee.caller.line);
}

1
谢谢, 出于支持原因,我想在生产环境中使用这个。我找到了一些代码,可以让你看到所有流程直到方法的调用堆栈,但它没有显示方法被调用的行号。 我猜这有一个简单的解决方案? - Tal

3
如果您需要非常完整和准确的内容,对于v8(指NodeJS和Chrome),有一个名为stack-trace的包,对我很有效。

https://www.npmjs.com/package/stack-trace

这有几个优点:能够生成深度堆栈跟踪,能够遍历异步调用,并在每个堆栈帧中提供脚本的完整 URL。
您可以在 此处 找到有关 v8 堆栈跟踪 API 的更多信息。 如果您需要在更多浏览器中使用,请使用此软件包:

https://www.npmjs.com/package/stacktrace-js

这个库解析格式化的堆栈跟踪字符串,通常在开发工具控制台中看到,这意味着它只能访问实际出现在格式化堆栈跟踪中的信息 - 不像v8 API,您无法获取(除其他外)堆栈帧中脚本的完整URL。
请注意,解析格式化的堆栈跟踪是一种相当脆弱的方法,因为此格式可能会在浏览器中更改,这将破坏您的应用程序。
据我所知,没有更安全的跨浏览器选择 - 如果您需要快速,准确且完整的东西,可以在所有浏览器中使用,那么您基本上就没什么运气了。

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