我可以从恐慌中恢复,处理错误,然后再次恐慌并保留原始的堆栈跟踪吗?

huangapple go评论82阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2016年1月18日 02:58:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/34842322.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定