从多个抽象级别处理错误的最佳实践

我想知道在 go 中处理多级抽象错误的最佳方法是什么。每次如果我必须为程序添加一个新的抽象级别,我就不得不将错误代码从低级别传输到高级别。因此日志文件中有重复的通信,否则我必须记住删除通信表级别低并将他转移到更高级别。下面简单举例。我跳过创建每个对象来更短和更清晰的代码,但我认为你理解我的问题


type ObjectOne struct{

    someValue int

}


func (o* ObjectOne)CheckValue()error{

    if o.someValue == 0 {

        SomeLogger.Printf("Value is 0 error program") // communicate form first level abstraction to logger

        return errors.New("Internal value in object is 0")

    }

    return nil

}


type ObjectTwoHigherLevel struct{

    objectOne ObjectOne

}


func (oT*  ObjectTwoHigherLevel)CheckObjectOneIsReady() error{

    if err := oT.objectOne.CheckValue() ; err != nil{

        SomeLogger.Printf("Value in objectOne is not correct for objectTwo %s" , err) //  second communicate

        return  err

    }

    return nil

}


type ObjectThreeHiggerLevel struct{

    oT ObjectTwoHigherLevel

}


func (oTh* ObjectThreeHiggerLevel)CheckObjectTwoIsReady()error{

    if err := oTh.oT.CheckObjectOneIsReady() ; err != nil{

        SomeLogger.Printf("Value in objectTwo is not correct for objectThree %s" , err)

    return err

    }

    return nil

}

结果在日志文件中我得到重复的帖子


Value is 0 error program 

Value in objectOne is not correct for objectTwo Internal value in object is 0 

Value in objectTwo is not correct for objectThree Internal value in object is 0

反过来,如果我只将一些转移err到更高级别而没有额外的日志,我会丢失每个级别发生的信息。


这个怎么解决?隐私副本如何通信?还是我的路是好的也是唯一的?


如果我创建一些对象,这些对象在几个抽象级别上搜索数据库中的某些内容,那么问题会更加令人沮丧,然后我在 logFile 中也得到几行形成相同的任务。


繁花不似锦
浏览 180回答 2
2回答

芜湖不芜

编辑:这个答案早于 Go 1.13,它提供了与所介绍的技术类似的东西。请查看Go 博客:处理 Go 1.13 中的错误。您应该处理错误,或者不处理错误,而是将其委托给更高级别(给调用者)。处理错误并返回它是不好的做法,就好像调用者也这样做一样,错误可能会被处理多次。处理错误意味着检查它并根据它做出决定,这可能是您简单地记录它,但这也算作“处理”它。如果您选择不处理而是将其委托给更高级别,那可能很好,但不要只返回您得到的错误值,因为它可能对没有上下文的调用者毫无意义。注释错误一个非常好的和推荐的委派方式是Annotating errors。这意味着您创建并返回一个新的错误值,但旧的错误值也包含在返回值中。包装器为包装的错误提供上下文。没有为标注错误公共图书馆:github.com/pkg/errors; 及其godoc:errors它基本上有 2 个功能: 1 用于包装现有错误:func Wrap(cause error, message string) error还有一个用于提取包装错误:func Cause(err error) error使用这些,您的错误处理可能如下所示:func (o *ObjectOne) CheckValue() error {    if o.someValue == 0 {        return errors.New("Object1 illegal state: value is 0")    }    return nil}第二级:func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {    if err := oT.objectOne.CheckValue(); err != nil {        return errors.Wrap(err, "Object2 illegal state: Object1 is invalid")    }    return nil}第三级:仅调用第二级检查:func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {    if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {        return errors.Wrap(err, "Object3 illegal state: Object2 is invalid")    }    return nil}请注意,由于这些CheckXX()方法不处理错误,因此它们不会记录任何内容。他们正在委派带注释的错误。如果有人使用ObjectThreeHiggerLevel决定处理错误:o3 := &ObjectThreeHiggerLevel{}if err := o3.CheckObjectTwoIsReady(); err != nil {    fmt.Println(err)}将呈现以下漂亮的输出:Object3 illegal state: Object2 is invalid: Object2 illegal state: Object1 is invalid: Object1 illegal state: value is 0没有多个日志的污染,并且所有细节和上下文都被保留了,因为我们使用errors.Wrap()它产生一个错误值,该错误值格式化为 a string,它递归地保留包装的错误:错误堆栈。您可以在博客文章中阅读有关此技术的更多信息:Dave Cheney:不要只检查错误,要优雅地处理它们“扩展”错误如果您喜欢更简单的事情和/或您不想与外部库发生麻烦并且您无法提取原始错误(确切的错误值,而不是您可以的错误字符串),那么您可以只需使用上下文扩展错误并返回这个新的扩展错误。扩展错误最容易通过使用fmt.Errorf()它来完成,它允许您创建一个“漂亮”的格式化错误消息,它会返回一个类型的值,error因此您可以直接返回它。使用fmt.Errorf(),您的错误处理可能如下所示:func (o *ObjectOne) CheckValue() error {    if o.someValue == 0 {        return fmt.Errorf("Object1 illegal state: value is %d", o.someValue)    }    return nil}第二级:func (oT *ObjectTwoHigherLevel) CheckObjectOneIsReady() error {    if err := oT.objectOne.CheckValue(); err != nil {        return fmt.Errorf("Object2 illegal state: %v", err)    }    return nil}第三级:仅调用第二级检查:func (oTh *ObjectThreeHiggerLevel) CheckObjectTwoIsReady() error {    if err := oTh.ObjectTwoHigherLevel.CheckObjectOneIsReady(); err != nil {        return fmt.Errorf("Object3 illegal state: %v", err)    }    return nil}ObjectThreeHiggerLevel如果它决定“处理”它,将显示以下错误消息:o3 := &ObjectThreeHiggerLevel{}if err := o3.CheckObjectTwoIsReady(); err != nil {    fmt.Println(err)}将呈现以下漂亮的输出:Object3 illegal state: Object2 illegal state: Object1 illegal state: value is 0请务必阅读博文:错误处理和 Go

侃侃尔雅

有各种库将堆栈跟踪嵌入到 Go 错误中。只需使用其中之一创建错误,它就会冒出完整的堆栈上下文,您可以稍后检查或记录。一个这样的图书馆:https://github.com/go-errors/errors还有一些我忘记了。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go