Как я могу получить трассировку стека 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 .

Ответов (25)

Решение

Изменить 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);
}

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

Я надеюсь, что это помогает вам.

один из способов получить реальную трассировку стека в 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() показывает трассировку стека.

В 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

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

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

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

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

Немного поздно для вечеринки, но вот еще одно решение, которое автоматически определяет, доступен ли arguments.callee, и использует новый стек Error ()., Если нет. Протестировано в Chrome, Safari и 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'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();

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

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

Вы можете использовать эту библиотеку 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>

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

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

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

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

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

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

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

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

функция:

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

вариант использования:

     try{
         aaa.bbb;//error throw here
     }
     catch (err){
         print_call_stack(err); 
     }
function stacktrace(){
  return (new Error()).stack.split('\n').reverse().slice(0,-2).reverse().join('\n');
}

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

Скриншот

Вот ответ, который дает вам максимальную производительность (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>" );
})();

Хорошее (и простое) решение, как указано в комментариях к исходному вопросу, - использовать 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
.success[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'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 :

Никаких следов

Вы даже можете отключить первый бит отображаемой информации (установить Console.settings.debug.showInfo значение false ) или полностью отключить отладку (установить Console.settings.debug.enabled значение false ), чтобы вам больше не пришлось комментировать оператор отладки! Просто оставьте их, и это ничего не даст.

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

throw new Error('some error here')

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

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

Этот код полифилла, работающий в современных (2017) браузерах (IE11, Opera, Chrome, FireFox, Яндекс):

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 - не работает в строгом режиме ни в одном браузере!

по крайней мере, в Edge 2021:

console.groupCollapsed('jjjjjjjjjjjjjjjjj')
    console.trace()
    try {
        throw "kuku"
    } catch(e) {
        console.log(e.stack)
    }
console.groupEnd()
traceUntillMe()

и ты закончил мой друг

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

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

Пример:

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

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

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

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

См. Подробную информацию в документации V8.