英文:
Can I recover from panic, handle the error, then panic again and keep the original stack trace?
问题
在recover
中是否有可能“重新抛出”错误并保留原始的堆栈跟踪?我所知道的最好方法是再次使用panic
,但这会创建一个新的堆栈跟踪。
func do() {
defer func() {
cleanUp()
if x := recover(); x != nil {
handleError()
panic(x)
}
}()
doStuff()
}
我希望实现这个的原因是,除非我的函数正常退出或handleError
运行,否则我的程序会死锁。而且,如果我不保留原始的堆栈跟踪,我就不知道它在哪里崩溃。
英文:
Is it possible to "rethrow" an error from recover
and keep the original stack trace? The best I know how to do is to panic again, but that does create a new stacktrace.
func do() {
defer func() {
cleanUp()
if x := recover(); x != nil {
handleError()
panic(x)
}
}()
doStuff()
}
My motivation for wanting this is that unless my function exits normally or handleError
runs, my program deadlocks. And unless I preserve the original strack trace, I do not know where it crashed.
答案1
得分: 14
解决方案是不调用recover
,因为这样既无法重新抛出异常,也无法访问堆栈跟踪。可以使用一个布尔标志代替recover
来检查是否发生了 panic。
func do() {
panicked := true
defer func() {
cleanUp()
if panicked {
handleError()
}
}()
doStuff()
panicked = false
}
链接:https://play.golang.org/p/PKeP9s-3tF
英文:
The solution is to not call recover
, because then neither rethrowing nor accessing the stack trace is possible. Use a bool flag instead of recover
to check for panic.
https://play.golang.org/p/PKeP9s-3tF
func do() {
panicked := true
defer func() {
cleanUp()
if panicked {
handleError()
}
}()
doStuff()
panicked = false
}
答案2
得分: 6
在堆栈中较高位置的延迟函数将在发生 panic 时运行,即使它们没有调用 recover()
。
只需删除 if 语句和重新 panic。然后处理错误,并让 panic 继续向上传递。
func do() {
defer handleError()
doStuff()
}
一个简单的示例:
func a() {
defer func() {
fmt.Println("a")
}()
panic("test")
}
func b() {
defer func() {
fmt.Println("b")
}()
}
func main() {
fmt.Println("Hello, playground")
b()
}
输出结果:
Hello, playground
b
英文:
Defered functions higher up in the stack will run on panic, even if they don't call recover()
.
Simply remove the if-statement and the re-panic. Then handle your error, and let the panic continue up the stack.
func do() {
defer handleError()
doStuff()
}
a simple demo:
https://play.golang.org/p/UiRou5MhUR
func a() {
defer func() {
fmt.Println("a")
}()
panic("test")
}
func b() {
defer func() {
fmt.Println("b")
}()
}
func main() {
fmt.Println("Hello, playground")
b()
}
outputs
Hello, playground
b
答案3
得分: 1
根据Daniel Conde Marin在这里的问题所提问的问题,我认为你不需要担心调用panic并传递recover()的响应对象会创建一个加密或"新的"堆栈跟踪,至少在Go 1.17中是这样的。看起来这样做只是在跟踪中添加了另一个"src/runtime/panic.go:
请参考这个示例测试:
https://goplay.space/#5i1RCJyiFJr
Go语言对待panic的方式与其他语言对待try/catch/throw/finally的方式不同。
英文:
As Daniel Conde Marin questioned here
I do not believe you need to worry about calling panic and passing in a recover()'s response object creating a cryptic or "new" stack trace, at least in go 1.17. It looks like doing so simply adds another "src/runtime/panic.go:<linenum>" frame to the trace.
See this example test:
https://goplay.space/#5i1RCJyiFJr
Go does not treat panics like other languages treat try/catch/throw/finally.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论