抛出异常时如何获得JavaScript堆栈跟踪?

How can I get a JavaScript stack trace when I throw an exception?

如果我自己抛出JavaScript异常(例如,throw"AArrggg"),我如何获得堆栈跟踪(在Firebug中或其他方式)? 现在我收到消息。

编辑:正如下面的许多人发布的那样,有可能获得JavaScript异常的堆栈跟踪,但我想获得异常的堆栈跟踪。 例如:

1
2
3
4
5
6
7
8
function foo() {
    bar(2);
}
function bar(n) {
    if (n < 2)
        throw"Oh no! 'n' is too small!"
    bar(n-1);
}

当调用foo时,我想获得一个堆栈跟踪,其中包括对foobarbar的调用。


编辑2(2017):

在所有现代浏览器中,您只需调用:console.trace();(MDN参考)

编辑1(2013):

在原始问题的注释中指出的更好(和更简单)的解决方案是使用Error对象的stack属性,如下所示:

1
2
3
4
function stackTrace() {
    var err = new Error();
    return err.stack;
}

这将生成如下输出:

1
2
3
4
5
6
7
DBX.Utils.stackTrace@http://localhost:49573/assets/js/scripts.js:44
DBX.Console.Debug@http://localhost:49573/assets/js/scripts.js:9
.success@http://localhost:49573/:462
x.Callbacks/c@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/p.fireWith@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
k@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/r@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6

给出调用函数的名称以及URL,调用函数等。

原创(2009年):

此代码段的修改版本可能会有所帮助:

1
2
3
4
5
6
7
function stacktrace() {
  function st2(f) {
    return !f ? [] :
        st2(f.caller).concat([f.toString().split('(')[0].substring(9) + '(' + f.arguments.join(',') + ')']);
  }
  return st2(arguments.callee.caller);
}


请注意,chrome / chrome(使用V8的其他浏览器)以及Firefox都有一个方便的界面来通过Error对象上的堆栈属性获取堆栈跟踪。

1
2
3
4
5
try {
   // Code throwing an exception
} catch(e) {
  console.log(e.stack);
}

它适用于基本异常以及您自己抛出的异常。 (认为??你使用的是Error类,这无论如何都是一种很好的做法)。

查看有关V8文档的详细信息


在Firefox中,您似乎不需要抛出异常。这足够了

1
2
e = new Error();
console.log(e.stack);


如果您有firebug,则脚本选项卡中的所有错误选项都会中断。一旦脚本达到你的断点,你可以看看firebug的堆栈窗口:

screenshot


原始问题的注释中指出的一个好的(简单的)解决方案是使用Error对象的stack属性,如下所示:

1
2
3
4
function stackTrace() {
    var err = new Error();
    return err.stack;
}

这将生成如下输出:

1
2
3
4
5
6
7
DBX.Utils.stackTrace@http://localhost:49573/assets/js/scripts.js:44
DBX.Console.Debug@http://localhost:49573/assets/js/scripts.js:9
.success@http://localhost:49573/:462
x.Callbacks/c@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/p.fireWith@http://localhost:49573/assets/js/jquery-1.10.2.min.js:4
k@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/r@http://localhost:49573/assets/js/jquery-1.10.2.min.js:6

给出调用函数的名称以及URL和行号,调用函数等。

