当前位置: 首页 > 知识库问答 >
问题:

go程序中的泛型恐慌恢复

邓深
2023-03-14

我试图从我的程序中创建的go例程中捕捉崩溃/恐慌,以便将它们发送到我的崩溃错误报告服务器(如Sentry/Raygun)

例如,

func main() {

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

做这件事的惯用方法是什么?

共有1个答案

蒋向笛
2023-03-14

您必须将一些代码“注入”到作为新goroutine启动的函数中:您必须调用一个延迟函数,在该函数中调用recover()。这是从恐慌状态中恢复的唯一方法。参见相关内容:为什么'defer recovery()'不能抓住恐慌?

例如:

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

    panic("catch me")
}()

这将输出(在围棋游乐场上试试):

Caught: catch me
func main() {
    go func() {
        defer logger()
        panic("catch me")
    }()

    time.Sleep(time.Second)
}

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

还有一个更方便甚至更紧凑的解决方案是创建一个实用函数,一个接收函数并负责恢复的“包装器”。

这是它的样子:

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")
})
Caught: catch me
Caught: catch me too

请注意,启动实际的goroutine发生在wrap()之外。这使调用方可以选择是否需要新的goroutine,只需在wrap()调用前缀go。通常这种方法在围棋中是首选的。这允许您通过将任意函数传递给wrap()来执行这些函数,并且它将“保护”其执行(通过从panics中恢复、正确地记录/报告它),即使您不希望在新的goroutine中并发运行它。

 类似资料:
  • 问题内容: 在defer函数中,我想查看一次恢复调用是否会产生非nil值(不恢复) 可能吗? 问题答案: 那确切的事情是不可能的。您可能只想重新恐慌,就像在其他语言中重新引发异常一样。

  • 问题内容: 我曾经认为,如果goroutine中的恐慌的调用者在恐慌之前完成,它将使其终止程序(延迟恢复没有任何帮助,因为此时还没有发生恐慌), 直到我尝试以下代码: 我发现无论调用者函数完成与否,如果goroutines开始恐慌,调用者的延迟恢复机制将无济于事。整个程序仍然无效。 所以为什么?理论上,调用者功能仍在运行。当出现紧急情况时,调用者的延迟功能应起作用(包括恢复)。 问题答案: 该规范

  • 我正在学习围棋,我试图理解如何正确处理来自外部包的恐慌。 调用doFoo方法会使服务器崩溃,我认为这是正确的行为,因为应用程序现在处于受损状态。最好是崩溃,然后通过某个负载均衡器将后续请求转发到不同的进程。但是,我的api服务器可能还在为其他客户机服务,它可能在维护websockets,而且我可能还想在这里返回一个500错误。 来自nodejs,我习惯了处理未捕获的同步异常和处理未捕获的异步异常的

  • 作为一个新的围棋爱好者,试图使用围棋的错误处理方式。明确地说--我喜欢例外。 我有一个服务器,它接受一个连接,处理一组请求并答复它们。我发现我可以做 在深层处理代码中 null 我觉得答案是“是的,它可以工作”,可以在您自己的代码中使用,但panic不应该被用于更广泛用途的库使用。库的标准和礼貌的行为方式是通过错误返回