33

Is there a standard / best practice way to add a cause of an exception in javascript. In java you might do this:

Throwable t = new Exception("whatever");
t.addCause(previouslyCaughtException);
throw t;

When the resulting exception is printed, it'll give you a nice trace that includes causes. Is there any good way to do this in javascript or do I have to roll my own?

4

5 回答 5

10

现在(直到有更好的答案),这就是我所做的:

...
} catch(e) {
  throw new Error("My error message, caused by: "+e.stack+"\n ------The above causes:-----")
}

我打印异常的方式使它看起来既漂亮又干净:

console.log(e.stack)

打印这样的东西:

My error message: SomeError
<some line>
<more lines>
------The above causes:-----
<some line>
<more lines>

如果它说“原因”,该行可能会更好,因为首先打印导致错误的异常的堆栈跟踪。

于 2013-07-26T20:00:05.563 回答
8

如果您使用的是 Node.js,则可以使用VError

Joyent 还有一个页面讨论 Node.js 中错误处理的最佳实践https://www.joyent.com/developers/node/design/errors

唯一的事情是,如果您传入空或未定义的参数,我不喜欢 Joyent 的 VError 实现如何失败。当您处理错误时,这一点尤其重要,因为它只会掩盖问题的根本原因。我已经分叉了他们的 VError,所以它不会因 null 或 undefined 参数而失败。 https://github.com/naddison36/node-verror

于 2014-10-14T02:03:31.573 回答
6

在产品中,我们使用TraceError

用法

import TraceError from 'trace-error';

global.TraceError = TraceError; // expose globally (optional)

throw new TraceError('Could not set status', srcError, ...otherErrors);

输出

于 2016-03-14T11:41:39.580 回答
2

tl;dr 这不是一个解决方案,只是一个帮助,直到 ECMA 脚本采用一些标准。

编辑:我将此答案包装到chainable-error npm 包中。

嗯,这是一个困难的话题。原因是,ECMA 脚本定义中没有关于堆栈跟踪的定义(甚至在 ES9 / ES2019中也没有)。所以一些引擎实现了他们自己的堆栈跟踪及其表示的想法。

他们中的许多人已经实现了该Error.prototype.stack属性,它是堆栈跟踪的字符串表示形式。由于未定义,因此您不能依赖字符串格式。幸运的是,V8 引擎很常见(Google Chrome 和 NodeJS),这让我们有机会至少尝试一下。

V8(以及使用它的应用程序)的一个好处是堆栈跟踪具有通用格式:

/path/to/file/script.js:11
        throw new Error("Some new Message", e);
        ^

Error: Some new Message
    at testOtherFnc (/path/to/file/script.js:69:15)
    at Object.<anonymous> (/path/to/file/script.js:73:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)

...并且堆栈跟踪不会在控制台中解析和设置样式。

这给了我们一个很好的机会来链接它们(或者至少改变错误产生的输出)。

一个非常简单的方法是这样的:

let ff = v => JSON.stringify(v, undefined, 4);
const formatForOutput = v => {
    try {
        return ff(v).replace(/\n/g, '\n    ');
    } catch (e) {
        return "" + v;
    }
};

const chainErrors = exporting.chainErrors = (e1, e2) => {
    if (e1 instanceof Error)
        e2.stack += '\nCaused by: ' + e1.stack;
    else
        e2.stack += '\nWas caused by throwing:\n    ' + formatForOutput(e1);

    return e2;
}

你可以这样使用:

function someErrorThrowingFunction() {
    throw new Error("Some Message");
}

function testOtherFnc() {
    try {
        someErrorThrowingFunction();
    } catch (e) {
        throw chainErrors(e, new Error("Some new Message"));
    }
}

产生:

/path/to/file/script.js:11
        throw new Error("Some new Message", e);
        ^

Error: Some new Message
    at testOtherFnc (/path/to/file/script.js:11:15)
    at Object.<anonymous> (/path/to/file/script.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)
Caused by: Error: Some Message
    at someErrorThrowingFunction (/path/to/file/script.js:4:11)
    at testOtherFnc (/path/to/file/script.js:9:9)
    at Object.<anonymous> (/path/to/file/script.js:15:1)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)

这与 Java 生成的堆栈跟踪非常相似。这存在三个问题。

第一个问题是调用站点的重复,这是可以解决但复杂的。

第二个是生成的输出依赖于引擎,这种尝试对于 V8 非常有效,但不适用于 Firefox,例如,因为 Firefox 不仅使用另一种样式,而且它们还解析和设置错误消息的样式,这会阻止我们像链接它一样这个。

第三个问题是可用性。这有点笨拙,你必须记住这个功能,如果你在正确的引擎中,你需要跟踪。另一种方法是这样的:

const Error = (() => {
    const glob = (() => { try { return window; } catch (e) { return global; } })();

    const isErrorExtensible = (() => {
        try {
            // making sure this is an js engine which creates "extensible" error stacks (i.e. not firefox)
            const stack = (new glob.Error('Test String')).stack;
            return stack.slice(0, 26) == 'Error: Test String\n    at ';
        } catch (e) { return false; }
    })();

    const OriginalError = glob.Error;

    if (isErrorExtensible) {
        let ff = v => JSON.stringify(v, undefined, 4);
        const formatForOutput = v => {
            try {
                return ff(v).replace(/\n/g, '\n    ');
            } catch (e) {
                return "" + v;
            }
        };

        const chainErrors = (e1, e2) => {
            if (e1 instanceof OriginalError)
                e2.stack += '\nCaused by: ' + e1.stack;
            else
                e2.stack += '\nWas caused by throwing:\n    ' + formatForOutput(e1);

            return e2;
        }

        class Error extends OriginalError {
            constructor(msg, chained) {
                super(msg);

                if (arguments.length > 1)
                    chainErrors(chained, this);
            }
        }

        return Error;
    } else
        return OriginalError; // returning the original if we can't chain it
})();

然后你可以像在 Java 中那样做:

function someErrorThrowingFunction() {
    throw new Error("Some Message");
}

function testOtherFnc() {
    try {
        someErrorThrowingFunction();
    } catch (e) {
        throw new Error("Some new Message", e);
    }
}

testOtherFnc();

尽管第二个版本带来了一些(其他)问题,但它可能是“更容易”的一个,因为即使引擎不支持链接,您也不需要更改代码,因为您可以提供一个函数(错误构造函数)尽可能多的参数。

无论哪种方式,希望这将是 ES2020 的东西。

于 2019-01-19T20:02:23.467 回答
1

您可以将错误对象链接Error起来,并进行stack和的连接message

var console = {
    log: function(s) {
      document.getElementById("console").innerHTML += s + "<br/>"
    }
  }

var error1=new Error("This is error 1");
console.log("Message: ".concat( error1.message ));
console.log("Stack<br>".concat(error1.stack) );

var error2=new Error("This is error 2");
console.log("Message: ".concat( error2.message) );
console.log("Stack<br>".concat( error2.stack) );

var error3=new Error("This is error 3");
error3.stack=error3.stack.concat(error2.stack).concat(error1.stack)
console.log("Message: ".concat(error3.message));
console.log("Stack<br>".concat(error3.stack));
<div id="console" />

于 2016-04-08T15:25:22.570 回答