Javascript V8复合错误堆栈格式

Javascript V8 composite Error stack format

我正在研究一个框架。我想定义一个 CompositeError class 这意味着它可以包含其他相关错误...

var err1 = new Error("msg1");
var err2 = new TypeError("msg2");
var err3 = new Error("msg3");

var err = new CompositeError({
    errors: [err1, err2, err3]
});

现在到了 v8,由于一个简单的错误,我得到了类似这样的堆栈:

var stackString = [
    "Error",
    "   at module.exports.extend.init (http://example.com/example.js:75:31)",
    "   at new Descendant (http://example.com/example.js:11:27)",
    "   at custom (http://example.com/spec/example.spec.js:222:23)",
    "   at Object.<anonymous> (http://example.com/spec/example.spec.js:224:13)",
    "   at http://example.com/spec/example.spec.js:10:20"
].join("\n");

有几个Stack解析器库,例如:https://github.com/stacktracejs/stacktrace.js

现在您怎么看,我怎样才能将 4 个错误实例的堆栈合并为一个并保持解析器兼容?

(请注意,我们讨论的是异步代码,因此与同步代码不同,这 4 个错误可以具有完全不同的堆栈。)

结论

我决定不通过复合错误来兼容堆栈解析器。我只是简单地写下 属性 的路径,例如x.y.z 和子错误的堆栈。

try {
    try {
        throw new df.UserError("Something really bad happened.");
    }
    catch (cause) {
        throw new df.CompositeError({
            message: "Something really bad caused this.",
            myCause: cause
        });
    }
catch (err) {
    console.log(err.toString());
    // CompositeError Something really bad caused this.

    console.log(err.stack);
    // prints the stack, something like:
    /*
        CompositeError Something really bad caused this.
            at null.<anonymous> (/README.md:71:11)
            ...
        caused by <myCause> UserError Something really bad happened.
            at null.<anonymous> (/README.md:68:9)
            ...
    */
}

这有点类似于 java 和其他支持嵌套错误的语言。

可以像这样轻松嵌套:

new df.CompositeError({
    message: "A complex composite error.",
    x: new df.CompositeError({
        message: "Nested error x.",
        y: new df.CompositeError({
            message: "Nested error x.y",
            z: new Error("Deeply nested error x.y.z")
        })
    })
});

我很难找到关于堆栈的任何信息。

我想我可以做这样的事情:

var stackString = [
    "CompositeError msg - composed of #1:Error msg1 #2:TypeError msg2 #3:Error msg3",
    "   at module.exports.extend.init (http://example.com/example.js:75:31)",
    "   at new Descendant (http://example.com/example.js:11:27)",
    "   at Error (#1:0:0)",
    "   at custom (http://example.com/spec/example.spec.js:222:23)",
    "   at Object.<anonymous> (http://example.com/spec/example.spec.js:224:13)",
    "   at TypeError (#2:0:0)",
    "   at http://example.com/spec/example.spec.js:10:20",
    ...
].join("\n");

整个 Composite 堆栈的问题在于环境存在差异。不仅仅是堆栈格式差异(可以使用不同的解析器进行管理),还有堆栈创建差异......

The stack property is set to undefined when the error is constructed, and gets the trace information when the error is raised. If an error is raised multiple times, the stack property is updated each time the error is raised.

https://msdn.microsoft.com/en-us/library/ie/hh699850%28v=vs.94%29.aspx

例如,在 nodejs 中,堆栈是通过实例化 Error 创建的,而在 MSIE 中,它是通过抛出 Error 实例来创建的。

var err = new Error(msg); // 1st line

//...

console.log(err.stack);
// by node.js this prints the stack generated by the 1st line
// by msie this prints nothing (according to the documentation, I did not try it out yet)

try {
    throw err; // Nth line
} catch (err){
    console.log(err.stack); 
    // by node.js this prints the stack generated by the 1st line
    // by msie this prints the stack generated by the Nth line (according to the documentation)
}

因此,如果我想留在公司,我必须执行以下操作:

var Error = extend(NativeError, {
    id: undefined,
    name: "Error",
    message: "",
    configure: Object.prototype.configure,
    isOptions: Object.prototype.isOptions,
    init: function (options, preprocessor) {
        this.id = id();
        if (typeof (options) == "string")
            options = {message: options};
        this.configure(options, preprocessor);

        var nativeError;
        try {
            throw new NativeError();
        } catch (caughtError){
            nativeError = caughtError;
        }

        var error = this;
        var stack;
        NativeObject.defineProperty(this, "stack", {
            enumerable: true,
            get: function () {
                if (stack === undefined) {
                    stack = "";
                    stack += error.name + " " + error.message + "\n";
                    stack += Stack.instance(nativeError);
                    delete(nativeError);
                }
                return stack;
            }
        });
    }
}, {
    instance: Object.instance,
    extend: Object.extend
});

现在我需要解析,因为如果没有解析,每个自定义错误堆栈都将包含我的自定义错误实例的实例化和 init() 的调用,如您在此处所见:

var stackString = [
    "Error",
    "   at module.exports.extend.init (http://example.com/example.js:75:31)",
    "   at new Descendant (http://example.com/example.js:11:27)",
    ...
].join("\n");

通过子类,列表也将包含子类的 init()(如果它覆盖并在之后调用原始 init)。

编辑:

我最终使用了 this format

CompositeError msg
    at ...
    ...
caused by <p> CompositeError msg2
    at ...
    ...
caused by <p.x> CustomErrror1 msg3
    at ...
    ...
caused by <q> CustomError2 msg4
    at ...
    ...

来自

throw new CompositeError({
    message: msg,
    p: new CompositeError({
        message: msg2,
        x: new CustomError1({
            message: msg3
        })
    }),
    q: new CustomError2({
        message: msg4
    })
});

也许 caused by 不是描述这种关系的最佳术语,可能 in association 或类似的东西会更好,但我认为分隔符字符串是什么并不重要。如果我没记错的话这个 caused by 来自 java composite exceptions.

你很幸运,现在有一个原生 AggregateError 可以满足你的需要:

const err1 = new Error("msg1");
const err2 = new TypeError("msg2");
const err3 = new Error("msg3");

const err = new AggregateError([err1, err2, err3]);