Слияние следов стека в восстановленных ошибках
Я перехватываю здесь ошибку из обещания Sequelize (Bluebird). Во-первых, это было сделано для изменения сообщения об ошибке, но, как оказалось, это также создает более информативную трассировку стека.
Это что-то вроде
sequelize.sync().catch(originalError => {
const rethrownError = new Error(originalError.msg + ': ' + originalError.sql);
throw rethrownError;
});
Где originalError.stack
не содержит строку, вызвавшую ошибку, но в ней содержится важная информация, полученная из драйвера Sequelize и MySQL:
SequelizeDatabaseError: ER_KEY_COLUMN_DOES_NOT_EXITS: Key column 'NonExisting' doesn't exist in table
at Query.formatError (...\node_modules\sequelize\lib\dialects\mysql\query.js:175:14)
at Query._callback (...\node_modules\sequelize\lib\dialects\mysql\query.js:49:21)
at Query.Sequence.end (...\node_modules\mysql\lib\protocol\sequences\Sequence.js:85:24)
at Query.ErrorPacket (...\node_modules\mysql\lib\protocol\sequences\Query.js:94:8)
at Protocol._parsePacket (...\node_modules\mysql\lib\protocol\Protocol.js:280:23)
at Parser.write (...\node_modules\mysql\lib\protocol\Parser.js:74:12)
at Protocol.write (...\node_modules\mysql\lib\protocol\Protocol.js:39:16)
at Socket.<anonymous> (...\node_modules\mysql\lib\Connection.js:109:28)
at emitOne (events.js:96:13)
at Socket.emit (events.js:188:7)
at readableAddChunk (_stream_readable.js:176:18)
at Socket.Readable.push (_stream_readable.js:134:10)
at TCP.onread (net.js:548:20)
rethrownError.stack
содержит интересующую точку (первая строка в стеке), но все остальное - мусор:
Error: ER_KEY_COLUMN_DOES_NOT_EXITS: Key column 'NonExisting' doesn't exist in table
at sequelize.sync.catch (...\app.js:59:17)
at tryCatcher (...\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (...\node_modules\bluebird\js\release\promise.js:504:31)
at Promise._settlePromise (...\node_modules\bluebird\js\release\promise.js:561:18)
at Promise._settlePromise0 (...\node_modules\bluebird\js\release\promise.js:606:10)
at Promise._settlePromises (...\node_modules\bluebird\js\release\promise.js:681:18)
at Async._drainQueue (...\node_modules\bluebird\js\release\async.js:138:16)
at Async._drainQueues (...\node_modules\bluebird\js\release\async.js:148:10)
at Immediate.Async.drainQueues (...\node_modules\bluebird\js\release\async.js:17:14)
at runCallback (timers.js:637:20)
at tryOnImmediate (timers.js:610:5)
at processImmediate [as _immediateCallback] (timers.js:582:5)
Я хотел бы сохранить информацию обо всех из них - и обозначить связь между ними, а не просто добавить две несвязанные записи журнала.
Я думал о регистрации их как одной ошибки с конкатенированным стеком, rethrownError.stack += '\n' + originalError.stack
.
Как следует обрабатывать эти две ошибки? Должны ли быть соединены следы стека? Существует ли соглашение о слиянии стеков ошибок в JavaScript (Node.js в частности)?
Цель состоит в том, чтобы сохранить итоговую ошибку значимой и не нарушать существующие инструменты, которые анализируют трассировки стека ошибок (а именно Stacktrace.js).
В рассматриваемых проектах используется логгер Winston или plain console.error
, поэтому в какой-то момент ошибка стробируется (в приведенном выше примере она была зарегистрирована с помощью обработчика необработанного отклонения).
Ответы
Ответ 1
Вот легкая альтернатива VError: rerror
(Im автор)
Идея такая же: обертывание ошибок в ошибках. Однако это намного проще. Он имеет меньше возможностей, но также работает в браузере. Он также учитывает, что создание трассировки стека является дорогостоящим. Вместо того чтобы создавать трассировки стека и добавлять их в строку, он создает стек ошибок внутри и создает только большую трассировку стека, если вам это нужно (используйте getter).
Пример
function fail() {
throw new RError({
name: 'BAR',
message: 'I messed up.'
})
}
function failFurther() {
try {
fail()
} catch (err) {
throw new RError({
name: 'FOO',
message: 'Something went wrong.',
cause: err
})
}
}
try {
failFurther()
} catch (err) {
console.error(err.why)
console.error(err.stacks)
}
Выход
FOO: Something went wrong. <- BAR: I messed up.
Error
at failFurther (/Users/boris/Workspace/playground/es5/index.js:98:11)
at Object.<anonymous> (/Users/boris/Workspace/playground/es5/index.js:107:3)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
at run (bootstrap_node.js:394:7)
<- Error
at fail (/Users/boris/Workspace/playground/es5/index.js:88:9)
at failFurther (/Users/boris/Workspace/playground/es5/index.js:96:5)
at Object.<anonymous> (/Users/boris/Workspace/playground/es5/index.js:107:3)
at Module._compile (module.js:556:32)
at Object.Module._extensions..js (module.js:565:10)
at Module.load (module.js:473:32)
at tryModuleLoad (module.js:432:12)
at Function.Module._load (module.js:424:3)
at Module.runMain (module.js:590:10)
Рекомендуемое чтение: https://www.joyent.com/node-js/production/design/errors
Ответ 2
Насколько я знаю, нет встроенного способа обработки вложенных ошибок в Node.js. Единственное, что я могу вам порекомендовать, это использовать библиотеку VError. Это действительно полезно при работе с расширенной обработкой ошибок.
Вы можете использовать fullStack
, чтобы комбинировать стеки многих ошибок:
var err1 = new VError('something bad happened');
var err2 = new VError(err1, 'something really bad happened here');
console.log(VError.fullStack(err2));
Ответ 3
Опираясь на fooobar.com/info/630521/..., я абстрагировал вызов VError.fullStack
следующим образом
class FullStackVError extends VError {
constructor(cause, ...args) {
super(cause, ...args);
let childFullStack;
if (cause instanceof VError) {
childFullStack = cause.stack;
cause.stack = cause._originalStack;
}
this._originalStack = this.stack;
this.stack = VError.fullStack(this);
if (cause instanceof VError) {
cause.stack = childFullStack;
}
}
}
Теперь console.log(err2.stack);
эквивалентно тому, что console.log(VError.fullStack(err2));
был бы.