我有一个非常精细和漂亮的解决方案,我为我正在进行的项目设计的,我已经提取并重新设计了一些以便进行概括。这里是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
(function(context){
    // Only global namespace.
    var Console = {
        //Settings
        settings: {
            debug: {
                alwaysShowURL: false,
                enabled: true,
                showInfo: true
            },
            stackTrace: {
                enabled: true,
                collapsed: true,
                ignoreDebugFuncs: true,
                spacing: false
            }
        }
    };

    // String formatting prototype function.
    if (!String.prototype.format) {
        String.prototype.format = function () {
            var s = this.toString(),
                args = typeof arguments[0],
                args = (("string" == args ||"number" == args) ? arguments : arguments[0]);
            if (!arguments.length)
                return s;
            for (arg in args)
                s = s.replace(RegExp("\\{" + arg +"\\}","gi"), args[arg]);
            return s;
        }
    }

    // String repeating prototype function.
    if (!String.prototype.times) {
        String.prototype.times = function () {
            var s = this.toString(),
                tempStr ="",
                times = arguments[0];
            if (!arguments.length)
                return s;
            for (var i = 0; i < times; i++)
                tempStr += s;
            return tempStr;
        }
    }

    // Commonly used functions
    Console.debug = function () {
        if (Console.settings.debug.enabled) {
            var args = ((typeof arguments !== 'undefined') ? Array.prototype.slice.call(arguments, 0) : []),
                sUA = navigator.userAgent,
                currentBrowser = {
                    firefox: /firefox/gi.test(sUA),
                    webkit: /webkit/gi.test(sUA),
                },
                aLines = Console.stackTrace().split("
"
),
                aCurrentLine,
                iCurrIndex = ((currentBrowser.webkit) ? 3 : 2),
                sCssBlack ="color:black;",
                sCssFormat ="color:{0}; font-weight:bold;",
                sLines ="";

            if (currentBrowser.firefox)
                aCurrentLine = aLines[iCurrIndex].replace(/(.*):/,"$1@").split("@");
            else if (currentBrowser.webkit)
                aCurrentLine = aLines[iCurrIndex].replace("at","").replace(")","").replace(/( \()/gi,"@").replace(/(.*):(\d*):(\d*)/,"$1@$2@$3").split("@");

            // Show info if the setting is true and there's no extra trace (would be kind of pointless).
            if (Console.settings.debug.showInfo && !Console.settings.stackTrace.enabled) {
                var sFunc = aCurrentLine[0].trim(),
                    sURL = aCurrentLine[1].trim(),
                    sURL = ((!Console.settings.debug.alwaysShowURL && context.location.href == sURL) ?"this page" : sURL),
                    sLine = aCurrentLine[2].trim(),
                    sCol;

                if (currentBrowser.webkit)
                    sCol = aCurrentLine[3].trim();

                console.info("%cOn line %c{0}%c{1}%c{2}%c of %c{3}%c inside the %c{4}%c function:".format(sLine, ((currentBrowser.webkit) ?", column" :""), ((currentBrowser.webkit) ? sCol :""), sURL, sFunc),
                             sCssBlack, sCssFormat.format("red"),
                             sCssBlack, sCssFormat.format("purple"),
                             sCssBlack, sCssFormat.format("green"),
                             sCssBlack, sCssFormat.format("blue"),
                             sCssBlack);
            }

            // If the setting permits, get rid of the two obvious debug functions (Console.debug and Console.stackTrace).
            if (Console.settings.stackTrace.ignoreDebugFuncs) {
                // In WebKit (Chrome at least), there's an extra line at the top that says"Error" so adjust for this.
                if (currentBrowser.webkit)
                    aLines.shift();
                aLines.shift();
                aLines.shift();
            }

            sLines = aLines.join(((Console.settings.stackTrace.spacing) ?"

"
:"
"
)).trim();

            trace = typeof trace !== 'undefined' ? trace : true;
            if (typeof console !=="undefined") {
                for (var arg in args)
                    console.debug(args[arg]);

                if (Console.settings.stackTrace.enabled) {
                    var sCss ="color:red; font-weight: bold;",
                        sTitle ="%c Stack Trace" +"".times(70);

                    if (Console.settings.stackTrace.collapsed)
                        console.groupCollapsed(sTitle, sCss);
                    else
                        console.group(sTitle, sCss);

                    console.debug("%c" + sLines,"color: #666666; font-style: italic;");

                    console.groupEnd();
                }
            }
        }
    }
    Console.stackTrace = function () {
        var err = new Error();
        return err.stack;
    }

    context.Console = Console;
})(window);

在GitHub上查看(目前是v1.2)!您可以像Console.debug("Whatever");一样使用它,它将根据Console中的设置打印输出和堆栈跟踪(或者只是简单的信息/没有任何额外的信息)。这是一个例子:

Console.js

确保使用Console对象中的设置!您可以在轨迹线之间添加间距并将其完全关闭。这里Console.trace设置为false

No trace

您甚至可以关闭显示的第一位信息(将Console.settings.debug.showInfo设置为false)或完全禁用调试(将Console.settings.debug.enabled设置为false),这样您就不必再次注释掉调试语句!让他们进去,这将无能为力。


我不认为你可以使用任何内置的东西,但我确实找到了很多人自己动手的例子。

  • DIY javascript堆栈跟踪
  • 任何浏览器中的Javascript堆栈跟踪


使用Chrome浏览器,您可以使用console.trace方法:https://developer.chrome.com/devtools/docs/console-api#consoletraceobject


您可以访问Error实例的stack(在Opera中的stacktrace)属性,即使您将其抛出也是如此。问题是,您需要确保使用throw new Error(string)(不要忘记新的而不是throw string

例:

1
2
3
4
5
try {
    0++;
} catch (e) {
    var myStackTrace = e.stack || e.stacktrace ||"";
}


在Firebug上获得真正的堆栈跟踪的一种方法是创建一个真正的错误,比如调用一个未定义的函数:

1
2
3
4
5
6
7
8
9
10
11
12
function foo(b){
  if (typeof b !== 'string'){
    // undefined Error type to get the call stack
    throw new ChuckNorrisError("Chuck Norris catches you.");
  }
}

function bar(a){
  foo(a);
}

foo(123);

或者使用console.error()后跟throw语句,因为console.error()显示堆栈跟踪。


更新Eugene的答案:必须抛出错误对象,以便IE(特定版本?)填充stack属性。以下应该比他当前的示例更好,并且应避免在IE中返回undefined

1
2
3
4
5
6
7
8
function stackTrace() {
  try {
    var err = new Error();
    throw err;
  } catch (err) {
    return err.stack;
  }
}

注1:这种事情只应在调试时完成,并在实时时禁用,特别是在频繁调用时。注意2:这可能不适用于所有浏览器,但似乎适用于FF和IE 11,这很适合我的需要。


这将为现代Chrome,Opera,Firefox和IE10 +提供堆栈跟踪(作为字符串数组)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function getStackTrace () {

  var stack;

  try {
    throw new Error('');
  }
  catch (error) {
    stack = error.stack || '';
  }

  stack = stack.split('
'
).map(function (line) { return line.trim(); });
  return stack.splice(stack[0] == 'Error' ? 2 : 1);
}

用法:

1
2
console.log(getStackTrace().join('
'
));

它从堆栈中排除了自己的调用以及Chrome和Firefox(但不是IE)使用的标题"错误"。

它不应该在旧浏览器上崩溃,而只是返回空数组。如果您需要更多通用解决方案,请查看stacktrace.js。它支持的浏览器列表确实令人印象深刻,但在我看来,这对于它的小任务非常重要:37Kb的缩小文本,包括所有依赖项。


在谷歌浏览器(版本19.0及更高版本)中,简单地抛出异常非常有效。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* file: code.js, line numbers shown */

188: function fa() {
189:    console.log('executing fa...');
190:    fb();
191: }
192:
193: function fb() {
194:    console.log('executing fb...');
195:    fc()
196: }
197:
198: function fc() {
199:    console.log('executing fc...');
200:    throw 'error in fc...'
201: }
202:
203: fa();

将在浏览器的控制台输出中显示堆栈跟踪:

1
2
3
4
5
6
7
8
9
executing fa...                         code.js:189
executing fb...                         code.js:194
executing fc...                         cdoe.js:199
/* this is your stack trace */
Uncaught error in fc...                 code.js:200
    fc                                  code.js:200
    fb                                  code.js:195
    fa                                  code.js:190
    (anonymous function)                code.js:203

希望这有帮助。


功能:

1
2
3
4
function print_call_stack(err) {
    var stack = err.stack;
    console.error(stack);
}

用例:

1
2
3
4
5
6
     try{
         aaa.bbb;//error throw here
     }
     catch (err){
         print_call_stack(err);
     }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<script type="text/javascript"
src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js">
<script type="text/javascript">
    try {
        // error producing code
    } catch(e) {
        var trace = printStackTrace({e: e});
        alert('Error!
'
+ 'Message: ' + e.message + '
Stack trace:
'
+ trace.join('
'
));
        // do something else with error
    }

此脚本将显示错误


这个polyfill代码在现代(2017)浏览器(IE11,Opera,Chrome,FireFox,Yandex)中工作:

1
2
3
4
5
printStackTrace: function () {
    var err = new Error();
    var stack = err.stack || /*old opera*/ err.stacktrace || ( /*IE11*/ console.trace ? console.trace() :"no stack info");
    return stack;
}

其他答案:

1
2
3
4
function stackTrace() {
  var err = new Error();
  return err.stack;
}

不在IE 11中工作!

使用arguments.callee.caller - 在任何浏览器中都不能在严格模式下工作!


派对迟到了,但是,这是另一个解决方案,如果arguments.callee可用则自动检测,如果没有则使用新的Error()。stack。
在chrome,safari和firefox中测试过。

2个变体 - stackFN(n)为您提供远离直接调用者的函数n的名称,stackArray()为您提供一个数组,stackArray()[0]作为直接调用者。

在http://jsfiddle.net/qcP9y/6/试试吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// returns the name of the function at caller-N
// stackFN()  = the immediate caller to stackFN
// stackFN(0) = the immediate caller to stackFN
// stackFN(1) = the caller to stackFN's caller
// stackFN(2) = and so on
// eg console.log(stackFN(),JSON.stringify(arguments),"called by",stackFN(1),"returns",retval);
function stackFN(n) {
    var r = n ? n : 0, f = arguments.callee,avail=typeof f ==="function",
        s2,s = avail ? false : new Error().stack;
    if (s) {
        var tl=function(x) { s = s.substr(s.indexOf(x) + x.length);},
        tr = function (x) {s = s.substr(0, s.indexOf(x) - x.length);};
        while (r-- >= 0) {
            tl(")");
        }
        tl(" at");
        tr("(");
        return s;
    } else {
        if (!avail) return null;
        s ="f = arguments.callee"
        while (r>=0) {
            s+=".caller";
            r--;  
        }
        eval(s);
        return f.toString().split("(")[0].trim().split("")[1];
    }
}
// same as stackFN() but returns an array so you can work iterate or whatever.
function stackArray() {
    var res=[],f = arguments.callee,avail=typeof f ==="function",
        s2,s = avail ? false : new Error().stack;
    if (s) {
        var tl=function(x) { s = s.substr(s.indexOf(x) + x.length);},
        tr = function (x) {s = s.substr(0, s.indexOf(x) - x.length);};
        while (s.indexOf(")")>=0) {
            tl(")");
            s2=""+s;
            tl(" at");
            tr("(");
            res.push(s);
            s=""+s2;
        }
    } else {
        if (!avail) return null;
        s ="f = arguments.callee.caller"
        eval(s);
        while (f) {
            res.push(f.toString().split("(")[0].trim().split("")[1]);
            s+=".caller";
            eval(s);
        }
    }
    return res;
}


function apple_makes_stuff() {
    var retval ="iPhones";
    var stk = stackArray();

    console.log("function",stk[0]+"() was called by",stk[1]+"()");
    console.log(stk);
    console.log(stackFN(),JSON.stringify(arguments),"called by",stackFN(1),"returns",retval);
    return retval;
}



function apple_makes (){
    return apple_makes_stuff("really nice stuff");
}

function apple () {
    return apple_makes();
}

   apple();

您可以使用此库http://www.stacktracejs.com/。这很好

从文档

You can also pass in your own Error to get a stacktrace not available
in IE or Safari 5-

1
2
3
4
5
6
7
8
9
10
11
12
13
<script type="text/javascript" src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js">
<script type="text/javascript">
    try {
        // error producing code
    } catch(e) {
        var trace = printStackTrace({e: e});
        alert('Error!
'
+ 'Message: ' + e.message + '
Stack trace:
'
+ trace.join('
'
));
        // do something else with error
    }


1
2
3
4
5
function stacktrace(){
  return (new Error()).stack.split('
'
).reverse().slice(0,-2).reverse().join('
'
);
}


这是一个答案,为您提供最大性能(IE 6+)和最大兼容性。与IE 6兼容!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    function stacktrace( log_result ) {
        var trace_result;
    // IE 6 through 9 compatibility
    // this is NOT an all-around solution because
    // the callee property of arguments is depredicated
    /*@cc_on
        // theese fancy conditinals make this code only run in IE
        trace_result = (function st2(fTmp) {
            // credit to Eugene for this part of the code
            return !fTmp ? [] :
                st2(fTmp.caller).concat([fTmp.toString().split('(')[0].substring(9) + '(' + fTmp.arguments.join(',') + ')']);
        })(arguments.callee.caller);
        if (log_result) // the ancient way to log to the console
            Debug.write( trace_result );
        return trace_result;
    @*/

        console = console || Console;   // just in case
        if (!(console && console.trace) || !log_result){
            // for better performance in IE 10
            var STerror=new Error();
            var unformated=(STerror.stack || STerror.stacktrace);
            trace_result ="\u25BC console.trace" + unformated.substring(unformated.indexOf('
'
,unformated.indexOf('
'
)));
        } else {
            // IE 11+ and everyone else compatibility
            trace_result = console.trace();
        }
        if (log_result)
            console.log( trace_result );
       
        return trace_result;
    }
// test code
(function testfunc(){
    document.write("[cc lang="javascript"]" + stacktrace( false ) +"

");
})();


哇 - 我没有看到一个人在6年内建议我们先检查stack是否可用才能使用它!你可以在错误处理程序中做的最糟糕的事情是抛出一个错误,因为调用了一些不存在的东西。

正如其他人所说,虽然stack现在大部分都是安全的,但在IE9或更早版本中并不支持。

我记录我的意外错误,堆栈跟踪非常重要。为获得最大支持,我首先检查Error.prototype.stack是否存在并且是一个函数。如果是这样,那么使用error.stack是安全的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        window.onerror = function (message: string, filename?: string, line?: number,
                                   col?: number, error?: Error)
        {
            // always wrap error handling in a try catch
            try
            {
                // get the stack trace, and if not supported make our own the best we can
                var msg = (typeof Error.prototype.stack == 'function') ? error.stack :
                         "NO-STACK" + filename + ' ' + line + ':' + col + ' + message;

                // log errors here or whatever you'
re planning on doing
                alert(msg);
            }
            catch (err)
            {

            }
        };

编辑:似乎由于stack是属性而不是方法,所以即使在旧版浏览器上也可以安全地调用它。我仍然感到困惑,因为我很确定之前检查Error.prototype对我有用,现在却没有 - 所以我不确定发生了什么。


使用console.error(e.stack) Firefox仅显示日志中的堆栈跟踪,
Chrome也会显示该消息。
如果消息包含重要信息,这可能是一个不好的惊喜。始终记录两者。


在Firefox上获取堆栈跟踪比在IE上更容易,但从根本上说这就是你想要做的事情:

在try / catch块中包装"有问题"的代码片段:

1
2
3
4
5
6
7
try {
    // some code that doesn't work
    var t = null;
    var n = t.not_a_value;
}
    catch(e) {
}

如果要检查"错误"对象的内容,则它包含以下字段:

e.fileName:问题来源的源文件/页面
e.lineNumber:出现问题的文件/页面中的行号
e.message:描述发生了什么类型错误的简单消息
e.name:发生的错误类型,在上面的示例中应该是'TypeError'
e.stack:包含导致异常的堆栈跟踪

我希望这能够帮到你。


试一试

1
throw new Error('some error here')

这适用于chrome:

enter image description here


我不得不用IE11调查smartgwt中的无限递归,所以为了更深入地研究,我需要一个堆栈跟踪。问题是,我无法使用开发控制台,因为复制更加困难。
在javascript方法中使用以下内容:

1
try{ null.toString(); } catch(e) { alert(e.stack); }