Как я могу получить трассировку стека JavaScript при выдаче исключения? - PullRequest
455 голосов
/ 26 февраля 2009

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

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

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

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

Ответы [ 24 ]

648 голосов
/ 11 марта 2009

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

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

Редактировать 1 (2013):

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

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

Будет сгенерирован вывод:

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):

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

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);
}
171 голосов
/ 17 ноября 2010

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

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

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

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

76 голосов
/ 28 февраля 2013

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

e = new Error();
console.log(e.stack);
25 голосов
/ 27 февраля 2009

Если у вас есть firebug, на вкладке скрипта есть опция перерыва на все ошибки. Как только сценарий достигнет вашей точки останова, вы можете посмотреть в окне стека firebug:

screenshot

10 голосов
/ 13 августа 2013

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

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

Будет сгенерировано следующее:

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 и номером строки, ее вызывающей функцией и т. Д.

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

(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(/(.*):/, "$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) ? "\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), так что вам больше не придется закомментировать оператор отладки! Просто оставьте их, и это ничего не даст.

10 голосов
/ 26 февраля 2009

Я не думаю, что есть что-то встроенное, что вы можете использовать, однако я нашел много примеров, когда люди сами катались.

7 голосов
/ 03 июля 2014

С браузером Chrome вы можете использовать console.trace метод: https://developer.chrome.com/devtools/docs/console-api#consoletraceobject

7 голосов
/ 21 августа 2009

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

Пример:

try {
    0++;
} catch (e) {
    var myStackTrace = e.stack || e.stacktrace || "";
}
6 голосов
/ 23 января 2015

Это даст трассировку стека (в виде массива строк) для современных 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 КБ минимизированного текста, включая все зависимости.

6 голосов
/ 14 марта 2012

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

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

...