Ответ 1
В prod мы используем TraceError
Использование
import TraceError from 'trace-error';
global.TraceError = TraceError; // expose globally (optional)
throw new TraceError('Could not set status', srcError, ...otherErrors);
Выход
Существует ли стандартный/лучший способ добавить причину исключения в javascript. В java вы можете сделать это:
Throwable t = new Exception("whatever");
t.addCause(previouslyCaughtException);
throw t;
Когда полученное исключение будет напечатано, оно даст вам приятный след, который включает в себя причины. Есть ли какой-либо хороший способ сделать это в javascript или мне нужно катить самостоятельно?
В prod мы используем TraceError
import TraceError from 'trace-error';
global.TraceError = TraceError; // expose globally (optional)
throw new TraceError('Could not set status', srcError, ...otherErrors);
Пока (пока нет лучшего ответа) вот что я сделал:
...
} 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>
Строка могла бы быть лучше, если бы она сказала "причины", потому что трассировка стека исключения, вызывающего ошибку, печатается первой.
Если вы используете Node.js, вы можете использовать VError
У Joyent также есть страница, на которой обсуждаются лучшие практики по обработке ошибок в Node.js https://www.joyent.com/developers/node/design/errors
Единственное, мне не нравится, как Joyent-реализация VError падает, если вы передаете нулевые или неопределенные параметры. Это особенно важно, когда вы обрабатываете ошибки, так как это просто замаскирует основную причину проблемы. Я разбудил их VError, чтобы он не потерпел неудачу с нулевыми или неопределенными параметрами. https://github.com/naddison36/node-verror
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.
Вы можете связать объекты ошибок 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" />