Как я могу получить трассировку стека JavaScript при выдаче исключения?

Если я сам сгенерирую исключение JavaScript (например, throw "AArrggg"), как я могу получить трассировку стека (в Firebug или иным образом)? Прямо сейчас я просто получаю сообщение.

редактировать: как много людей ниже опубликовал, возможно получить трассировку стека для исключения JavaScript, но я хочу получить трассировку стека для моих исключений. Например:

function foo() {
    bar(2);
}
function bar(n) {
    if (n < 2)
        throw "Oh no! 'n' is too small!"
    bar(n-1);
}

Когда вызывается foo, я хочу получить трассировку стека, которая включает вызовы foo, bar, bar.

Ответы

Ответ 1

Изменить 2 (2017):

Во всех современных браузерах вы можете просто вызвать: console.trace(); (Ссылка MDN)

Изменить 1 (2013):

Лучшее (и более простое) решение, как указано в комментариях к исходному вопросу, заключается в использовании свойства stack объекта Error следующим образом:

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

Это сгенерирует вывод примерно так:

[email protected]://localhost:49573/assets/js/scripts.js:44
[email protected]://localhost:49573/assets/js/scripts.js:9
[email protected]://localhost:49573/:462
x.Callbacks/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:4
[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:6

Предоставление имени вызывающей функции вместе с URL, вызывающей функцией и т.д.

Оригинал (2009):

Модифицированная версия этого фрагмента может несколько помочь:

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);
}

Ответ 2

Обратите внимание, что chromium/chrome (другие браузеры, использующие V8), а также Firefox имеют удобный интерфейс для получения трассировки стека через свойство стека объектов Error.

try {
   // Code throwing an exception
} catch(e) {
  console.log(e.stack);
}

Это относится как к базовым исключениям, так и к тем, которые вы сами выбрасываете. (Считается, что вы используете класс Error, который в любом случае является хорошей практикой).

Подробности смотрите в документации V8

Ответ 3

В Firefox кажется, что вам не нужно бросать исключение. Достаточно сделать

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

Ответ 4

Если у вас есть firebug, на вкладке script есть опция break on all errors. Как только script попадает в точку останова, вы можете посмотреть окно стека firebug:

screenshot

Ответ 6

Хорошее (и простое) решение, указанное в комментариях к исходному вопросу, заключается в использовании свойства stack объекта Error, например:

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

Это будет генерировать вывод следующим образом:

