在ExtendScript中获取错误的堆栈跟踪

时间:2013-04-24 20:29:48

标签: adobe adobe-indesign extendscript indesign-server

当我在ExtendScript中捕获错误时,我希望能够记录其堆栈跟踪。似乎错误在ExtendScript中不包含堆栈跟踪,所以我正在尝试将错误添加到堆栈跟踪中。

我知道获得堆栈跟踪的唯一方法是$.stack。字段$.stack包含您访问该字段时的当前堆栈跟踪。

我的第一次尝试是创建自己的包含堆栈的错误对象。 Error对象非常特殊,因为它可以获取创建它的代码的行和文件名。例如,

try {
    throw new Error("Houston, we have a problem.");
}
catch (e) {
    $.writeln("Line: " + e.line);
    $.writeln("File: " + e.fileName);
    $.writeln("Message: " + e.message);
}

将打印:

Line: 2
File: ~/Desktop/Source1.jsx
Message: Houston, we have a problem.

我认为用这种能力创建你自己的对象是不可能的。我能得到的最接近的是:

function MyError(msg, file, line) {
    this.message = msg;
    this.fileName = file;
    this.line = line;
    this.stack = $.stack;
}

try {
    throw new MyError("Houston, we have a problem.", $.fileName, $.line);
}
catch (e) {
    $.writeln("Line: " + e.line);
    $.writeln("File: " + e.fileName);
    $.writeln("Message: " + e.message);
    $.writeln("Stack: " + e.stack);
}

打印哪些:

Line: 9
File: ~/Desktop/Source2.jsx
Message: Houston, we have a problem.
Stack: [Source3.jsx]
MyError("Houston, we have a p"...,"~/Desktop/Source2.js"...,9)

在这里我们可以看到我正在创建自己的错误对象并明确地传递它的行和文件名(因为MyError无法自行解决)。我还在创建错误时包含了当前堆栈。

当我调用自己的错误对象时,这可以正常工作,但是当其他代码调用常规Error对象或自动生成错误时(例如通过非法访问),它不起作用。我希望能够获得任何错误的堆栈跟踪,无论它是如何生成的。

其他方法可能是修改Error的构造函数,修改Error的原型,或者完全替换Error对象。我无法使这些方法起作用。

另一个想法是在我的代码的每个方法中放置一个catch块,并将当前堆栈添加到错误中(如果它还没有)。如果可能,我想避免使用此选项。

我没有想法。有没有办法让堆栈跟踪错误?

4 个答案:

答案 0 :(得分:2)

这不完美,但我找到了部分解决方案。

事实1:Error.prototype是一个错误对象。

事实2:无论何时创建错误,都会调用方法Error.prototype.toString

事实3:可以修改字段Error.prototype.toString

该方法通常只返回字符串“Error”,因此我们可以用我们自己的存储堆栈的方法替换它,然后返回字符串“Error”。

Error.prototype.toString = function() {
    if (typeof this.stack === "undefined" || this.stack === null) {
        this.stack = "placeholder";
        // The previous line is needed because the next line may indirectly call this method.
        this.stack = $.stack;
    }
    return "Error";
}

try {
    throw new Error("Houston, we have a problem.");
}
catch (e) {
    $.writeln("Line: " + e.line);
    $.writeln("File: " + e.fileName);
    $.writeln("Message: " + e.message);
    $.writeln("Stack: " + e.stack);
}

结果:

Line: 11
File: ~/Desktop/Source10.jsx
Message: Houston, we have a problem.
Stack: [Source10.jsx]
toString()

有效!唯一的问题是自动错误。

Error.prototype.toString = function() {
    if (typeof this.stack === "undefined" || this.stack === null) {
        this.stack = "placeholder";
        // The previous line is needed because the next line may indirectly call this method.
        this.stack = $.stack;
    }
    return "Error";
}

try {
    var foo = null;
    foo.bar;
}
catch (e) {
    $.writeln("Line: " + e.line);
    $.writeln("File: " + e.fileName);
    $.writeln("Message: " + e.message);
    $.writeln("Stack: " + e.stack);
}

结果:

Line: 12
File: ~/Desktop/Source12.jsx
Message: null is not an object
Stack: undefined

所以它不适用于所有错误,而是它的进展。

答案 1 :(得分:2)

我已经提出了另一种解决方案,但这需要您更改一些代码。而不是像往常一样调用方法:

myObject.myMethod1("Hello", "world");

你需要切换到这样的调用方法:

myObject.do("myMethod1", "Hello", "world");

以下是一个完整的示例:

Object.prototype.do = function stackHelper() {
    // Convert the arguments into an array.
    var argumentArray = Array.prototype.slice.call(arguments);
    // Remove the first argument, which is the function's name.
    var functionString = argumentArray.shift();
    try {
        this[functionString].apply(this, argumentArray);
    }
    catch (e) {
        if (typeof e.stack === "undefined" || e.stack === null) {
            e.stack = $.stack;
        }
        throw e;
    }
};

var myObject = {
    myMethod1: function myMethod1(myArg1, myArg2){
        this.do("myMethod2", myArg1, myArg2);
    },

    myMethod2: function myMethod2(myArg1, myArg2){
        this.do("myMethod3", myArg1, myArg2);
    },

    myMethod3: function myMethod3(myArg1, myArg2){
        $.writeln(myArg1 + ", " + myArg2 + "!");
        var foo = null;
        foo.bar; // Throws an error.
    },
};

try {
    myObject.do("myMethod1", "Hello", "world");
}
catch (e) {
    $.writeln("Stack: " + e.stack);
}

输出如下:

Hello, world!
Stack: [do.jsx]
stackHelper("myMethod1","Hello","world")
myMethod1("Hello","world")
stackHelper("myMethod2","Hello","world")
myMethod2("Hello","world")
stackHelper("myMethod3","Hello","world")

这不是一个很好的解决方案,但至少它适用于所有错误。

答案 2 :(得分:1)

据我所知,您无法修改Error.prototype.toString - 函数的[本机代码]。所以我提出了这个解决方案:

function ReturnCustomErrorString(e, additionalMessage)
{
    try {
        var errorString = e.toString();
        errorString = errorString.concat("\n", "additionalMessage: " + additionalMessage + "\n", "file: " + e.fileName + "\n", "line: " + e.line + "\n", "stack-trace: \n" + $.stack);
        return errorString;
    }
    catch (e) {
        alert("Error in : " + ReturnCustomErrorString.name + "(...)\n" + e);
        exit();
    }
}

用法:

try {
    // code that does throw an error
} catch (e) { 
    alert(ReturnCustomErrorString(e)); 
}

在我写这个函数之前,我经常在catch-block中做过类似的事情:

alert(e);

现在我正在做alert(ReturnCustomErrorString(e));,但我获得了更多有用的信息。所以目前我认为这个解决方案非常好。

答案 3 :(得分:0)

如果您只需要显示自定义消息,我就会编写此代码。 我认为这解决了,...对我来说没关系。

try
{
    app.selection[0].contents = 1
}
catch (myError)
{
    alert(myError.number); // For check the number error
    if (myError.number == 30477)
    {
        alert("Mensagem Edu\n" + "Line: " + myError.line + "\n" + "File: " + myError.fileName + "\n" + "Message: " + myError.message + "\n" + "Stack: " + myError.stack);
        exit();
    }
    else (myError);
    {}
    exit();
}