چگونه میتوانم هنگام پرتاب یک استثنا، یک ردپای پشته جاوااسکریپت دریافت کنم؟


اگر خودم یک استثنای جاوااسکریپت پرتاب کنم (مثلاً throw "AArrggg")، چگونه میتوانم ردپای استک را دریافت کنم (در فایرباگ یا غیر آن)؟ در حال حاضر فقط پیام را دریافت میکنم.

ویرایش: همانطور که بسیاری از افراد در زیر نوشتهاند، امکان گرفتن یک trace پشته برای یک استثنای جاوااسکریپت وجود دارد، اما من میخواهم یک trace پشته برای استثناهای خودم بگیرم. برای مثال:

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

وقتی foo فراخوانی میشود، میخواهم یک ردپای پشته دریافت کنم که شامل فراخوانیهای foo، bar، bar باشد.

پاسخ شماره ۱

ویرایش ۲ (۲۰۱۷):

در تمام مرورگرهای مدرن شما میتوانید به سادگی فراخوانی کنید: console.trace(); (مرجع MDN)

ویرایش ۱ (۲۰۱۳):

یک راه حل بهتر (و سادهتر) همانطور که در نظرات پرسش اصلی اشاره شده است، استفاده از خاصیت 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);
}

پاسخ شماره ۲

مرورگرهای Chrome/Chromium و سایر مرورگرهایی که از V8 استفاده میکنند، همچنین Firefox، یک رابط کاربری راحت برای دریافت stacktrace از طریق ویژگی stack شیء Error دارند:

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

جزئیات را در مستندات V8 ببینید

پاسخ شماره ۳

به نظر میرسد در فایرفاکس نیازی به پرتاب استثنا ندارید. کافی است انجام دهید

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

پاسخ شماره ۴

اگر Firebug دارید، در تب اسکریپت گزینهای برای توقف در تمام خطاها وجود دارد. وقتی اسکریپت به نقطه توقف شما برسد، میتوانید پنجرهی پشتهی Firebug را مشاهده کنید:

پاسخ شماره ۵

یک راه حل خوب (و ساده) همانطور که در نظرات سوال اصلی اشاره شده است، استفاده از ویژگی 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);

آن را در گیتهاب بررسی کنید (در حال حاضر نسخه ۱.۲)! شما میتوانید از آن مانند Console.debug("Whatever"); استفاده کنید و بسته به تنظیمات در Console، خروجی و یک دنبالگذاری پشته را چاپ میکند (یا فقط اطلاعات ساده/چیزی اضافه چاپ نمیکند). در اینجا یک مثال آورده شده است:

مطمئن شوید که با تنظیمات در شیء Console بازی کنید! شما میتوانید فاصله بین خطوط ردگیری را اضافه کنید و یا آن را کاملاً خاموش کنید. در اینجا با Console.trace تنظیم شده روی false نشان داده شده است:

شما حتی میتوانید اولین بخش از اطلاعات نمایش داده شده را خاموش کنید (تنظیم Console.settings.debug.showInfo روی false) یا کل دیباگ را به طور کامل غیرفعال کنید (تنظیم Console.settings.debug.enabled روی false) تا دیگر نیازی به کامنت گذاشتن روی دستورات دیباگ نداشته باشید! فقط آنها را همانطور بگذارید و این هیچ کاری انجام نخواهد داد.

پاسخ شماره ۶

من فکر نمیکنم چیزی داخلی وجود داشته باشد که بتوانید استفاده کنید، با این حال من تعداد زیادی نمونه از افرادی که خودشان چیزهایی ساختهاند پیدا کردم.

  • ردیابی استک جاوااسکریپت DIY
  • ردیابی پشته جاوااسکریپت در هر مرورگر