通用恐慌在GO计划中恢复



我正在尝试从程序中创建的GO例程中捕获崩溃/恐慌,以将其发送到我的crash-error报告服务器(例如Sentry/Raygun(

例如,

func main() {
    go func() {
        // Get this panic
        panic("Go routine panic")
    }()
}

答案指出,goroutine无法从另一Goroutine中的恐慌中恢复。

习惯方法是什么?

您必须"注入"作为新的Goroutine启动的功能中的一些代码:您必须调用延期函数,在该功能中调用recover()。这是从惊恐状态中恢复的唯一方法。请参阅相关:为什么defer recover((`不陷入恐慌?

例如:

go func() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Caught:", r)
        }
    }()
    panic("catch me")
}()

这将输出(在Go Playground上尝试(:

Caught: catch me

在您启动的每个goroutine中执行此操作是不可行的,但是您当然可以将恢复的操作功能移动到命名函数,然后调用该功能(但当然会推迟(:

func main() {
    go func() {
        defer logger()
        panic("catch me")
    }()
    time.Sleep(time.Second)
}
func logger() {
    if r := recover(); r != nil {
        fmt.Println("Caught:", r)
    }
}

这将输出相同的(在Go Playground上尝试(。

另一个更方便,更紧凑的解决方案是创建一个实用程序功能,"包装器"接收功能并照顾恢复的功能。

这就是外观:

func wrap(f func()) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Caught:", r)
        }
    }()
    f()
}

现在使用它更简单:

go wrap(func() {
    panic("catch me")
})
go wrap(func() {
    panic("catch me too")
})

它将输出(在Go Playground上尝试(:

Caught: catch me
Caught: catch me too

最终注意:

请注意,启动实际的goroutine发生在wrap()之外。这使呼叫者可以选择仅将wrap()调用的go调用才能决定是否需要新的Goroutine。通常,这种方法是GO的首选。这使您可以通过将其传递给wrap()来执行任意功能,并且它将"保护"。即使您不希望在新的Goroutine中同时运行它,它的执行(通过恐慌恢复,正确记录/报告(。另一方面,如果您将go移入wrap()中,它甚至不再起作用,因为recover()呼叫不会在惊慌的Goroutine上发生。

相关内容

  • 没有找到相关文章

最新更新