[email protected]://localhost:49573/assets/js/scripts.js:44
[email protected]://localhost:49573/assets/js/scripts.js:9
[email protected]://localhost:49573/:462
x.Callbacks/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:4
x.Callbacks/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:4
[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:6
.send/[email protected]://localhost:49573/assets/js/jquery-1.10.2.min.js:6

Предоставление имени вызывающей функции вместе с URL-адресом и номером строки, его вызывающей функцией и т.д.

У меня есть очень сложное и красивое решение, которое я разработал для проекта, над которым я сейчас работаю, и я извлек и переработал его немного, чтобы его обобщили. Вот он:

(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("\n"),
                aCurrentLine,
                iCurrIndex = ((currentBrowser.webkit) ? 3 : 2),
                sCssBlack = "color:black;",
                sCssFormat = "color:{0}; font-weight:bold;",
                sLines = "";

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

            // Show info if the setting is true and there 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 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) ? "\n\n" : "\n")).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), чтобы вам больше не приходилось закомментировать выражение об отладке! Просто оставьте их, и это ничего не сделает.

Ответ 7

Вы можете получить доступ к свойствам stack (stacktrace в Opera) экземпляра Error, даже если вы его выбросили. Дело в том, что вам нужно использовать throw new Error(string) (не забывайте новый вместо throw string.

Пример:

try {
    0++;
} catch (e) {
    var myStackTrace = e.stack || e.stacktrace || "";
}

Ответ 8

Один из способов получить реальную трассировку стека в Firebug - создать реальную ошибку, например, вызвать функцию undefined:

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() показывает трассировку стека.

Ответ 10

Обновление для ответа Евгения: объект ошибки должен быть выброшен для того, чтобы IE (конкретные версии?) заполнили свойство stack. Следующее должно работать лучше, чем его текущий пример, и следует избегать возврата undefined, когда в IE.

function stackTrace() {
  try {
    var err = new Error();
    throw err;
  } catch (err) {
    return err.stack;
  }
}

Примечание 1: Такое поведение должно выполняться только при отладке и отключении при работе в режиме реального времени, особенно при частом вызове. Примечание 2: Это может не работать во всех браузерах, но, похоже, работает в FF и IE 11, что прекрасно подходит для моих потребностей.

Ответ 11

Это даст трассировку стека (в виде массива строк) для современных Chrome, Opera, Firefox и IE10+

function getStackTrace () {

  var stack;

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

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

Использование:

console.log(getStackTrace().join('\n'));

Он исключает из стека свой собственный вызов, а также заголовок "Ошибка", который используется Chrome и Firefox (но не IE).

Он не должен вылетать в старых браузерах, а просто возвращать пустой массив. Если вам нужно более универсальное решение, посмотрите на stacktrace.js. Его список поддерживаемых браузеров действительно впечатляет, но, на мой взгляд, он очень большой для той небольшой задачи, для которой он предназначен: 37 КБ минимизированного текста, включая все зависимости.

Ответ 12

В Google Chrome (версия 19.0 и выше) просто бросая исключение работает отлично. Например:

/* 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();

отобразит трассировку стека на выходе консоли браузера:

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

Надеюсь на эту помощь.

Ответ 13

<script type="text/javascript"
src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script>
<script type="text/javascript">
    try {
        // error producing code
    } catch(e) {
        var trace = printStackTrace({e: e});
        alert('Error!\n' + 'Message: ' + e.message + '\nStack trace:\n' + trace.join('\n'));
        // do something else with error
    }
</script>

этот script покажет ошибку

Ответ 14

функция:

function print_call_stack(err) {
    var stack = err.stack;
    console.error(stack);
}

прецедент:

     try{
         aaa.bbb;//error throw here
     }
     catch (err){
         print_call_stack(err); 
     }

Ответ 15

Этот polyfill код работает в современных (2017) браузерах (IE11, Opera, Chrome, FireFox, Yandex):

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

Другие ответы:

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

не работает в IE 11!

Использование arguments.callee.caller - не работает в строгом режиме в любом браузере!

Ответ 16

Вы можете использовать эту библиотеку http://www.stacktracejs.com/. Это очень хорошо

Из документации

Вы также можете передать свою собственную ошибку, чтобы получить стек, не доступный в IE или Safari 5 -

<script type="text/javascript" src="https://rawgithub.com/stacktracejs/stacktrace.js/master/stacktrace.js"></script>
<script type="text/javascript">
    try {
        // error producing code
    } catch(e) {
        var trace = printStackTrace({e: e});
        alert('Error!\n' + 'Message: ' + e.message + '\nStack trace:\n' + trace.join('\n'));
        // do something else with error
    }
</script>

Ответ 17

function stacktrace(){
  return (new Error()).stack.split('\n').reverse().slice(0,-2).reverse().join('\n');
}

Ответ 18

Легче получить трассировку стека в Firefox, чем в IE, но принципиально здесь то, что вы хотите сделать:

Оберните "проблемный" фрагмент кода в блоке try/catch:

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: содержит трассировку стека, которая вызвала исключение.

Надеюсь, это поможет вам.

Ответ 19

Поздно вечеринке, но вот еще одно решение, которое автоматически определяет, если доступно аргументы .callee, и использует новый Error(). stack if not. Протестировано в хроме, сафари и firefox.

2 варианта - stackFN (n) дает вам имя функции n вне непосредственного вызывающего, а stackArray() дает вам массив, stackArray() [0], являющийся непосредственным вызывающим.

Попробуйте http://jsfiddle.net/qcP9y/6/

// 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 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();

Ответ 20

Мне пришлось исследовать бесконечную рекурсию в smartgwt с IE11, поэтому, чтобы исследовать глубже, мне понадобилась трассировка стека. Проблема заключалась в том, что я не смог использовать консоль dev, потому что воспроизведение было сложнее. Используйте следующий метод javascript:

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

Ответ 21

Вау - я не вижу ни одного человека через 6 лет, предлагая сначала проверить, есть ли stack, прежде чем использовать его! Самое худшее, что вы можете сделать в обработчике ошибок - это выбросить ошибку из-за вызова чего-то, чего не существует.

Как говорили другие, в то время как stack в основном безопасен в использовании, он не поддерживается в IE9 или ранее.

Я регистрирую свои неожиданные ошибки, и трассировка стека очень важна. Для максимальной поддержки я сначала проверяю, существует ли Error.prototype.stack и является функцией. Если это так, безопасно использовать error.stack.

        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 работала на меня раньше, а теперь этого нет, поэтому я не уверен, что происходит.

Ответ 22

Использование console.error(e.stack) Firefox показывает только стек в журналах, В Chrome также отображается сообщение. Это может быть неприятным сюрпризом, если сообщение содержит важную информацию. Всегда регистрируйте оба.

Ответ 23

Вот ответ, который дает максимальную производительность (IE 6+) и максимальную совместимость. Совместимость с IE 6!

    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('\n',unformated.indexOf('\n'))); 
    	} 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( "<pre>" + stacktrace( false ) + "</pre>" );
})();

Ответ 24

Просто попробуйте

throw new Error('some error here')

Это очень хорошо работает для хрома:

введите описание изображения здесь