Подтвердить что ты не робот

Как связать исключения в javascript (т.е. Добавить причину, например, в java)

Существует ли стандартный/лучший способ добавить причину исключения в javascript. В java вы можете сделать это:

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

Когда полученное исключение будет напечатано, оно даст вам приятный след, который включает в себя причины. Есть ли какой-либо хороший способ сделать это в javascript или мне нужно катить самостоятельно?

4b9b3361

Ответ 1

В prod мы используем TraceError

Использование

import TraceError from 'trace-error';

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

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

Выход

Ответ 2

Пока (пока нет лучшего ответа) вот что я сделал:

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

Строка могла бы быть лучше, если бы она сказала "причины", потому что трассировка стека исключения, вызывающего ошибку, печатается первой.

Ответ 3

Если вы используете Node.js, вы можете использовать VError

У Joyent также есть страница, на которой обсуждаются лучшие практики по обработке ошибок в Node.js https://www.joyent.com/developers/node/design/errors

Единственное, мне не нравится, как Joyent-реализация VError падает, если вы передаете нулевые или неопределенные параметры. Это особенно важно, когда вы обрабатываете ошибки, так как это просто замаскирует основную причину проблемы. Я разбудил их VError, чтобы он не потерпел неудачу с нулевыми или неопределенными параметрами. https://github.com/naddison36/node-verror

Ответ 4

tl; dr Это не решение, просто помощник, пока ECMA Script не примет какой-то стандарт.

РЕДАКТИРОВАТЬ: я обернул этот ответ в пакет 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.

Ответ 5

Вы можете связать объекты ошибок 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" />