如何在 nodejs/javascript 中重新抛出错误或异常并包含自定义消息。
我有以下代码
var json = JSON.parse(result);
如果发生任何解析错误,我想在异常消息中包含 result
内容。像这样。
1. try {
2. var json = JSON.parse(result);
3. expect(json.messages.length).to.be(1);
4. } catch(ex) {
5. throw new Error(ex.message + ". " + "JSON response: " + result);
6. }
这里的问题是我丢失了堆栈跟踪。
有没有类似于java
的方法?
throw new Error("JSON response: " + result, ex);
最佳答案
我不知道像 Java 这样的本地方法,而且我还没有找到一个优雅的解决方案来包装错误。
创建 new Error
的问题是您可能会丢失附加到引发的原始 Error
的元数据,堆栈跟踪和类型通常是重要的项目丢了。
对现有抛出的错误进行修改会更快,但仍然可以从不存在的错误中修改数据。在其他地方创建的错误中四处寻找也感觉不对。
创建一个新的错误和新的堆栈
新的Error
的.stack
属性是一个纯字符串,可以在抛出它之前进行修改以说出你喜欢的内容。但是,完全替换错误 stack
属性可能会让调试非常困惑。
当原始抛出的错误和错误处理程序位于不同的位置或文件(这在 Promise 中很常见)时,您可能能够追踪原始错误的来源,但无法追踪实际捕获错误的处理程序。为避免这种情况,最好在 stack
中保留对原始错误和新错误的一些引用。如果其中存储了其他元数据,访问完整的原始错误也很有用。
这是一个捕获错误的示例,将其包装在一个新错误中,但添加原始 stack
并存储 error
:
try {
throw new Error('First one')
} catch (error) {
let e = new Error(`Rethrowing the "${error.message}" error`)
e.original_error = error
e.stack = e.stack.split('\n').slice(0,2).join('\n') + '\n' +
error.stack
throw e
}
哪个抛出:
/so/42754270/test.js:9
throw e
^
Error: Rethrowing the "First one" error
at test (/so/42754270/test.js:5:13)
Error: First one
at test (/so/42754270/test.js:3:11)
at Object.<anonymous> (/so/42754270/test.js:13:1)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
所以我们创建了一个新的通用Error
。不幸的是,原始错误的类型从输出中隐藏了,但 error
已附加为 .original_error
因此仍然可以访问它。新的 stack
已被大量删除,除了生成线很重要,并附加了原始错误 stack
。
任何尝试解析堆栈跟踪的工具都可能不适用于此更改或最佳情况,它们会检测到两个错误。
使用 ES2015+ 错误类重新抛出
把它变成一个可重用的 ES2015+ 错误类:
class RethrownError extends Error {
constructor(message, error){
super(message)
this.name = this.constructor.name
if (!error) throw new Error('RethrownError requires a message and error')
this.original_error = error
this.stack_before_rethrow = this.stack
const message_lines = (this.message.match(/\n/g)||[]).length + 1
this.stack = this.stack.split('\n').slice(0, message_lines+1).join('\n') + '\n' +
error.stack
}
}
throw new RethrownError(`Oh no a "${error.message}" error`, error)
结果
/so/42754270/test2.js:31
throw new RethrownError(`Oh no a "${error.message}"" error`, error)
^
RethrownError: Oh no a "First one" error
at test (/so/42754270/test2.js:31:11)
Error: First one
at test (/so/42754270/test2.js:29:11)
at Object.<anonymous> (/so/42754270/test2.js:35:1)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
那么您就知道,每当您看到 RethrownError
时,原始错误仍将在 .original_error
处出现。
这种方法并不完美,但这意味着我可以将来自底层模块的已知错误重新键入更容易处理的泛型类型,通常使用 bluebird filtered catch .catch(TypeError, handler)
注意 stack
在这里变成可枚举的
修改堆栈时出现相同错误
有时您需要将原始错误保持原样。
在这种情况下,您只需将新信息附加/插入到现有堆栈中即可。
file = '/home/jim/plumbers'
try {
JSON.parse('k')
} catch (e) {
let message = `JSON parse error in ${file}`
let stack = new Error(message).stack
e.stack = e.stack + '\nFrom previous ' + stack.split('\n').slice(0,2).join('\n') + '\n'
throw e
}
返回
/so/42754270/throw_error_replace_stack.js:13
throw e
^
SyntaxError: Unexpected token k in JSON at position 0
at Object.parse (native)
at Object.<anonymous> (/so/42754270/throw_error_replace_stack.js:8:13)
at Module._compile (module.js:570:32)
at Object.Module._extensions..js (module.js:579:10)
at Module.load (module.js:487:32)
at tryModuleLoad (module.js:446:12)
at Function.Module._load (module.js:438:3)
at Module.runMain (module.js:604:10)
at run (bootstrap_node.js:394:7)
at startup (bootstrap_node.js:149:9)
From previous Error: JSON parse error in "/home/jim/plumbers"
at Object.<anonymous> (/so/42754270/throw_error_replace_stack.js:11:20)
还请注意,堆栈处理很简单,并且假定错误消息是单行。如果您遇到多行错误消息,您可能需要在 处查找 \n 以终止消息。
关于javascript - 在 NodeJS 中重新抛出异常并且不丢失堆栈跟踪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42754270/