go - 如何在 Go 中的 `panic` 之后抑制堆栈跟踪?

标签 go

我正在开发一个 CLI 工具,如果出现问题,我想记录自定义错误并 panic 退出。 panic 的问题是panic 的退出后面是我不想向用户显示的堆栈跟踪。有没有办法 panic 并有一个忍者般的隐秘/安静的导出?
(选择 panic 而不是 os.Exit() 因为它可以处理任何 defer 并且看起来更干净。)

最佳答案

嗯,直接回答是肯定的,有办法:

  • panic带有自定义错误“哨兵”类型或自定义“哨兵”值,然后
  • 有一个defer -红色调用 main其中recover() -s panic 并检查返回的值是否为哨兵类型(或等于哨兵值 - 确切的方法取决于您)。
    如果它检测到“已知”错误,它会以非零退出代码静默退出;
    否则 panic再次。

  • 但老实说,我认为您的思维方式受到编程语言的影响太大了,但有异常(exception):我还没有看到 CLI 应用程序在“以通常方式”处理错误时会遇到任何困难,或者实际上会从使用 panic 的救助中受益。 .
    与您渴望的方法相反的论点是:冒泡错误允许在展开的调用堆栈的每个级别上为其添加更多上下文,这是有意义的——产生尽可能有用的错误以显示。
    基本上,它是这样工作的:
    func main() {
    
      ...
    
      err := DoStuff()
      if err != nil {
        log.Fatal("failed to do stuff: ", err)
      }
    
      ...
    }
    
    func DoStuff() error {
      foo, err := InitializeWhatever()
      if err != nil {
        return fmt.Errorf("failed to inialize whatever: %w", err)
      }
    
      ...
    
      return nil
    }
    
    func InitializeWhatever() (*Whatever, error) {
      handle, err := OpenWhateverElse()
      if err != nil {
        return nil, fmt.Errorf("failed to open whatever else: %w", err)
      }
    
      ...
    
      return whatever, nil
    }
    
    …这会产生类似的东西failed to do stuff: failed to inialize whatever: failed to open whatever else: task failed successfully......这清楚地表明哪些事件序列导致了不希望的结果。
    当然,像往常一样,YMMV,除了你之外没有人最了解你的情况,但这仍然是值得思考的事情。

    这是我上面所写内容的各种想法列表。
  • 使用已知类型的已知值/错误进行 panic 的方法实际上并不是什么新鲜事——例如,参见 net/http.ErrAbortHandler .encoding/json包用于使用这种方法来打破多个嵌套循环(尽管从那时起已经重新设计)。
  • 尽管如此,一些专家consider sentinel errors to be bad design并建议改为断言(使用 Go 的类型断言)错误的行为——例如,您可以查看 net.Error TimeoutTemporary允许没有针对临时错误和由于超时导致的错误及其组合的具体导出类型的方法,而是让它们都支持一组通用的方法,这些方法允许调用者理解错误的性质。
  • 从 1.13 开始,前往 gained对“包装”错误的高级支持——产生套娃式错误链,这允许一种位于上述两个极端之间的错误处理方法:可以在第一次发现错误的地方创建特定类型的错误,然后在调用堆栈冒泡时将其包装在上下文中,然后在处理错误的位置断言最内部错误的类型。 A friendly explanation on how it works .

  • 好吧,我建议阅读 this因为,我必须承认,如果你已经这样做了,你可能一开始就不会问你的问题;-)

    关于go - 如何在 Go 中的 `panic` 之后抑制堆栈跟踪?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62983700/

    相关文章:

    go - 如何关闭运行基于流的逻辑的 goroutine?

    go - 如何在GoLang中获取MySQL表的描述

    go - 使用来自其父级的方法更改嵌入式结构的属性

    http - 为什么 Golang http.ResponseWriter 执行被延迟?

    node.js - 在 Go 中,编写非阻塞代码有意义吗?

    使用 Golang 解析 yaml 文件

    go - int 前的插入符号是什么意思?

    go - 通过 import 生成基于 protobuf 的 go 文件

    arrays - 如何生成数字序列

    reflection - 是否可以在 go 中动态创建带有接收器(方法)的函数?